import argparse import json import sys from datetime import datetime import requests def load_config(path="config/config.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) def build_url(config, path): return f"{config['base_url']}/api/{config['tenant']}/{path}" def build_headers(config, accept="*/*"): return { "accept": accept, "Authorization": f"Bearer {config['token']}", "Content-Type": "application/json", } def cmd_send(config, args): """POST /v1/chat/message/sendMsg - 发送普通消息""" payload = { "chatChannelId": config["chat_channel_id"], "chatContactType": "EXTERNAL", "chatContactId": config["chat_contact_id"], "msgType": args.msg_type or "text", "msgBody": json.dumps({"content": args.content}, ensure_ascii=False), "actualSenderType": args.sender_type or "WORK_WE_CUSTOMER", "sender": {"type": "EXTERNAL"}, } url = build_url(config, "v1/chat/message/sendMsg") resp = requests.post(url, headers=build_headers(config), json=payload, timeout=15) return resp def cmd_stream(config, args): """POST /v1/chat/message/sendMsgStream - 发送消息(流式返回SSE)""" payload = { "chatChannelId": config["chat_channel_id"], "chatContactType": "EXTERNAL", "chatContactId": config["chat_contact_id"], "msgType": args.msg_type or "text", "msgBody": json.dumps({"content": args.content}, ensure_ascii=False), "actualSenderType": args.sender_type or "WORK_WE_CUSTOMER", "sender": {"type": "EXTERNAL"}, } url = build_url(config, "v1/chat/message/sendMsgStream") resp = requests.post( url, headers=build_headers(config, accept="text/event-stream"), json=payload, timeout=60, stream=True, ) if resp.status_code == 200: print(f"[{now()}] 流式响应:") for line in resp.iter_lines(decode_unicode=True): if line: print(f" {line}") return resp def cmd_history(config, args): """GET /v1/chat/message - 获取聊天记录""" params = { "chatChannelId": config["chat_channel_id"], "chatContactId": config["chat_contact_id"], "page": args.page or 0, "size": args.size or 10, } if args.start_time: params["startMsgTime"] = args.start_time if args.end_time: params["endMsgTime"] = args.end_time url = build_url(config, "v1/chat/message") resp = requests.get(url, headers=build_headers(config), params=params, timeout=15) if resp.status_code == 200: data = resp.json() records = data.get("data", []) total = data.get("total", 0) print(f"[{now()}] 共 {total} 条记录, 当前页 {len(records)} 条:") for msg in records: sender = msg.get("senderName") or msg.get("actualSenderName") or msg.get("sender", "?") print(f" [{msg.get('msgTime', '')}] {sender}: {json.dumps(msg.get('msgBody', {}), ensure_ascii=False)}") return resp def now(): return datetime.now().strftime("%H:%M:%S") def print_result(resp): print(f"[{now()}] HTTP {resp.status_code}") if resp.status_code == 200: try: data = resp.json() print(f"[{now()}] 响应: {json.dumps(data, ensure_ascii=False, indent=2)}") status = data.get("status") if status: print(f"[{now()}] 消息状态: {status}") except Exception: print(f"[{now()}] 响应: {resp.text[:500]}") else: print(f"[{now()}] 错误: {resp.text[:500] if resp.text else '(空响应)'}") def main(): parser = argparse.ArgumentParser(description="Tutu API 模拟调用工具") sub = parser.add_subparsers(dest="command") p_send = sub.add_parser("send", help="发送普通消息") p_send.add_argument("content", help="消息内容") p_send.add_argument("--msg-type", default="text", help="消息类型 (默认 text)") p_send.add_argument("--sender-type", help="发送者类型 (默认 WORK_WE_CUSTOMER)") p_stream = sub.add_parser("stream", help="发送流式消息 (SSE)") p_stream.add_argument("content", help="消息内容") p_stream.add_argument("--msg-type", default="text", help="消息类型") p_stream.add_argument("--sender-type", help="发送者类型") p_hist = sub.add_parser("history", help="获取聊天记录") p_hist.add_argument("--page", type=int, default=0, help="页码 (默认 0)") p_hist.add_argument("--size", type=int, default=10, help="每页条数 (默认 10)") p_hist.add_argument("--start-time", help="开始时间 (yyyy-MM-dd HH:mm:ss)") p_hist.add_argument("--end-time", help="结束时间") args = parser.parse_args() if not args.command: parser.print_help() sys.exit(1) config = load_config() commands = { "send": cmd_send, "stream": cmd_stream, "history": cmd_history, } print(f"[{now()}] 命令: {args.command}") resp = commands[args.command](config, args) if args.command != "stream" or resp.status_code != 200: print_result(resp) if __name__ == "__main__": main()