mirror of
https://github.com/snowy-yang/qqbotsdk.git
synced 2026-10-21 12:56:17 +00:00
以「语义可读、逻辑清晰、减少心智负担」为目标,不追求极致速度:
- **移除 pyee 依赖**:EventEmitter 改为内置的 `事件名 → handler 列表` 注册表
(on() 追加、emit() 顺序遍历)。SDK 用不到 pyee 的 once/new_listener/错误
事件机制,去掉后少一个依赖、分发语义一眼可读。注册语义不变(同函数重复
注册只生效一次),并补测试锁定「重复注册只生效一次」「按注册顺序执行」。
破坏性:once/remove_listener 等装饰性 API 不再提供。
- **去掉热路径上的重复计算**(纯缓存,不改语义):
· payloads 按类缓存解析计划(字段名 + 是否嵌套 Author),不再每次事件重跑
dataclasses.fields 与联合类型解包;未收录字段照旧忽略(新增回归测试,
线上 payload 会带 author.union_openid 这类字段)。实测 7.5µs → 2.5µs。
· crypto 按 AppSecret 缓存派生私钥(每个 webhook 请求都要验签,派生一次
约 40µs);公钥派生只差 3µs,不再多缓存一层。
- **命名与接口一致性**:
· WebsocketConnecter.reply_helper → send_helper(它从出站缓冲取帧发送,
原名会让人误以为还有 reply 队列;model.py 的对应注释同步)
· Connecter 由 typing.Protocol 改为 abc.ABC,与 BaseProtocol 风格统一
- **webhook 去重改摊还清理**:过期判定按当前 id 做(O(1),且 TTL 外重发的
同一 id 仍按新事件处理——原先的写法在阈值清理下会误判为重复,已由测试
抓出并修正),整表清过期项只在表长到阈值时执行,不再每请求 O(n) 扫描。
测试 85 全绿(新增未知字段/解析计划缓存/注册语义/去重摊还与 TTL 过期),
ruff check/format + pyright 干净。文档 ARCHITECTURE/API/CHANGELOG 同步。
82 lines
1.8 KiB
TOML
82 lines
1.8 KiB
TOML
[project]
|
||
name = "qqbotsdk"
|
||
version = "0.1.0"
|
||
description = "QQ 官方机器人 Python SDK(WebSocket / Webhook)"
|
||
license = "MIT"
|
||
license-files = ["LICENSE"]
|
||
readme = "README.md"
|
||
authors = [
|
||
{ name = "snowy-yang", email = "102860110+snowy-yang@users.noreply.github.com" },
|
||
]
|
||
requires-python = ">=3.13"
|
||
dependencies = [
|
||
"aiohttp>=3.14.3",
|
||
"cryptography>=50.0.1",
|
||
"loguru>=0.7.3",
|
||
"python-dotenv>=1.2.3",
|
||
"ujson>=6.0.0",
|
||
]
|
||
|
||
[project.scripts]
|
||
qqbotsdk = "qqbotsdk:main"
|
||
|
||
[build-system]
|
||
requires = ["uv_build>=0.12.7,<0.13.0"]
|
||
build-backend = "uv_build"
|
||
|
||
[dependency-groups]
|
||
dev = ["pyright>=1.1.411", "pytest>=8.3.0", "pytest-asyncio>=0.24.0", "ruff>=0.16.5"]
|
||
|
||
[tool.pytest.ini_options]
|
||
asyncio_mode = "auto"
|
||
testpaths = ["tests"]
|
||
|
||
|
||
[tool.ruff]
|
||
target-version = "py313"
|
||
line-length = 88
|
||
indent-width = 4
|
||
|
||
[tool.ruff.lint]
|
||
select = [
|
||
"E", # pycodestyle errors
|
||
"F", # Pyflakes
|
||
"I", # isort
|
||
"N", # pep8-naming
|
||
"W", # pycodestyle warnings
|
||
"UP", # pyupgrade
|
||
"B", # flake8-bugbear
|
||
"C4", # flake8-comprehensions
|
||
"SIM", # flake8-simplify
|
||
]
|
||
ignore = [
|
||
"E501", # line too long
|
||
"B008", # do not perform function calls in argument defaults
|
||
]
|
||
|
||
[tool.ruff.lint.pydocstyle]
|
||
convention = "google"
|
||
|
||
[tool.ruff.lint.isort]
|
||
known-first-party = ["qqbotsdk"]
|
||
|
||
[tool.ruff.lint.per-file-ignores]
|
||
"tests/**/*.py" = ["S"]
|
||
|
||
[tool.ruff.format]
|
||
quote-style = "double"
|
||
indent-style = "space"
|
||
skip-magic-trailing-comma = false
|
||
line-ending = "auto"
|
||
|
||
[tool.pyright]
|
||
typeCheckingMode = "standard"
|
||
pythonVersion = "3.13"
|
||
include = ["src"]
|
||
exclude = ["**/__pycache__", "**/.*", "**/node_modules", ".venv"]
|
||
reportMissingImports = true
|
||
reportMissingTypeStubs = false
|
||
reportUnknownParameterType = false
|
||
reportUnknownVariableType = false
|
||
reportUnknownMemberType = false
|