send_message.py 4.14 KB
#!/usr/bin/env python3
"""
send_message.py - 发送飞书消息(Python 版本,自动处理 JSON 转义)

用法:
    python3 send_message.py <chat_id> <message> [msg_type]

示例:
    python3 send_message.py oc_xxxxxx "Hello World"
    python3 send_message.py oc_xxxxxx "{\"text\":\"Hello\"}" text
    python3 send_message.py oc_xxxxxx "{\"title\":\"Title\",\"content\":\"Body\"}" post
"""

import json
import sys
import os
import urllib.request


def get_token():
    """获取 tenant_access_token"""
    app_id = os.environ.get('FEISHU_APP_ID', 'cli_a91b01debab81cd1')
    app_secret = os.environ.get('FEISHU_APP_SECRET')
    
    if not app_secret:
        # 尝试从 .env 文件读取
        env_file = os.path.expanduser('~/.hermes/.env')
        if os.path.exists(env_file):
            with open(env_file, 'r') as f:
                for line in f:
                    if line.startswith('FEISHU_APP_SECRET='):
                        app_secret = line.split('=', 1)[1].strip()
                        break
    
    if not app_secret:
        print("Error: FEISHU_APP_SECRET not found")
        sys.exit(1)
    
    token_data = json.dumps({
        "app_id": app_id,
        "app_secret": app_secret
    }).encode('utf-8')
    
    token_req = urllib.request.Request(
        'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
        data=token_data,
        headers={'Content-Type': 'application/json'},
        method='POST'
    )
    
    with urllib.request.urlopen(token_req, timeout=30) as response:
        token_result = json.loads(response.read().decode('utf-8'))
        if token_result.get('code') != 0:
            print(f"Error getting token: {token_result}")
            sys.exit(1)
        return token_result['tenant_access_token']


def send_message(chat_id, message, msg_type='text'):
    """发送消息到飞书"""
    token = get_token()
    
    # 处理 content 格式
    if msg_type == 'text':
        # 如果 message 已经是 JSON 字符串,直接使用
        try:
            json.loads(message)
            content = message
        except json.JSONDecodeError:
            # 否则包装为 JSON 字符串
            content = json.dumps({"text": message})
    elif msg_type == 'post':
        try:
            json.loads(message)
            content = message
        except json.JSONDecodeError:
            content = json.dumps({
                "zh_cn": {
                    "title": message,
                    "content": [[{"tag": "text", "text": message}]]
                }
            })
    elif msg_type == 'interactive':
        # 卡片消息,必须是 JSON 字符串
        try:
            json.loads(message)
            content = message
        except json.JSONDecodeError:
            print("Error: interactive message must be valid JSON")
            sys.exit(1)
    else:
        content = message
    
    message_data = {
        "receive_id": chat_id,
        "msg_type": msg_type,
        "content": content
    }
    
    msg_req = urllib.request.Request(
        'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id',
        data=json.dumps(message_data).encode('utf-8'),
        headers={
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json'
        },
        method='POST'
    )
    
    with urllib.request.urlopen(msg_req, timeout=30) as response:
        result = json.loads(response.read().decode('utf-8'))
        if result.get('code') == 0:
            print(f"✅ Message sent successfully!")
            print(f"Message ID: {result['data']['message_id']}")
        else:
            print(f"❌ Error: {result.get('msg', 'Unknown error')}")
            print(f"Full response: {json.dumps(result, indent=2, ensure_ascii=False)}")
            sys.exit(1)


def main():
    if len(sys.argv) < 3:
        print("Usage: python3 send_message.py <chat_id> <message> [msg_type]")
        print("  msg_type: text (default), post, interactive")
        sys.exit(1)
    
    chat_id = sys.argv[1]
    message = sys.argv[2]
    msg_type = sys.argv[3] if len(sys.argv) > 3 else 'text'
    
    send_message(chat_id, message, msg_type)


if __name__ == '__main__':
    main()