qqbotsdk-py 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 snowy-yang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: qqbotsdk-py
3
+ Version: 0.1.0
4
+ Summary: QQ 官方机器人 Python SDK(WebSocket / Webhook)
5
+ Keywords: qq,qqbot,bot,sdk,websocket,webhook,机器人
6
+ Author: snowy-yang
7
+ Author-email: snowy-yang <102860110+snowy-yang@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Natural Language :: Chinese (Simplified)
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Dist: aiohttp>=3.14.3
19
+ Requires-Dist: cryptography>=50.0.1
20
+ Requires-Dist: loguru>=0.7.3
21
+ Requires-Dist: python-dotenv>=1.2.3
22
+ Requires-Dist: ujson>=6.0.0
23
+ Requires-Python: >=3.12
24
+ Project-URL: Homepage, https://github.com/snowy-yang/qqbotsdk
25
+ Project-URL: Repository, https://github.com/snowy-yang/qqbotsdk
26
+ Project-URL: Changelog, https://github.com/snowy-yang/qqbotsdk/blob/main/CHANGELOG.md
27
+ Description-Content-Type: text/markdown
28
+
29
+ # qqbotsdk
30
+
31
+ QQ 官方机器人 Python SDK(Python 3.12+ / asyncio / aiohttp),支持 WebSocket 与 Webhook 两种接入方式。
32
+
33
+ **📖 在线文档:<https://qqbotsdk.4i.hk/>**
34
+
35
+ [使用指南](docs/GUIDE.md) · [API 参考](docs/API.md) · [架构](docs/ARCHITECTURE.md) · [示例](examples/) · [更新日志](CHANGELOG.md)
36
+
37
+ ## 架构
38
+
39
+ ```mermaid
40
+ flowchart TB
41
+ subgraph 接入层
42
+ WS["WebsocketConnecter(ws 长连接 / 重连 / 心跳看门狗)"]
43
+ WH["WebhookConnecter(http 回调 / 验签 / 去重)"]
44
+ end
45
+ subgraph 协议层
46
+ WSP["WebsocketProtocol(握手 / 会话 / 序列号)"]
47
+ WHP["WebhookProtocol(op=13 验证应答)"]
48
+ end
49
+ subgraph 分发层
50
+ Q[("EventQueue<br/>业务事件 op=0")]
51
+ EE["EventEmitter(路由 / 参数注入 / 异常隔离)"]
52
+ end
53
+ subgraph 业务层
54
+ API["BotApi(REST + ApiError)"]
55
+ USER["用户 handler @ee.on(...)"]
56
+ end
57
+ QQ["QQ 开放平台"]
58
+
59
+ WS -. "逐帧 on_frame" .-> WSP
60
+ WH -. "op=13 逐帧 on_frame" .-> WHP
61
+ WS -->|"业务事件"| Q
62
+ WH -->|"业务事件"| Q
63
+ Q --> EE
64
+ EE --> USER
65
+ USER --> API
66
+ API --> QQ
67
+ WS --> QQ
68
+ ```
69
+
70
+ 组件在 `run_loop` 中显式装配,依赖关系与设计决策详见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
71
+
72
+ ## 特性
73
+
74
+ - 断线自动重连(指数退避 + 心跳看门狗),重连时自动 RESUME 续传
75
+ - Webhook 接入自带 Ed25519 验签、op=13 验证应答与事件去重
76
+ - 事件订阅由项目根目录的 `intents.toml` 按大类开关
77
+ - Markdown 与内嵌按钮一等支持,`respond_interaction()` 回应按钮回调
78
+ - 组件显式装配(无全局单例),全 SDK 共享一个 HTTP 连接池
79
+
80
+ ## 配置
81
+
82
+ `.env`(`APPID`/`APPSECRET` 必填,缺失启动即报错):
83
+
84
+ ```ini
85
+ APPID=你的AppID
86
+ APPSECRET=你的AppSecret
87
+ BASE_URL=https://api.bot.qq.com # 可选
88
+ TIMEOUT=5000 # 可选,毫秒
89
+ CONNECTER=websocket # websocket 或 webhook
90
+
91
+ # 仅 webhook 接入时
92
+ WEBHOOK_HOST=0.0.0.0
93
+ WEBHOOK_PORT=8080
94
+ WEBHOOK_PATH=/qqbot/webhook
95
+ ```
96
+
97
+ ## 使用
98
+
99
+ ```python
100
+ from qqbotsdk import EventEmitter, main
101
+ from qqbotsdk.payloads import GroupAtMessage
102
+ from qqbotsdk.api import BotApi
103
+
104
+ ee = EventEmitter()
105
+
106
+ # handler 形参按标注注入 payload 与 BotApi 等组件
107
+ @ee.on("GROUP_AT_MESSAGE_CREATE")
108
+ async def on_group_message(msg: GroupAtMessage, api: BotApi):
109
+ # 被动回复带 msg_id;多次回复递增 msg_seq;失败抛 ApiError
110
+ await api.post_group_message(
111
+ msg.group_openid,
112
+ content=f"你说:{msg.content}",
113
+ msg_id=msg.id,
114
+ )
115
+
116
+ if __name__ == "__main__":
117
+ main(ee)
118
+ ```
119
+
120
+ 运行:
121
+
122
+ ```bash
123
+ uv run python main.py
124
+ ```
125
+
126
+ Webhook 接入:`.env` 中 `CONNECTER` 改为 `webhook`,再把回调地址配置到 QQ 开放平台(仅支持 80/443/8080/8443 端口,需公网 HTTPS)。
127
+
128
+ 完整示例见 [examples/](examples/),更多写法见[使用指南](docs/GUIDE.md)。
129
+
130
+ ## 开发
131
+
132
+ ```bash
133
+ uv run pytest # 单元测试
134
+ uv run ruff check src tests examples && uv run pyright
135
+ ```
136
+
137
+ 推送与 Pull Request 由 GitHub Actions 自动执行同样的检查([.github/workflows/ci.yml](.github/workflows/ci.yml))。
138
+
139
+ 文档站点基于 [docsify](https://docsify.js.org/),文档变更时自动发布([.github/workflows/deploy-docs.yml](.github/workflows/deploy-docs.yml));本地预览:
140
+
141
+ ```bash
142
+ npx docsify-cli serve
143
+ ```
144
+
145
+ ## 待完善
146
+
147
+ - 更多 OpenAPI 封装(按需在 `api.py` 的 `BotApi` 上加薄封装即可)
@@ -0,0 +1,119 @@
1
+ # qqbotsdk
2
+
3
+ QQ 官方机器人 Python SDK(Python 3.12+ / asyncio / aiohttp),支持 WebSocket 与 Webhook 两种接入方式。
4
+
5
+ **📖 在线文档:<https://qqbotsdk.4i.hk/>**
6
+
7
+ [使用指南](docs/GUIDE.md) · [API 参考](docs/API.md) · [架构](docs/ARCHITECTURE.md) · [示例](examples/) · [更新日志](CHANGELOG.md)
8
+
9
+ ## 架构
10
+
11
+ ```mermaid
12
+ flowchart TB
13
+ subgraph 接入层
14
+ WS["WebsocketConnecter(ws 长连接 / 重连 / 心跳看门狗)"]
15
+ WH["WebhookConnecter(http 回调 / 验签 / 去重)"]
16
+ end
17
+ subgraph 协议层
18
+ WSP["WebsocketProtocol(握手 / 会话 / 序列号)"]
19
+ WHP["WebhookProtocol(op=13 验证应答)"]
20
+ end
21
+ subgraph 分发层
22
+ Q[("EventQueue<br/>业务事件 op=0")]
23
+ EE["EventEmitter(路由 / 参数注入 / 异常隔离)"]
24
+ end
25
+ subgraph 业务层
26
+ API["BotApi(REST + ApiError)"]
27
+ USER["用户 handler @ee.on(...)"]
28
+ end
29
+ QQ["QQ 开放平台"]
30
+
31
+ WS -. "逐帧 on_frame" .-> WSP
32
+ WH -. "op=13 逐帧 on_frame" .-> WHP
33
+ WS -->|"业务事件"| Q
34
+ WH -->|"业务事件"| Q
35
+ Q --> EE
36
+ EE --> USER
37
+ USER --> API
38
+ API --> QQ
39
+ WS --> QQ
40
+ ```
41
+
42
+ 组件在 `run_loop` 中显式装配,依赖关系与设计决策详见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
43
+
44
+ ## 特性
45
+
46
+ - 断线自动重连(指数退避 + 心跳看门狗),重连时自动 RESUME 续传
47
+ - Webhook 接入自带 Ed25519 验签、op=13 验证应答与事件去重
48
+ - 事件订阅由项目根目录的 `intents.toml` 按大类开关
49
+ - Markdown 与内嵌按钮一等支持,`respond_interaction()` 回应按钮回调
50
+ - 组件显式装配(无全局单例),全 SDK 共享一个 HTTP 连接池
51
+
52
+ ## 配置
53
+
54
+ `.env`(`APPID`/`APPSECRET` 必填,缺失启动即报错):
55
+
56
+ ```ini
57
+ APPID=你的AppID
58
+ APPSECRET=你的AppSecret
59
+ BASE_URL=https://api.bot.qq.com # 可选
60
+ TIMEOUT=5000 # 可选,毫秒
61
+ CONNECTER=websocket # websocket 或 webhook
62
+
63
+ # 仅 webhook 接入时
64
+ WEBHOOK_HOST=0.0.0.0
65
+ WEBHOOK_PORT=8080
66
+ WEBHOOK_PATH=/qqbot/webhook
67
+ ```
68
+
69
+ ## 使用
70
+
71
+ ```python
72
+ from qqbotsdk import EventEmitter, main
73
+ from qqbotsdk.payloads import GroupAtMessage
74
+ from qqbotsdk.api import BotApi
75
+
76
+ ee = EventEmitter()
77
+
78
+ # handler 形参按标注注入 payload 与 BotApi 等组件
79
+ @ee.on("GROUP_AT_MESSAGE_CREATE")
80
+ async def on_group_message(msg: GroupAtMessage, api: BotApi):
81
+ # 被动回复带 msg_id;多次回复递增 msg_seq;失败抛 ApiError
82
+ await api.post_group_message(
83
+ msg.group_openid,
84
+ content=f"你说:{msg.content}",
85
+ msg_id=msg.id,
86
+ )
87
+
88
+ if __name__ == "__main__":
89
+ main(ee)
90
+ ```
91
+
92
+ 运行:
93
+
94
+ ```bash
95
+ uv run python main.py
96
+ ```
97
+
98
+ Webhook 接入:`.env` 中 `CONNECTER` 改为 `webhook`,再把回调地址配置到 QQ 开放平台(仅支持 80/443/8080/8443 端口,需公网 HTTPS)。
99
+
100
+ 完整示例见 [examples/](examples/),更多写法见[使用指南](docs/GUIDE.md)。
101
+
102
+ ## 开发
103
+
104
+ ```bash
105
+ uv run pytest # 单元测试
106
+ uv run ruff check src tests examples && uv run pyright
107
+ ```
108
+
109
+ 推送与 Pull Request 由 GitHub Actions 自动执行同样的检查([.github/workflows/ci.yml](.github/workflows/ci.yml))。
110
+
111
+ 文档站点基于 [docsify](https://docsify.js.org/),文档变更时自动发布([.github/workflows/deploy-docs.yml](.github/workflows/deploy-docs.yml));本地预览:
112
+
113
+ ```bash
114
+ npx docsify-cli serve
115
+ ```
116
+
117
+ ## 待完善
118
+
119
+ - 更多 OpenAPI 封装(按需在 `api.py` 的 `BotApi` 上加薄封装即可)
@@ -0,0 +1,118 @@
1
+ [project]
2
+ name = "qqbotsdk-py"
3
+ version = "0.1.0"
4
+ description = "QQ 官方机器人 Python SDK(WebSocket / Webhook)"
5
+ license = "MIT"
6
+ license-files = ["LICENSE"]
7
+ readme = "README.md"
8
+ keywords = [
9
+ "qq",
10
+ "qqbot",
11
+ "bot",
12
+ "sdk",
13
+ "websocket",
14
+ "webhook",
15
+ "机器人",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "Natural Language :: Chinese (Simplified)",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ ]
27
+ requires-python = ">=3.12"
28
+ dependencies = [
29
+ "aiohttp>=3.14.3",
30
+ "cryptography>=50.0.1",
31
+ "loguru>=0.7.3",
32
+ "python-dotenv>=1.2.3",
33
+ "ujson>=6.0.0",
34
+ ]
35
+
36
+ [[project.authors]]
37
+ name = "snowy-yang"
38
+ email = "102860110+snowy-yang@users.noreply.github.com"
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/snowy-yang/qqbotsdk"
42
+ Repository = "https://github.com/snowy-yang/qqbotsdk"
43
+ Changelog = "https://github.com/snowy-yang/qqbotsdk/blob/main/CHANGELOG.md"
44
+
45
+ [project.scripts]
46
+ qqbotsdk = "qqbotsdk:main"
47
+
48
+ [build-system]
49
+ requires = ["uv_build>=0.12.7,<0.13.0"]
50
+ build-backend = "uv_build"
51
+
52
+ [tool.uv.build-backend]
53
+ module-name = "qqbotsdk"
54
+
55
+ [tool.pytest.ini_options]
56
+ asyncio_mode = "auto"
57
+ testpaths = ["tests"]
58
+
59
+ [tool.ruff]
60
+ target-version = "py312"
61
+ line-length = 88
62
+ indent-width = 4
63
+
64
+ [tool.ruff.lint]
65
+ select = [
66
+ "E",
67
+ "F",
68
+ "I",
69
+ "N",
70
+ "W",
71
+ "UP",
72
+ "B",
73
+ "C4",
74
+ "SIM",
75
+ ]
76
+ ignore = [
77
+ "E501",
78
+ "B008",
79
+ ]
80
+
81
+ [tool.ruff.lint.pydocstyle]
82
+ convention = "google"
83
+
84
+ [tool.ruff.lint.isort]
85
+ known-first-party = ["qqbotsdk"]
86
+
87
+ [tool.ruff.lint.per-file-ignores]
88
+ "tests/**/*.py" = ["S"]
89
+
90
+ [tool.ruff.format]
91
+ quote-style = "double"
92
+ indent-style = "space"
93
+ skip-magic-trailing-comma = false
94
+ line-ending = "auto"
95
+
96
+ [tool.pyright]
97
+ typeCheckingMode = "standard"
98
+ pythonVersion = "3.12"
99
+ include = ["src"]
100
+ exclude = [
101
+ "**/__pycache__",
102
+ "**/.*",
103
+ "**/node_modules",
104
+ ".venv",
105
+ ]
106
+ reportMissingImports = true
107
+ reportMissingTypeStubs = false
108
+ reportUnknownParameterType = false
109
+ reportUnknownVariableType = false
110
+ reportUnknownMemberType = false
111
+
112
+ [dependency-groups]
113
+ dev = [
114
+ "pyright>=1.1.411",
115
+ "pytest>=8.3.0",
116
+ "pytest-asyncio>=0.24.0",
117
+ "ruff>=0.16.5",
118
+ ]
@@ -0,0 +1,100 @@
1
+ [project]
2
+ name = "qqbotsdk-py"
3
+ version = "0.1.0"
4
+ description = "QQ 官方机器人 Python SDK(WebSocket / Webhook)"
5
+ license = "MIT"
6
+ license-files = ["LICENSE"]
7
+ readme = "README.md"
8
+ authors = [
9
+ { name = "snowy-yang", email = "102860110+snowy-yang@users.noreply.github.com" },
10
+ ]
11
+ keywords = ["qq", "qqbot", "bot", "sdk", "websocket", "webhook", "机器人"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "Natural Language :: Chinese (Simplified)",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ requires-python = ">=3.12"
23
+ dependencies = [
24
+ "aiohttp>=3.14.3",
25
+ "cryptography>=50.0.1",
26
+ "loguru>=0.7.3",
27
+ "python-dotenv>=1.2.3",
28
+ "ujson>=6.0.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/snowy-yang/qqbotsdk"
33
+ Repository = "https://github.com/snowy-yang/qqbotsdk"
34
+ Changelog = "https://github.com/snowy-yang/qqbotsdk/blob/main/CHANGELOG.md"
35
+
36
+ [project.scripts]
37
+ qqbotsdk = "qqbotsdk:main"
38
+
39
+ [build-system]
40
+ requires = ["uv_build>=0.12.7,<0.13.0"]
41
+ build-backend = "uv_build"
42
+
43
+ [tool.uv.build-backend]
44
+ module-name = "qqbotsdk"
45
+
46
+ [dependency-groups]
47
+ dev = ["pyright>=1.1.411", "pytest>=8.3.0", "pytest-asyncio>=0.24.0", "ruff>=0.16.5"]
48
+
49
+ [tool.pytest.ini_options]
50
+ asyncio_mode = "auto"
51
+ testpaths = ["tests"]
52
+
53
+
54
+ [tool.ruff]
55
+ target-version = "py312"
56
+ line-length = 88
57
+ indent-width = 4
58
+
59
+ [tool.ruff.lint]
60
+ select = [
61
+ "E", # pycodestyle errors
62
+ "F", # Pyflakes
63
+ "I", # isort
64
+ "N", # pep8-naming
65
+ "W", # pycodestyle warnings
66
+ "UP", # pyupgrade
67
+ "B", # flake8-bugbear
68
+ "C4", # flake8-comprehensions
69
+ "SIM", # flake8-simplify
70
+ ]
71
+ ignore = [
72
+ "E501", # line too long
73
+ "B008", # do not perform function calls in argument defaults
74
+ ]
75
+
76
+ [tool.ruff.lint.pydocstyle]
77
+ convention = "google"
78
+
79
+ [tool.ruff.lint.isort]
80
+ known-first-party = ["qqbotsdk"]
81
+
82
+ [tool.ruff.lint.per-file-ignores]
83
+ "tests/**/*.py" = ["S"]
84
+
85
+ [tool.ruff.format]
86
+ quote-style = "double"
87
+ indent-style = "space"
88
+ skip-magic-trailing-comma = false
89
+ line-ending = "auto"
90
+
91
+ [tool.pyright]
92
+ typeCheckingMode = "standard"
93
+ pythonVersion = "3.12"
94
+ include = ["src"]
95
+ exclude = ["**/__pycache__", "**/.*", "**/node_modules", ".venv"]
96
+ reportMissingImports = true
97
+ reportMissingTypeStubs = false
98
+ reportUnknownParameterType = false
99
+ reportUnknownVariableType = false
100
+ reportUnknownMemberType = false
@@ -0,0 +1,83 @@
1
+ """qqbotsdk:QQ 官方机器人 SDK 入口。
2
+
3
+ 用法:构造 `EventEmitter()` 注册 handler,交给 `main(ee)`
4
+ 启动;连接方式由 .env 的 CONNECTER 决定(websocket / webhook)。
5
+ 架构详情见 docs/ARCHITECTURE.md。
6
+ """
7
+
8
+ import asyncio
9
+
10
+ from aiohttp import ClientSession
11
+ from dotenv import load_dotenv
12
+ from loguru import logger
13
+
14
+ from .api import BotApi
15
+ from .config import Config
16
+ from .connecter import Connecter as Connecter
17
+ from .connecter import WebhookConnecter, WebsocketConnecter
18
+ from .emitter import EventEmitter as EventEmitter
19
+ from .protocol import BaseProtocol as BaseProtocol
20
+ from .queue import EventQueue as EventQueue
21
+ from .session import Session
22
+ from .token import AccessToken
23
+ from .webhook_protocol import WebhookProtocol
24
+ from .ws_protocol import WebsocketProtocol
25
+
26
+ load_dotenv()
27
+
28
+
29
+ async def run_loop(emitter: EventEmitter | None = None) -> None:
30
+ """启动 SDK。可传入外部构造的 EventEmitter 以注册 handler;不传时自建。
31
+
32
+ 全部组件在此显式装配,并按类型登记到 emitter.services 供 handler
33
+ 参数注入;共享的 HTTP 连接池随 run_loop 结束释放。
34
+ """
35
+ if emitter is None:
36
+ emitter = EventEmitter()
37
+
38
+ config = Config.load()
39
+ http = ClientSession(timeout=config.timeout)
40
+ session = Session()
41
+ token = AccessToken(config, http)
42
+ api = BotApi(config, http, token)
43
+ emitter.services.update(
44
+ {
45
+ EventEmitter: emitter,
46
+ EventQueue: emitter.queue,
47
+ Config: config,
48
+ Session: session,
49
+ ClientSession: http,
50
+ AccessToken: token,
51
+ BotApi: api,
52
+ }
53
+ )
54
+
55
+ # 各接入方式配自己的协议处理器:协议帧由适配器就近处理,只有业务事件
56
+ # (op=0)入队交给 emitter 分发(见 protocol.py / connecter.py)
57
+ match config.connecter:
58
+ case "websocket":
59
+ protocol = WebsocketProtocol(config, session, token)
60
+ connecter = WebsocketConnecter(
61
+ config, http, token, session, emitter.queue, protocol
62
+ )
63
+ case "webhook":
64
+ protocol = WebhookProtocol(config)
65
+ connecter = WebhookConnecter(config, emitter.queue, protocol)
66
+ case unknown:
67
+ raise ValueError(f"未知的 CONNECTER: {unknown}")
68
+
69
+ logger.info(f"qqbotsdk 启动,连接方式: {config.connecter}")
70
+ try:
71
+ await asyncio.gather(emitter.dispatch(), connecter.run())
72
+ finally:
73
+ # 在飞的后台 handler 先于连接池收尾,避免用到已关闭的 ClientSession
74
+ await emitter.close()
75
+ # 全 SDK 共享一个连接池,统一在此释放
76
+ await http.close()
77
+
78
+
79
+ def main(emitter: EventEmitter | None = None) -> None:
80
+ try:
81
+ asyncio.run(run_loop(emitter))
82
+ except KeyboardInterrupt:
83
+ logger.info("qqbotsdk 已退出")
@@ -0,0 +1,39 @@
1
+ """OpenAPI 客户端:BotApi 按域组合——v2 群/单聊、频道消息、频道管理、
2
+ 指令面板(斜线指令)与全局菜单、群管理、音频、论坛。
3
+
4
+ 各域模块是方法集(继承 BaseApi 共享请求内核:鉴权头注入、429 自动
5
+ 重试、401 刷新 token 重试、统一 ApiError),方法名与拆分前完全一致。
6
+ """
7
+
8
+ from .audio import AudioApi
9
+ from .channel import ChannelApi
10
+ from .core import ApiError
11
+ from .core import BaseApi as BaseApi
12
+ from .forum import ForumApi
13
+ from .group import GroupApi
14
+ from .guild import GuildApi
15
+ from .panel import PanelApi
16
+ from .v2 import V2Api, button, keyboard
17
+
18
+
19
+ class BotApi(V2Api, ChannelApi, GuildApi, PanelApi, GroupApi, AudioApi, ForumApi):
20
+ """通用 REST 封装;具体业务接口见各域方法集。
21
+
22
+ BaseApi(请求内核)经域方法集继承自然落在 MRO 末尾,
23
+ 构造签名 (config, http, token) 由它提供。
24
+ """
25
+
26
+
27
+ __all__ = [
28
+ "ApiError",
29
+ "AudioApi",
30
+ "BotApi",
31
+ "ChannelApi",
32
+ "ForumApi",
33
+ "GroupApi",
34
+ "GuildApi",
35
+ "PanelApi",
36
+ "V2Api",
37
+ "button",
38
+ "keyboard",
39
+ ]
@@ -0,0 +1,45 @@
1
+ """音频方法集(仅音频类机器人):子频道播放控制与上/下麦。
2
+
3
+ 需联系平台开通权限后调用;控制与上/下麦均触发 AUDIO_ACTION 事件。
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from .core import BaseApi
9
+
10
+ # 播放状态(AudioControl.status)
11
+ AUDIO_STATUS_START = 0
12
+ AUDIO_STATUS_PAUSE = 1
13
+ AUDIO_STATUS_RESUME = 2
14
+ AUDIO_STATUS_STOP = 3
15
+
16
+
17
+ class AudioApi(BaseApi):
18
+ """音频方法集;请求内核(鉴权/重试/错误)由 BaseApi 提供。"""
19
+
20
+ async def control_channel_audio(
21
+ self,
22
+ channel_id: str,
23
+ audio_url: str | None = None,
24
+ text: str | None = None,
25
+ status: int = AUDIO_STATUS_START,
26
+ ) -> dict:
27
+ """控制子频道音频播放(POST /channels/{channel_id}/audio)。
28
+
29
+ status 为 START/PAUSE/RESUME/STOP(AUDIO_STATUS_* 常量);
30
+ audio_url 与状态文本 text 仅 START 时传。
31
+ """
32
+ body: dict[str, Any] = {"status": status}
33
+ if audio_url is not None:
34
+ body["audio_url"] = audio_url
35
+ if text is not None:
36
+ body["text"] = text
37
+ return await self.post(f"/channels/{channel_id}/audio", json=body)
38
+
39
+ async def put_channel_mic(self, channel_id: str) -> dict:
40
+ """机器人上麦(PUT /channels/{channel_id}/mic)。"""
41
+ return await self.put(f"/channels/{channel_id}/mic")
42
+
43
+ async def delete_channel_mic(self, channel_id: str) -> dict:
44
+ """机器人下麦(DELETE /channels/{channel_id}/mic)。"""
45
+ return await self.delete(f"/channels/{channel_id}/mic")