wecom-aibot-sdk-python 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. wecom_aibot_sdk_python-0.1.0/.github/workflows/publish.yml +48 -0
  2. wecom_aibot_sdk_python-0.1.0/.gitignore +30 -0
  3. wecom_aibot_sdk_python-0.1.0/.python-version +1 -0
  4. wecom_aibot_sdk_python-0.1.0/PKG-INFO +176 -0
  5. wecom_aibot_sdk_python-0.1.0/README.md +162 -0
  6. wecom_aibot_sdk_python-0.1.0/docs/README_zh.md +162 -0
  7. wecom_aibot_sdk_python-0.1.0/examples/basic.py +105 -0
  8. wecom_aibot_sdk_python-0.1.0/main.py +5 -0
  9. wecom_aibot_sdk_python-0.1.0/pyproject.toml +32 -0
  10. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/__init__.py +38 -0
  11. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/api.py +59 -0
  12. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/client.py +425 -0
  13. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/crypto.py +50 -0
  14. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/logger.py +34 -0
  15. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/message_handler.py +94 -0
  16. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/__init__.py +33 -0
  17. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/api.py +38 -0
  18. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/common.py +12 -0
  19. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/config.py +21 -0
  20. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/event.py +11 -0
  21. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/types/message.py +189 -0
  22. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/utils.py +20 -0
  23. wecom_aibot_sdk_python-0.1.0/src/wecom_aibot_sdk/ws.py +329 -0
  24. wecom_aibot_sdk_python-0.1.0/tests/test_basic.py +179 -0
