Commit Graph

451 Commits

Author SHA1 Message Date
jxxghp
a59c9bb94e refactor(types): govern event dispatch callback types 2026-10-16 11:12:37 +08:00
jxxghp
2e897d9fe5 refactor(types): govern event handler binding contracts 2026-10-16 11:10:46 +08:00
jxxghp
e1b6d478e6 refactor(types): govern event registry and snapshot contracts 2026-10-16 11:08:28 +08:00
jxxghp
37a5787bf3 refactor(types): govern interpreter environment detection 2026-10-16 10:55:16 +08:00
jxxghp
65e86f8f3d refactor(types): govern torrent cache recognition result types 2026-10-16 10:54:03 +08:00
jxxghp
e67f602c0d refactor(types): govern IP address helpers with strict mypy 2026-10-16 10:52:52 +08:00
jxxghp
95e936f1b3 refactor(types): govern reflection helpers without changing runtime signatures 2026-10-16 10:51:22 +08:00
jxxghp
9d5ad08e08 refactor(types): govern agent display contract with strict mypy 2026-10-16 10:03:27 +08:00
jxxghp
226a13075c refactor(types): govern correlation middleware with strict mypy 2026-10-16 09:40:38 +08:00
jxxghp
5457d131b5 refactor(types): govern challenge detection with strict mypy 2026-10-16 09:37:46 +08:00
jxxghp
e59f1de518 refactor(types): govern URL query composition with strict mypy 2026-10-16 09:35:28 +08:00
jxxghp
70511cc4d2 refactor(types): govern token stream with strict mypy 2026-10-16 09:27:01 +08:00
jxxghp
0ae48fb281 refactor(types): govern media title parsing with strict mypy 2026-10-16 09:21:27 +08:00
jxxghp
4955b7b9ea fix: harden backend isolation and execution safety 2026-10-16 09:11:28 +08:00
InfinityPacer
9181ad2b65 feat(community): serve community medals from the Wiki registry behind remote switches
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:28:58 +08:00
jxxghp
63c8c0d71a fix(transfer): render aggregated success notifications with user template (#7007) 2026-10-15 13:10:43 +08:00
Opportunity
5e476e2419 fix(indexer): 修复 Discuz 用户分享率解析 (#7006) 2026-10-15 13:01:57 +08:00
jxxghp
ea60ed0fd6 feat(community): recognize GitHub contributions and honor levels 2026-10-15 12:11:36 +08:00
jxxghp
91b70ee2ce fix(wechatclawbot): 识别 item_list 媒体入站消息并修正 CDN 探测判定 (#7000)
* fix(wechatclawbot): 识别 item_list 媒体入站消息并修正 CDN 探测判定 (#6992)

iLink 下行媒体报文把附件放在 item_list 子项中,原解析存在三处缺口,
图片/语音/文件入站消息会被判为空消息丢弃:

- item_list 仅按数字 type 处理 1 文本/2 图片/5、6 文件,缺少 3 语音/
  4 文件/5 视频分派;改为按实际出现的子项键名分派,同时兼容历史
  type=5/6 文件报文。
- 附件引用只读取顶层 url/aeskey,识别不了 image_item.media.full_url /
  media.aes_key 等嵌套字段;统一按“顶层优先、media 子对象兜底”提取。
- 消息被解析为空时直接返回 None,日志只剩“未识别到有效消息”;补充
  渠道级诊断日志,输出 message_id 与截断后的原始报文。

另外把“微信 ClawBot 媒体 CDN”网络探测的 400 纳入成功状态码:该 CDN
对无参数裸请求固定返回 400,此前探测项恒定误报失败。

tests/test_wechatclawbot.py 整文件迁移为 pytest 原生,并新增 item_list
媒体解析、仅图片消息可入链、空消息诊断日志回归;nettest 契约测试补充
CDN 400 探测用例。复杂度与 mypy 低水位随债务下降同步固化。

* fix(wechatclawbot): 同步架构清单中的 mypy 债务数字 (#6992)
2026-10-15 09:02:59 +08:00
@huluohu
76a4e33e44 fix(telegram): Rich Message 失败时保留原始错误并降级纯文本 (#6993)
* fix(telegram): Rich Message 失败时保留原始错误并降级纯文本

Telegram Rich Message 发送失败时存在三个叠加问题:

1. `__send_rich_message` 用固定文本 `RetryException("发送 Telegram Rich
   Message 失败")` 包装异常,`str(e)` 里没有 Telegram 返回的 description,
   日志只剩“发送失败”四个字,无法判断是内容非法、方法不存在还是格式问题。

2. 内容非法这类确定性失败(400)同样走满3 次退避重试(3s、6s),
   期间同一条坏消息反复阻塞,且最终报错仍无有效信息。

3. 失败即丢失通知:Rich Message 发不出去时没有任何降级路径,
   整条通知直接消失。

改动:

- 异常文本改为 f"...失败:{err}",保留 Telegram 原始 description 与错误码。
- 新增 `NonRetryableTelegramError`(继承 `ImmediateException`)与
  `_is_non_retryable_api_error`,400 立即抛出以跳过 retry 退避;
  5xx 与网络异常保持原有重试策略。
- `send_msg` / `edit_msg` 增加纯文本降级出口,Rich Message 失败时
  用不带格式解析的纯文本重发,保证通知内容不丢失。
- 降级路径显式传 `parse_mode=""`:`TelegramBotApi._parse_mode` 在参数为
  `None` 时会回落到客户端默认的 MarkdownV2,那正是 Rich Message 已经
  失败过的解析路径,漏传会让降级重演同一个错误。

新增 tests/test_telegram_rich_message_fallback.py(14 项)覆盖错误信息保留、
400 免重试、5xx 仍重试、发送与编辑两条降级路径,以及 parse_mode 回落陷阱的
回归防护。

* fix(telegram): 修正 Rich Message 降级路径的 mypy 与架构基线

上一提交引入的两处门禁失败:

1. `__send_plain_fallback` 用 `**self._topic_kwargs(chat_id)` 展开参数,mypy
   把 `**` 匹配到 `send_message` 第 5 个形参 `disable_web_page_preview`
   (Optional[bool]),判定 `dict[str, int]` 不兼容,arg-type 由 1 增至 2。
   改为显式传 `message_thread_id`,`_topic_kwargs` 只可能产出该键。

2. 新增 `app.schemas.exception` 导入带来 2 条依赖边,宿主基线由 9542 变为
   9544。已用 `baseline.py --write-host` 固化,其余 3 个基线文件字节不变。

   连带影响:docs/architecture-overview.md 与 docs/refactor/
   optimization-checklist.md 硬编码了边数,`test_architecture_documents_
   match_generated_quality_metrics` 断言两处必须与基线一致,同步为 9,544。

门禁与测试验证:

- scripts/architecture/mypy_ratchet.py          exit 0(9163 个类型错误)
- scripts/architecture/baseline.py --check-host exit 0
- pytest(fallback + contract + telegram)      75 passed

---------

Co-authored-by: jxxghp <jxxghp@gmail.com>
2026-10-15 07:32:16 +08:00
jxxghp
72c8bff312 feat(music): preserve local covers and fall back after online scraping fails 2026-10-14 23:15:41 +08:00
jxxghp
13a58be2a6 feat(notifications): render structured results by channel capabilities 2026-10-14 08:05:41 +08:00
jxxghp
b82bbcb420 fix(webhook)!: guard unknown movie paths in transfer history
Recover Jellyfin deletion paths from webhook payloads or bounded server-scoped caches. Avoid querying deleted items and preserve TV season/series lookup behavior.

BREAKING CHANGE: Movie and unspecified-type history queries without a destination now return no records unless match_all_versions=True is explicitly requested.

Refs #6988
2026-10-13 23:48:10 +08:00
jxxghp
48cffca3a1 feat(transfer): aggregate successful notifications by batch (#6981) 2026-10-13 17:21:17 +08:00
jxxghp
98bd038623 fix(music): reconcile album completion and prevent duplicate downloads
Recover complete release coverage from successful local transfers and audio tags/CUE before subscription searches. Keep release-group and album boundaries, and reject completed torrent hashes before downloader submission.
2026-10-13 17:05:57 +08:00
jxxghp
86c7fccfd5 feat(subscribe): persist business activity with scoped details 2026-10-12 23:43:01 +08:00
jxxghp
99ef7e508b fix(indexer): support custom seeding and Unit3D bonus pages 2026-10-12 08:36:19 +08:00
jxxghp
bd1e6eb899 fix(agent): separate public API queries from bound user operations 2026-10-12 07:30:35 +08:00
jxxghp
7a853577d8 fix(subscribe): preserve original execution log content 2026-10-12 06:58:01 +08:00
jxxghp
391f55df40 feat(subscribe): add isolated execution logs with authorized streaming (#6963) 2026-10-11 22:18:37 +08:00
InfinityPacer
b174d3caf8 fix(testing): register runtime configuration in shared plugin bootstrap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:56:48 +08:00
jxxghp
01301b887a fix(plugin): accept underscore IDs in instance validation (#6962) 2026-10-11 13:59:38 +08:00
jxxghp
e8d63f215c docs(architecture): sync workflow type debt low watermark 2026-10-11 07:45:25 +08:00
InfinityPacer
25aaf3a32b feat(plugin): add agent pet extension contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:08:15 +08:00
jxxghp
dd90883c5d fix(torrent): respect tracker rate limits and quota cooldowns
Recognize explicit JSON rate limits before torrent parsing and short retries. Preserve per-call quota errors through download preparation so subscription cooldowns cover the tracker window.

Refs #6950
2026-10-10 23:34:35 +08:00
InfinityPacer
3faa49fd7a fix(workflow): keep torrent contexts usable across persistence and filtering
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:44:19 +08:00
jxxghp
2b415cc1b9 fix(cli): unwrap MCP REST tool responses 2026-10-10 17:00:56 +08:00
jxxghp
b663825fe7 fix(plugin): recover dependencies without disabled constraints
Keep enabled host and clone sources in dependency recovery. Fall back to per-plugin requests while retaining ready and recovered manifest constraints, preserving original sources and cancellation ownership.

Refs #6943
2026-10-10 12:20:13 +08:00
jxxghp
d7db4cfc0b fix(plugin): serialize package swaps with imports (#6939) 2026-10-10 07:42:37 +08:00
jxxghp
9924e5e7ff fix(workflow): satisfy architecture gates for issue 6931 2026-10-09 23:58:33 +08:00
freeman
f3ad81b39e style: apply safe ruff auto-fixes and exempt startup late imports
Run `ruff check app tests scripts --fix` with the configured rule set
(E4/E7/E9/F/I): import sorting (I001), f-strings without placeholders
(F541), lambda/variable/unused-import cleanups that ruff marks safe.
Nine files whose only ruff debt sits next to pre-existing pylint
import-resolution errors (lazy exports, sys.path script imports) or
whose "unused" imports are reached through attribute access by tests are
left untouched so the changed-file pylint gate and the benchmark tests
stay green; their diagnostics remain in the baseline.

app/startup/lifecycle/__init__.py and app/main.py import business
modules after cache composition, import-path and stdio setup on purpose
(the source comments document the contract), so declare E402
per-file-ignores for them instead of adding per-line noqa.

Ruff ratchet watermark drops from 498 to 100; sync the baseline fixture
and the optimization checklist figure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 13:29:09 +08:00
jxxghp
1c1db41222 Merge pull request #6926 from cwd0204/test/optional-downloader-stubs 2026-10-09 13:05:13 +08:00
freeman
2165475dd0 fix(security): throttle password login after repeated failures
The access-token endpoint accepted unlimited password attempts; the
only existing rate limiters are single-instance helpers for outbound
site requests. Add app/application/security/throttle.py, an in-memory
LoginThrottle keyed by (client address, normalized username) that locks
for 30s after five consecutive failures and doubles the lockout up to
15 minutes. The endpoint answers 429 with Retry-After while locked,
counts only real credential failures (an MFA challenge after a correct
password does not count), and clears the record on success.

X-Forwarded-For is deliberately not trusted because there is no
trusted-proxy setting; behind the bundled nginx all requests share one
address, so the key degrades to per-username throttling, which is the
dimension brute-force protection needs. Sync the dependency snapshot
and architecture metrics for the new module.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 12:01:04 +08:00
freeman
88641d4a7a test: stub optional downloader modules only when they are missing
test_chain_rate_limit.py and test_system_notification_dispatch.py
installed a bare qbittorrentapi/transmission_rpc/psutil ModuleType into
sys.modules at import time with sys.modules.setdefault. When either file
is collected before a test that imports the real package in the same
shard (for example tests/test_downloader_add_failure_message.py and its
TorrentDictionary import), the placeholder wins and collection fails
with "cannot import name ... (unknown location)". Any change to the test
file set reshuffles the six CI shards and can surface this.

Use app.testing.bootstrap.ensure_optional_stub, which keeps the real
module when it is installed, and convert both unittest.TestCase files to
pytest-native functions as the testing guide requires when touching a
TestCase file. Record the resulting lower Ruff watermark.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 11:58:51 +08:00
jxxghp
35d5cac84f fix(subscribe): validate TMDB conversion and sync architecture facts 2026-10-08 21:22:17 +08:00
jxxghp
70811b551e feat(music): optionally simplify written lyrics 2026-10-08 18:40:16 +08:00
jxxghp
6758633e01 docs(architecture): sync metrics for Milkie adapter 2026-10-08 17:56:07 +08:00
jxxghp
32d37fa8b4 feat(music): preserve per-task CUE recognition policy 2026-10-08 08:51:01 +08:00
ChantXu64
e5e2558585 feat(search): 订阅补全逐页搜索、断点续搜与资源页手动分页 (#6907) 2026-10-07 14:40:55 +08:00
jxxghp
6c9ebd01f3 fix(agent): close owned LLM clients with their lifecycle
Refs #6901
2026-10-07 09:18:40 +08:00