@@ -0,0 +1,48 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ test:
9
+ description: 'Publish to TestPyPI'
10
+ required: false
11
+ default: 'false'
12
+ type: boolean
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ build-and-publish:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ id-token: write
22
+
23
+ steps:
24
+ - name: Checkout code
25
+ uses: actions/checkout@v4
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.10"
31
+
32
+ - name: Install build dependencies
33
+ run: |
34
+ python -m pip install --upgrade pip
35
+ pip install build
36
+
37
+ - name: Build package
38
+ run: python -m build
39
+
40
+ - name: Publish to TestPyPI
41
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.test == true }}
42
+ uses: pypa/gh-action-pypi-publish@release/v1
43
+ with:
44
+ repository-url: https://test.pypi.org/legacy/
45
+
46
+ - name: Publish to PyPI
47
+ if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.test != true) }}
48
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,30 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ venv/
12
+ env/
13
+
14
+ # IDE
15
+ .idea/
16
+ .vscode/
17
+ *.swp
18
+ *.swo
19
+
20
+ # OS
21
+ .DS_Store
22
+ Thumbs.db
23
+
24
+ # Test/coverage
25
+ .pytest_cache/
26
+ .coverage
27
+ htmlcov/
28
+
29
+ # Logs
30
+ *.log
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: wecom-aibot-sdk-python
3
+ Version: 0.1.0
4
+ Summary: WeCom AI Bot Python SDK - Based on WebSocket long connection, provides core capabilities including message sending/receiving, streaming replies, template cards, event callbacks, and file download decryption
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: aiohttp>=3.9.0
7
+ Requires-Dist: pycryptodome>=3.20.0
8
+ Requires-Dist: websockets>=12.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
11
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
12
+ Requires-Dist: ruff>=0.3.0; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # WeCom AI Bot Python SDK
16
+
17
+ Enterprise WeChat AI Bot Python SDK - Based on WebSocket long connection, providing message sending/receiving, streaming replies, template cards, event callbacks, file download/decryption and other core capabilities.
18
+
19
+ ## Features
20
+
21
+ - **WebSocket Long Connection** - Built-in default address `wss://openws.work.weixin.qq.com`, ready to use
22
+ - **Auto Authentication** - Automatically sends authentication frame after connection (botId + secret)
23
+ - **Heartbeat Keep-Alive** - Automatic heartbeat maintenance, auto-detects connection issues when ACKs are missing
24
+ - **Auto Reconnect** - Exponential backoff reconnection strategy (1s → 2s → 4s → ... → 30s max)
25
+ - **Message Dispatch** - Auto-parses message types and triggers corresponding events (text/image/mixed/voice/file)
26
+ - **Streaming Reply** - Built-in streaming reply methods, supports Markdown and mixed content
27
+ - **Template Cards** - Supports replying with template card messages, stream+card combo replies, card updates
28
+ - **Proactive Push** - Proactively send Markdown or template card messages to specified chats
29
+ - **Event Callbacks** - Supports enter_chat, template_card_event, feedback_event
30
+ - **Serial Reply Queue** - Replies with same req_id are sent serially, auto-waits for receipt
31
+ - **File Download & Decryption** - Built-in AES-256-CBC file decryption, each image/file message has its own aeskey
32
+ - **Pluggable Logging** - Supports custom Logger, includes DefaultLogger with timestamps
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install wecom-aibot-sdk
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ import asyncio
44
+ from wecom_aibot_sdk import WSClient, generate_req_id
45
+
46
+ async def main():
47
+ # 1. Create client instance
48
+ client = WSClient({
49
+ "bot_id": "your-bot-id",
50
+ "secret": "your-bot-secret",
51
+ })
52
+
53
+ # 2. Listen for text messages and reply with streaming
54
+ async def on_text(frame):
55
+ content = frame.body.get("text", {}).get("content", "")
56
+ stream_id = generate_req_id("stream")
57
+
58
+ # Send intermediate content
59
+ await client.reply_stream(frame, stream_id, "Thinking...", finish=False)
60
+
61
+ # Send final result
62
+ await client.reply_stream(frame, stream_id, f'You said: "{content}"', finish=True)
63
+
64
+ client.on("message.text", on_text)
65
+
66
+ # 3. Listen for enter_chat event (send welcome)
67
+ async def on_enter(frame):
68
+ await client.reply_welcome(frame, {
69
+ "msgtype": "text",
70
+ "text": {"content": "Hello! How can I help you?"},
71
+ })
72
+
73
+ client.on("event.enter_chat", on_enter)
74
+
75
+ # 4. Connect
76
+ await client.connect_async()
77
+
78
+ # Keep running
79
+ while client.is_connected:
80
+ await asyncio.sleep(1)
81
+
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ## API Reference
86
+
87
+ ### WSClient
88
+
89
+ Core client class providing connection management, message sending/receiving.
90
+
91
+ ```python
92
+ client = WSClient({
93
+ "bot_id": "your-bot-id",
94
+ "secret": "your-bot-secret",
95
+ # Optional:
96
+ "reconnect_interval": 1000, # Reconnect base delay (ms)
97
+ "max_reconnect_attempts": 10, # Max reconnect attempts (-1 for infinite)
98
+ "heartbeat_interval": 30000, # Heartbeat interval (ms)
99
+ "request_timeout": 10000, # HTTP request timeout (ms)
100
+ "ws_url": "wss://...", # Custom WebSocket URL
101
+ "logger": custom_logger, # Custom logger instance
102
+ })
103
+ ```
104
+
105
+ #### Methods
106
+
107
+ | Method | Description | Returns |
108
+ |--------|-------------|---------|
109
+ | `connect_async()` | Establish WebSocket connection | `None` |
110
+ | `disconnect()` | Disconnect | `None` |
111
+ | `reply(frame, body, cmd?)` | Send reply message (generic) | `None` |
112
+ | `reply_stream(frame, stream_id, content, finish?, msg_item?, feedback?)` | Send streaming reply | `None` |
113
+ | `reply_welcome(frame, body)` | Send welcome reply (within 5s of event) | `None` |
114
+ | `reply_template_card(frame, template_card, feedback?)` | Reply with template card | `None` |
115
+ | `reply_stream_with_card(frame, stream_id, content, finish?, options?)` | Send stream + card combo | `None` |
116
+ | `update_template_card(frame, template_card, userids?)` | Update template card (within 5s) | `None` |
117
+ | `send_message(chatid, body)` | Proactively send message | `None` |
118
+ | `download_file(url, aes_key)` | Download and decrypt file | `tuple[bytes, str?]` |
119
+
120
+ #### Events
121
+
122
+ | Event | Callback | Description |
123
+ |-------|----------|-------------|
124
+ | `connected` | `()` | WebSocket connected |
125
+ | `authenticated` | `()` | Authentication successful |
126
+ | `disconnected` | `(reason)` | Connection lost |
127
+ | `reconnecting` | `(attempt)` | Reconnecting (attempt N) |
128
+ | `error` | `(frame)` | Error occurred |
129
+ | `message` | `(frame)` | Any message received |
130
+ | `message.text` | `(frame)` | Text message |
131
+ | `message.image` | `(frame)` | Image message |
132
+ | `message.mixed` | `(frame)` | Mixed content message |
133
+ | `message.voice` | `(frame)` | Voice message |
134
+ | `message.file` | `(frame)` | File message |
135
+ | `event` | `(frame)` | Any event |
136
+ | `event.enter_chat` | `(frame)` | User entered chat |
137
+ | `event.template_card_event` | `(frame)` | Card button clicked |
138
+ | `event.feedback_event` | `(frame)` | User feedback |
139
+
140
+ ## Project Structure
141
+
142
+ ```
143
+ wecom_aibot_sdk/
144
+ ├── __init__.py # Package entry, exports
145
+ ├── client.py # WSClient core client
146
+ ├── ws.py # WebSocket connection manager
147
+ ├── message_handler.py # Message parsing and event dispatch
148
+ ├── api.py # HTTP API client (file download)
149
+ ├── crypto.py # AES-256-CBC file decryption
150
+ ├── logger.py # Default logger implementation
151
+ ├── utils.py # Utility functions (generate_req_id, etc.)
152
+ └── types/ # Type definitions
153
+ ├── __init__.py
154
+ ├── config.py # Configuration types
155
+ ├── event.py # Event types
156
+ ├── message.py # Message types
157
+ ├── api.py # API/WebSocket frame types
158
+ └── common.py # Common types (Logger)
159
+ ```
160
+
161
+ ## Development
162
+
163
+ ```bash
164
+ # Install dev dependencies
165
+ pip install -e ".[dev]"
166
+
167
+ # Run tests
168
+ pytest
169
+
170
+ # Format code
171
+ ruff format .
172
+ ```
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,162 @@
1
+ # WeCom AI Bot Python SDK
2
+
3
+ Enterprise WeChat AI Bot Python SDK - Based on WebSocket long connection, providing message sending/receiving, streaming replies, template cards, event callbacks, file download/decryption and other core capabilities.
4
+
5
+ ## Features
6
+
7
+ - **WebSocket Long Connection** - Built-in default address `wss://openws.work.weixin.qq.com`, ready to use
8
+ - **Auto Authentication** - Automatically sends authentication frame after connection (botId + secret)
9
+ - **Heartbeat Keep-Alive** - Automatic heartbeat maintenance, auto-detects connection issues when ACKs are missing
10
+ - **Auto Reconnect** - Exponential backoff reconnection strategy (1s → 2s → 4s → ... → 30s max)
11
+ - **Message Dispatch** - Auto-parses message types and triggers corresponding events (text/image/mixed/voice/file)
12
+ - **Streaming Reply** - Built-in streaming reply methods, supports Markdown and mixed content
13
+ - **Template Cards** - Supports replying with template card messages, stream+card combo replies, card updates
14
+ - **Proactive Push** - Proactively send Markdown or template card messages to specified chats
15
+ - **Event Callbacks** - Supports enter_chat, template_card_event, feedback_event
16
+ - **Serial Reply Queue** - Replies with same req_id are sent serially, auto-waits for receipt
17
+ - **File Download & Decryption** - Built-in AES-256-CBC file decryption, each image/file message has its own aeskey
18
+ - **Pluggable Logging** - Supports custom Logger, includes DefaultLogger with timestamps
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install wecom-aibot-sdk
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```python
29
+ import asyncio
30
+ from wecom_aibot_sdk import WSClient, generate_req_id
31
+
32
+ async def main():
33
+ # 1. Create client instance
34
+ client = WSClient({
35
+ "bot_id": "your-bot-id",
36
+ "secret": "your-bot-secret",
37
+ })
38
+
39
+ # 2. Listen for text messages and reply with streaming
40
+ async def on_text(frame):
41
+ content = frame.body.get("text", {}).get("content", "")
42
+ stream_id = generate_req_id("stream")
43
+
44
+ # Send intermediate content
45
+ await client.reply_stream(frame, stream_id, "Thinking...", finish=False)
46
+
47
+ # Send final result
48
+ await client.reply_stream(frame, stream_id, f'You said: "{content}"', finish=True)
49
+
50
+ client.on("message.text", on_text)
51
+
52
+ # 3. Listen for enter_chat event (send welcome)
53
+ async def on_enter(frame):
54
+ await client.reply_welcome(frame, {
55
+ "msgtype": "text",
56
+ "text": {"content": "Hello! How can I help you?"},
57
+ })
58
+
59
+ client.on("event.enter_chat", on_enter)
60
+
61
+ # 4. Connect
62
+ await client.connect_async()
63
+
64
+ # Keep running
65
+ while client.is_connected:
66
+ await asyncio.sleep(1)
67
+
68
+ asyncio.run(main())
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ ### WSClient
74
+
75
+ Core client class providing connection management, message sending/receiving.
76
+
77
+ ```python
78
+ client = WSClient({
79
+ "bot_id": "your-bot-id",
80
+ "secret": "your-bot-secret",
81
+ # Optional:
82
+ "reconnect_interval": 1000, # Reconnect base delay (ms)
83
+ "max_reconnect_attempts": 10, # Max reconnect attempts (-1 for infinite)
84
+ "heartbeat_interval": 30000, # Heartbeat interval (ms)
85
+ "request_timeout": 10000, # HTTP request timeout (ms)
86
+ "ws_url": "wss://...", # Custom WebSocket URL
87
+ "logger": custom_logger, # Custom logger instance
88
+ })
89
+ ```
90
+
91
+ #### Methods
92
+
93
+ | Method | Description | Returns |
94
+ |--------|-------------|---------|
95
+ | `connect_async()` | Establish WebSocket connection | `None` |
96
+ | `disconnect()` | Disconnect | `None` |
97
+ | `reply(frame, body, cmd?)` | Send reply message (generic) | `None` |
98
+ | `reply_stream(frame, stream_id, content, finish?, msg_item?, feedback?)` | Send streaming reply | `None` |
99
+ | `reply_welcome(frame, body)` | Send welcome reply (within 5s of event) | `None` |
100
+ | `reply_template_card(frame, template_card, feedback?)` | Reply with template card | `None` |
101
+ | `reply_stream_with_card(frame, stream_id, content, finish?, options?)` | Send stream + card combo | `None` |
102
+ | `update_template_card(frame, template_card, userids?)` | Update template card (within 5s) | `None` |
103
+ | `send_message(chatid, body)` | Proactively send message | `None` |
104
+ | `download_file(url, aes_key)` | Download and decrypt file | `tuple[bytes, str?]` |
105
+
106
+ #### Events
107
+
108
+ | Event | Callback | Description |
109
+ |-------|----------|-------------|
110
+ | `connected` | `()` | WebSocket connected |
111
+ | `authenticated` | `()` | Authentication successful |
112
+ | `disconnected` | `(reason)` | Connection lost |
113
+ | `reconnecting` | `(attempt)` | Reconnecting (attempt N) |
114
+ | `error` | `(frame)` | Error occurred |
115
+ | `message` | `(frame)` | Any message received |
116
+ | `message.text` | `(frame)` | Text message |
117
+ | `message.image` | `(frame)` | Image message |
118
+ | `message.mixed` | `(frame)` | Mixed content message |
119
+ | `message.voice` | `(frame)` | Voice message |
120
+ | `message.file` | `(frame)` | File message |
121
+ | `event` | `(frame)` | Any event |
122
+ | `event.enter_chat` | `(frame)` | User entered chat |
123
+ | `event.template_card_event` | `(frame)` | Card button clicked |
124
+ | `event.feedback_event` | `(frame)` | User feedback |
125
+
126
+ ## Project Structure
127
+
128
+ ```
129
+ wecom_aibot_sdk/
130
+ ├── __init__.py # Package entry, exports
131
+ ├── client.py # WSClient core client
132
+ ├── ws.py # WebSocket connection manager
133
+ ├── message_handler.py # Message parsing and event dispatch
134
+ ├── api.py # HTTP API client (file download)
135
+ ├── crypto.py # AES-256-CBC file decryption
136
+ ├── logger.py # Default logger implementation
137
+ ├── utils.py # Utility functions (generate_req_id, etc.)
138
+ └── types/ # Type definitions
139
+ ├── __init__.py
140
+ ├── config.py # Configuration types
141
+ ├── event.py # Event types
142
+ ├── message.py # Message types
143
+ ├── api.py # API/WebSocket frame types
144
+ └── common.py # Common types (Logger)
145
+ ```
146
+
147
+ ## Development
148
+
149
+ ```bash
150
+ # Install dev dependencies
151
+ pip install -e ".[dev]"
152
+
153
+ # Run tests
154
+ pytest
155
+
156
+ # Format code
157
+ ruff format .
158
+ ```
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,162 @@
1
+ # 企业微信智能机器人 Python SDK
2
+
3
+ 基于 WebSocket 长连接通道,提供消息收发、流式回复、模板卡片、事件回调、文件下载解密等核心能力。
4
+
5
+ ## 功能特性
6
+
7
+ - **WebSocket 长连接** - 内置默认地址 `wss://openws.work.weixin.qq.com`,开箱即用
8
+ - **自动认证** - 连接后自动发送认证帧(botId + secret)
9
+ - **心跳保活** - 自动维护心跳,ACK 缺失时自动检测连接问题
10
+ - **自动重连** - 指数退避重连策略(1s → 2s → 4s → ... → 最大 30s)
11
+ - **消息分发** - 自动解析消息类型并触发相应事件(text/image/mixed/voice/file)
12
+ - **流式回复** - 内置流式回复方法,支持 Markdown 和混合内容
13
+ - **模板卡片** - 支持回复模板卡片消息、流式+卡片组合回复、卡片更新
14
+ - **主动推送** - 主动向指定会话发送 Markdown 或模板卡片消息
15
+ - **事件回调** - 支持 enter_chat、template_card_event、feedback_event
16
+ - **串行回复队列** - 相同 req_id 的回复串行发送,自动等待回执
17
+ - **文件下载解密** - 内置 AES-256-CBC 文件解密,每条图片/文件消息有独立 aeskey
18
+ - **可插拔日志** - 支持自定义 Logger,内置带时间戳的 DefaultLogger
19
+
20
+ ## 安装
21
+
22
+ ```bash
23
+ pip install wecom-aibot-sdk
24
+ ```
25
+
26
+ ## 快速开始
27
+
28
+ ```python
29
+ import asyncio
30
+ from wecom_aibot_sdk import WSClient, generate_req_id
31
+
32
+ async def main():
33
+ # 1. 创建客户端实例
34
+ client = WSClient({
35
+ "bot_id": "your-bot-id",
36
+ "secret": "your-bot-secret",
37
+ })
38
+
39
+ # 2. 监听文本消息并以流式方式回复
40
+ async def on_text(frame):
41
+ content = frame.body.get("text", {}).get("content", "")
42
+ stream_id = generate_req_id("stream")
43
+
44
+ # 发送中间内容
45
+ await client.reply_stream(frame, stream_id, "正在思考...", finish=False)
46
+
47
+ # 发送最终结果
48
+ await client.reply_stream(frame, stream_id, f'你说:"{content}"', finish=True)
49
+
50
+ client.on("message.text", on_text)
51
+
52
+ # 3. 监听进入会话事件(发送欢迎语)
53
+ async def on_enter(frame):
54
+ await client.reply_welcome(frame, {
55
+ "msgtype": "text",
56
+ "text": {"content": "你好!有什么可以帮助你的吗?"},
57
+ })
58
+
59
+ client.on("event.enter_chat", on_enter)
60
+
61
+ # 4. 建立连接
62
+ await client.connect_async()
63
+
64
+ # 保持运行
65
+ while client.is_connected:
66
+ await asyncio.sleep(1)
67
+
68
+ asyncio.run(main())
69
+ ```
70
+
71
+ ## API 参考
72
+
73
+ ### WSClient
74
+
75
+ 核心客户端类,提供连接管理、消息收发。
76
+
77
+ ```python
78
+ client = WSClient({
79
+ "bot_id": "your-bot-id",
80
+ "secret": "your-bot-secret",
81
+ # 可选配置:
82
+ "reconnect_interval": 1000, # 重连基础延迟(毫秒)
83
+ "max_reconnect_attempts": 10, # 最大重连次数(-1 为无限)
84
+ "heartbeat_interval": 30000, # 心跳间隔(毫秒)
85
+ "request_timeout": 10000, # HTTP 请求超时(毫秒)
86
+ "ws_url": "wss://...", # 自定义 WebSocket URL
87
+ "logger": custom_logger, # 自定义日志实例
88
+ })
89
+ ```
90
+
91
+ #### 方法
92
+
93
+ | 方法 | 描述 | 返回值 |
94
+ |------|------|--------|
95
+ | `connect_async()` | 建立 WebSocket 连接 | `None` |
96
+ | `disconnect()` | 断开连接 | `None` |
97
+ | `reply(frame, body, cmd?)` | 发送回复消息(通用) | `None` |
98
+ | `reply_stream(frame, stream_id, content, finish?, msg_item?, feedback?)` | 发送流式回复 | `None` |
99
+ | `reply_welcome(frame, body)` | 发送欢迎回复(事件后 5 秒内) | `None` |
100
+ | `reply_template_card(frame, template_card, feedback?)` | 回复模板卡片 | `None` |
101
+ | `reply_stream_with_card(frame, stream_id, content, finish?, options?)` | 发送流式 + 卡片组合 | `None` |
102
+ | `update_template_card(frame, template_card, userids?)` | 更新模板卡片(5 秒内) | `None` |
103
+ | `send_message(chatid, body)` | 主动发送消息 | `None` |
104
+ | `download_file(url, aes_key)` | 下载并解密文件 | `tuple[bytes, str?]` |
105
+
106
+ #### 事件
107
+
108
+ | 事件 | 回调 | 描述 |
109
+ |------|------|------|
110
+ | `connected` | `()` | WebSocket 已连接 |
111
+ | `authenticated` | `()` | 认证成功 |
112
+ | `disconnected` | `(reason)` | 连接断开 |
113
+ | `reconnecting` | `(attempt)` | 正在重连(第 N 次) |
114
+ | `error` | `(frame)` | 发生错误 |
115
+ | `message` | `(frame)` | 收到任意消息 |
116
+ | `message.text` | `(frame)` | 文本消息 |
117
+ | `message.image` | `(frame)` | 图片消息 |
118
+ | `message.mixed` | `(frame)` | 混合内容消息 |
119
+ | `message.voice` | `(frame)` | 语音消息 |
120
+ | `message.file` | `(frame)` | 文件消息 |
121
+ | `event` | `(frame)` | 任意事件 |
122
+ | `event.enter_chat` | `(frame)` | 用户进入会话 |
123
+ | `event.template_card_event` | `(frame)` | 卡片按钮点击 |
124
+ | `event.feedback_event` | `(frame)` | 用户反馈 |
125
+
126
+ ## 项目结构
127
+
128
+ ```
129
+ wecom_aibot_sdk/
130
+ ├── __init__.py # 包入口,导出
131
+ ├── client.py # WSClient 核心客户端
132
+ ├── ws.py # WebSocket 连接管理器
133
+ ├── message_handler.py # 消息解析和事件分发
134
+ ├── api.py # HTTP API 客户端(文件下载)
135
+ ├── crypto.py # AES-256-CBC 文件解密
136
+ ├── logger.py # 默认日志实现
137
+ ├── utils.py # 工具函数(generate_req_id 等)
138
+ └── types/ # 类型定义
139
+ ├── __init__.py
140
+ ├── config.py # 配置类型
141
+ ├── event.py # 事件类型
142
+ ├── message.py # 消息类型
143
+ ├── api.py # API/WebSocket 帧类型
144
+ └── common.py # 通用类型(Logger)
145
+ ```
146
+
147
+ ## 开发
148
+
149
+ ```bash
150
+ # 安装开发依赖
151
+ pip install -e ".[dev]"
152
+
153
+ # 运行测试
154
+ pytest
155
+
156
+ # 格式化代码
157
+ ruff format .
158
+ ```
159
+
160
+ ## 许可证
161
+
162
+ MIT
@@ -0,0 +1,105 @@
1
+ """
2
+ Basic usage example for WeCom AI Bot SDK
3
+ """
4
+
5
+ import asyncio
6
+ import signal
7
+
8
+ from wecom_aibot_sdk import WSClient, generate_req_id
9
+
10
+
11
+ async def main():
12
+ # 1. Create client instance
13
+ client = WSClient({
14
+ "bot_id": "your-bot-id", # Get from WeCom admin console
15
+ "secret": "your-bot-secret", # Get from WeCom admin console
16
+ })
17
+
18
+ # 2. Register event handlers
19
+
20
+ # Handle authentication success
21
+ async def on_authenticated(frame):
22
+ print("Authenticated successfully")
23
+
24
+ client.on("authenticated", on_authenticated)
25
+
26
+ # Handle text messages with streaming reply
27
+ async def on_text_message(frame):
28
+ content = frame.body.get("text", {}).get("content", "")
29
+ print(f"Received text: {content}")
30
+
31
+ stream_id = generate_req_id("stream")
32
+
33
+ # Send streaming intermediate content
34
+ await client.reply_stream(frame, stream_id, "Thinking...", finish=False)
35
+
36
+ # Send final result
37
+ await asyncio.sleep(1)
38
+ await client.reply_stream(
39
+ frame,
40
+ stream_id,
41
+ f'Hello! You said: "{content}"',
42
+ finish=True,
43
+ )
44
+
45
+ client.on("message.text", on_text_message)
46
+
47
+ # Handle enter chat event (send welcome message)
48
+ async def on_enter_chat(frame):
49
+ await client.reply_welcome(frame, {
50
+ "msgtype": "text",
51
+ "text": {"content": "Hello! I'm your AI assistant. How can I help you?"},
52
+ })
53
+
54
+ client.on("event.enter_chat", on_enter_chat)
55
+
56
+ # Handle image messages
57
+ async def on_image_message(frame):
58
+ body = frame.body
59
+ image = body.get("image", {})
60
+ url = image.get("url", "")
61
+ aeskey = image.get("aeskey", "")
62
+
63
+ if url and aeskey:
64
+ buffer, filename = await client.download_file(url, aeskey)
65
+ print(f"Downloaded image: {filename}, size: {len(buffer)} bytes")
66
+
67
+ client.on("message.image", on_image_message)
68
+
69
+ # Handle errors
70
+ async def on_error(frame):
71
+ print(f"Error: {frame.body}")
72
+
73
+ client.on("error", on_error)
74
+
75
+ # 3. Connect
76
+ print("Connecting...")
77
+ await client.connect_async()
78
+
79
+ # 4. Graceful shutdown
80
+ loop = asyncio.get_event_loop()
81
+
82
+ def signal_handler():
83
+ print("\nShutting down...")
84
+ asyncio.create_task(client.disconnect())
85
+
86
+ for sig in (signal.SIGINT, signal.SIGTERM):
87
+ try:
88
+ loop.add_signal_handler(sig, signal_handler)
89
+ except NotImplementedError:
90
+ # Windows doesn't support add_signal_handler
91
+ pass
92
+
93
+ # Keep running
94
+ print("Client running. Press Ctrl+C to exit.")
95
+ try:
96
+ while client.is_connected:
97
+ await asyncio.sleep(1)
98
+ except asyncio.CancelledError:
99
+ pass
100
+ finally:
101
+ await client.disconnect()
102
+
103
+
104
+ if __name__ == "__main__":
105
+ asyncio.run(main())
@@ -0,0 +1,5 @@
1
+ """Entry point for development/testing"""
2
+
3
+ from wecom_aibot_sdk import WSClient, generate_req_id, DefaultLogger
4
+
5
+ __all__ = ["WSClient", "generate_req_id", "DefaultLogger"]