fix: 多群订阅同源查重 (#70) + 字体加载/配置/文档清理#75
Conversation
启用表格转图时,字体下载在 bootstrap 中由同步 await 改为后台 asyncio.create_task 预取,首次表格渲染前再按需等待就绪(未配置下载的 环境直接回退纯文本,不发起网络请求)。同时补全缺失的表格转图总开关 EntryTextFormatter.configure_table_to_image,修复其原本不存在却被 bootstrap 调用导致的启动 AttributeError。
命令处理器参数均带默认值(args: GreedyStr = ""),框架解析时只读默认值 不读注解,greedy 合并分支恒不触发——该注解功能上是惰性的,等价于 str。 故将注解统一改为 str,文件上传监听改用框架原生 filter.event_message_type(filter.EventMessageType.ALL),删除整个 astrbot_compat 兼容层并同步重写相关回归测试。
将 telegraph_proxy 从顶层 media 配置移入 telegram_strategy 模板,与 enable_telegraph/telegraph_token 同源。发送时由 TelegramSender 从策略 解析并传入 TelegraphClient(留空即直连,不再继承通用 HTTP 代理),修复 此前 _get_telegraph_proxy 定义却从未被调用的死配置问题,并移除相关死链路。 启动配置自愈新增迁移:旧 media.telegraph_proxy 会自动写入首个 telegram 策略模板项。
新增/完善 docs/usage 下的命令、配置、AI 工具、Plugin Pages 与兼容性文档; commands.md 补 sub_stop 的 rss_stop 别名并修正 rsshelp 别名,configuration.md 随 telegraph_proxy 归位 Telegram 策略同步调整。CHANGELOG 2.0.3 由 22 条精简为 面向用户的高层要点。一并清理已内置字体后多余的 assets/fonts/OFL.txt,并在 metadata.yaml 声明最低 AstrBot 版本 >=4.24.0。
messaging 包通过 __getattr__ 懒加载所有导出,导致 __all__ 中 31 个名字在 类型检查器看来「未定义」(reportUnsupportedDunderAll)。新增 if TYPE_CHECKING 块真实导入这些符号:类型检查时静态可见、告警清零,运行时该块不执行,原有 懒加载行为完全保留。
订阅查重原先用 (user_id, feed_id) 忽略 target_session,导致同一用户在不同 群聊订阅同一 Feed 被误判为「您已经订阅了此源」。将仓储方法 get_by_user_and_feed 收紧为 get_by_user_feed_session(user_id, feed_id, target_session),/sub 与导入两处查重均带上会话;同时避免多会话行存在时 scalar_one_or_none 误抛。新增回归测试覆盖「不同会话各自成功、相同会话仍拒绝」。
将此前压缩成扁平要点的 2.0.3 条目还原为 Keep a Changelog 分节格式,并把 「媒体缓存 GC / 完整性阈值 / 平台降级策略不再作为用户配置项暴露」从 Changed 提取为独立的 Removed 段。
There was a problem hiding this comment.
Sorry @FlanChanXwO, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
for more information, see https://pre-commit.ci
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/infrastructure/persistence/subscription_repository_impl.py (1)
44-61:⚠️ Potential issue | 🟠 Major | ⚡ Quick win为避免重复数据导致 MultipleResultsFound:给
(user_id, feed_id, target_session)加唯一约束或改为取第一条防御
get_by_user_feed_session()里用result.scalar_one_or_none();但src/infrastructure/persistence/models.py的SubORM未声明该三列唯一约束/唯一索引,src/infrastructure/persistence/migrations/V1_init.py创建rsshub_sub也没有UNIQUE/CREATE UNIQUE/unique index,因此一旦历史数据或并发写入产生重复行仍可能再次触发MultipleResultsFound(与#70同源)。建议:要么在数据库层补上(user_id, feed_id, target_session)唯一约束(并清理历史重复);要么在查询上加limit(1)/ 改用scalars().first(),确保重复数据也不会抛异常。 [src/infrastructure/persistence/subscription_repository_impl.py 44-61]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/infrastructure/persistence/subscription_repository_impl.py` around lines 44 - 61, The get_by_user_feed_session method uses result.scalar_one_or_none() but SubORM lacks a DB-level unique constraint on (user_id, feed_id, target_session), so duplicate rows can raise MultipleResultsFound; fix by either adding a unique constraint/index for those three columns in the ORM model (SubORM) and the migration (V1_init.py) and clean duplicates, or make the query defensive by applying a limit and using scalars().first() (i.e., change the select in get_by_user_feed_session to include .limit(1) or call result.scalars().first() instead of scalar_one_or_none) to avoid exceptions if duplicates exist.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/infrastructure/rendering/font_manager.py`:
- Around line 76-87: ensure_table_font_runtime currently re-runs expensive
synchronous font verification (_verify_font) on each call, which can block the
event loop when invoked repeatedly (e.g., per-table in html_parser); add a
module-level cache (e.g. _cached_verified_font: Path | None) and an asyncio.Lock
to make checking/setting atomic, then update ensure_table_font_runtime to first
return the cached path if present, otherwise run _verify_font once, store the
verified path in _cached_verified_font, and return it; also ensure any
install/download path (ensure_table_font) clears or updates
_cached_verified_font after a successful download so subsequent calls use the
cache; reference functions: ensure_table_font_runtime, _verify_font,
ensure_table_font, get_runtime_font_path.
---
Outside diff comments:
In `@src/infrastructure/persistence/subscription_repository_impl.py`:
- Around line 44-61: The get_by_user_feed_session method uses
result.scalar_one_or_none() but SubORM lacks a DB-level unique constraint on
(user_id, feed_id, target_session), so duplicate rows can raise
MultipleResultsFound; fix by either adding a unique constraint/index for those
three columns in the ORM model (SubORM) and the migration (V1_init.py) and clean
duplicates, or make the query defensive by applying a limit and using
scalars().first() (i.e., change the select in get_by_user_feed_session to
include .limit(1) or call result.scalars().first() instead of
scalar_one_or_none) to avoid exceptions if duplicates exist.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 638f6822-b6ce-4ebb-931a-e846db74828a
📒 Files selected for processing (35)
CHANGELOG.mdREADME.md_conf_schema.jsonassets/fonts/OFL.txtbootstrap.pydocs/README.mddocs/usage/README.mddocs/usage/ai-tools.mddocs/usage/commands.mddocs/usage/compatibility.mddocs/usage/configuration.mddocs/usage/plugin-pages.mdmain.pymetadata.yamlsrc/application/commands/import_subscriptions_cmd.pysrc/application/commands/subscribe_feed_cmd.pysrc/application/services/html_parser.pysrc/domain/repositories/subscription_repository.pysrc/infrastructure/config/legacy_migration.pysrc/infrastructure/config/models/runtime_settings.pysrc/infrastructure/config/models/sender_strategy_models.pysrc/infrastructure/config/settings_builder.pysrc/infrastructure/messaging/__init__.pysrc/infrastructure/messaging/senders/base_sender.pysrc/infrastructure/messaging/senders/telegram_sender.pysrc/infrastructure/persistence/subscription_repository_impl.pysrc/infrastructure/pipeline/entry_formatter.pysrc/infrastructure/rendering/font_manager.pysrc/interfaces/astrbot_compat.pytests/unit/application/test_commands.pytests/unit/application/test_import_export.pytests/unit/application/test_settings.pytests/unit/infrastructure/test_platform_sequence_senders.pytests/unit/test_command_handlers_regression.pytests/unit/test_conf_schema.py
💤 Files with no reviewable changes (3)
- assets/fonts/OFL.txt
- src/interfaces/astrbot_compat.py
- src/infrastructure/messaging/senders/base_sender.py
- maintenance.md 新增「仓库体积」章节:插件仓库总大小严格不得超过 16 MB, 大体积资源应按需下载而非纳入仓库。 - engineering-principles.md 代码组织新增:每个函数/方法必须有确切的中文 docstring,并与实现保持一致。
- font_manager: ensure_table_font_runtime 引入模块级缓存 _cached_verified_font + _verify_lock,首次校验后命中缓存,避免按 <table> 反复跑 15MB 全量 SHA256 阻塞事件循环;ensure_table_font 在校验/下载成功后同步更新缓存。 - subscription_repository_impl: get_by_user_feed_session 改用 limit(1) + scalars().first(),(user_id, feed_id, target_session) 无唯一约束时若存在 重复行不再抛 MultipleResultsFound。 - 补 font_manager 缓存回归测试与缓存重置 fixture。
* fix(media): harden predownload and config handling (#62) * fix(media): harden predownload and config handling * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * docs: refresh help image and project docs (#66) * docs: refresh help image and project docs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: update pull request template * ci: remove unused knowledgebase script --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix(media): improve media detection and platform sending (#64) * fix(media): improve media detection and platform sending * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(media): address platform review feedback * fix(media): address review feedback * refactor(media): add table rendering pipeline --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * feat(pages): add push retry and data self-healing (#65) * feat(pages): add push retry and data self-healing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(pages): address web api review feedback * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor(pages): split dashboard modules --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix(pages): improve multi-select filters (#68) * fix(pages): improve multi-select filters * fix(pages): address filter review comments * fix(telegram): proxy telegraph page creation (#67) * fix(kb): reconcile route knowledge manifest (#69) * fix(kb): reconcile route knowledge manifest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(kb): address manifest review feedback --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix(db,onebot): drop legacy link_preview column and add napcat stream upload (#74) * fix(db): drop legacy link_preview column and add napcat stream upload 修复 Issue #58:旧库残留的 link_preview NOT NULL 列导致 /sub 创建订阅失败。 新增 V2 迁移在启动时安全删除该列(幂等)。 同时把 OneBot 的 prefer_local_video 配置替换为 napcat_stream_mode (disabled/fallback/always):媒体已统一预下载,发送统一使用本地文件; 新增 NapCat 流式上传模块,支持发送前预上传或失败后流式重试,避免大文件 OOM。 主动推送场景通过 platform_manager 解析 bot 客户端,旧 prefer_local_video 配置在启动自愈时迁移为对应 napcat_stream_mode。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(config): replace stale FFmpegSettings export with MediaRuntimeSettings FFmpegSettings was renamed to MediaRuntimeSettings in the napcat_stream refactor but the __init__.py re-exports in config/ and models/ still referenced the old name, causing ImportError at runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * feat: 内置 Noto Sans SC 字体并移除硬编码系统字体路径 (#71) * feat: bundle Noto Sans SC font and remove hardcoded system font paths Ship NotoSansSC-subset.otf (~14 MB) in assets/fonts/ so the table image renderer works out-of-the-box with CJK text on any OS. Remove the six hardcoded absolute system font paths from _iter_font_candidates() to eliminate platform-dependent font probing. - Add assets/fonts/NotoSansSC-subset.otf (Noto Sans SC Subset Variable OTF) - Add assets/fonts/OFL.txt (SIL Open Font License 1.1) - Remove hardcoded macOS/Linux system font paths from table_image_renderer.py - Update font type annotations to support FreeTypeFont | ImageFont union - Add tests for bundled font discovery and system path verification - Update docs/project/formatting.md to reflect bundled font Font search priority: env vars > assets/fonts/ > Pillow default fallback * feat: runtime font download instead of bundled CJK font Replace the 14 MB bundled NotoSansSC-subset.otf with runtime download from jsDelivr CDN. Font is downloaded at plugin startup, verified with SHA256 + size checks, and atomically written to persistent data dir. When no CJK font is available (download failed or offline), tables fall back to plain text (A | B | C) instead of using Pillow default font, preventing broken CJK rendering. - Add font_manager.py with async download, double-check lock, atomic write - Remove ImageFont.load_default() fallback from table_image_renderer - Add _warned_no_font + early return None when no font - Add get_runtime_font_dir() to font candidate priority list - Wire await ensure_table_font() in bootstrap startup flow - Add 8 font_manager tests + update table_renderer tests - Fix conftest.py namespace package conflict across feature branches - Update formatting.md docs Closes #71 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix: 多群订阅同源查重 (#70) + 字体加载/配置/文档清理 (#75) * perf(rendering): 字体改为后台预取+按需加载,不再阻塞插件启动 启用表格转图时,字体下载在 bootstrap 中由同步 await 改为后台 asyncio.create_task 预取,首次表格渲染前再按需等待就绪(未配置下载的 环境直接回退纯文本,不发起网络请求)。同时补全缺失的表格转图总开关 EntryTextFormatter.configure_table_to_image,修复其原本不存在却被 bootstrap 调用导致的启动 AttributeError。 * refactor(interfaces): 移除无效的 GreedyStr 兼容层 astrbot_compat 命令处理器参数均带默认值(args: GreedyStr = ""),框架解析时只读默认值 不读注解,greedy 合并分支恒不触发——该注解功能上是惰性的,等价于 str。 故将注解统一改为 str,文件上传监听改用框架原生 filter.event_message_type(filter.EventMessageType.ALL),删除整个 astrbot_compat 兼容层并同步重写相关回归测试。 * refactor(config): telegraph_proxy 归位 Telegram 策略并接通 将 telegraph_proxy 从顶层 media 配置移入 telegram_strategy 模板,与 enable_telegraph/telegraph_token 同源。发送时由 TelegramSender 从策略 解析并传入 TelegraphClient(留空即直连,不再继承通用 HTTP 代理),修复 此前 _get_telegraph_proxy 定义却从未被调用的死配置问题,并移除相关死链路。 启动配置自愈新增迁移:旧 media.telegraph_proxy 会自动写入首个 telegram 策略模板项。 * docs: 整理 usage 文档与 README,精简 CHANGELOG 2.0.3 新增/完善 docs/usage 下的命令、配置、AI 工具、Plugin Pages 与兼容性文档; commands.md 补 sub_stop 的 rss_stop 别名并修正 rsshelp 别名,configuration.md 随 telegraph_proxy 归位 Telegram 策略同步调整。CHANGELOG 2.0.3 由 22 条精简为 面向用户的高层要点。一并清理已内置字体后多余的 assets/fonts/OFL.txt,并在 metadata.yaml 声明最低 AstrBot 版本 >=4.24.0。 * fix(messaging): 用 TYPE_CHECKING 块消除 __all__ 懒加载告警 messaging 包通过 __getattr__ 懒加载所有导出,导致 __all__ 中 31 个名字在 类型检查器看来「未定义」(reportUnsupportedDunderAll)。新增 if TYPE_CHECKING 块真实导入这些符号:类型检查时静态可见、告警清零,运行时该块不执行,原有 懒加载行为完全保留。 * fix(subscription): 查重按会话隔离,允许多群订阅同一源 (#70) 订阅查重原先用 (user_id, feed_id) 忽略 target_session,导致同一用户在不同 群聊订阅同一 Feed 被误判为「您已经订阅了此源」。将仓储方法 get_by_user_and_feed 收紧为 get_by_user_feed_session(user_id, feed_id, target_session),/sub 与导入两处查重均带上会话;同时避免多会话行存在时 scalar_one_or_none 误抛。新增回归测试覆盖「不同会话各自成功、相同会话仍拒绝」。 * docs(changelog): 2.0.3 恢复 Added/Changed/Removed/Fixed 分节结构 将此前压缩成扁平要点的 2.0.3 条目还原为 Keep a Changelog 分节格式,并把 「媒体缓存 GC / 完整性阈值 / 平台降级策略不再作为用户配置项暴露」从 Changed 提取为独立的 Removed 段。 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs(dev): 新增仓库体积上限与函数中文 docstring 规范 - maintenance.md 新增「仓库体积」章节:插件仓库总大小严格不得超过 16 MB, 大体积资源应按需下载而非纳入仓库。 - engineering-principles.md 代码组织新增:每个函数/方法必须有确切的中文 docstring,并与实现保持一致。 * fix: 缓存字体校验结果并防御订阅查重重复行 - font_manager: ensure_table_font_runtime 引入模块级缓存 _cached_verified_font + _verify_lock,首次校验后命中缓存,避免按 <table> 反复跑 15MB 全量 SHA256 阻塞事件循环;ensure_table_font 在校验/下载成功后同步更新缓存。 - subscription_repository_impl: get_by_user_feed_session 改用 limit(1) + scalars().first(),(user_id, feed_id, target_session) 无唯一约束时若存在 重复行不再抛 MultipleResultsFound。 - 补 font_manager 缓存回归测试与缓存重置 fixture。 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
本次合并修复了 issue #70(同一用户无法在不同群聊订阅同一 RSS 源),并附带若干启动性能优化、死配置接通与代码/文档清理。
Modifications / 改动点
核心改动按功能拆分为多个提交:
fix(subscription) — issue Bug: /sub功能异常 #70:订阅查重原先用
(user_id, feed_id)忽略target_session,导致同一用户在不同群聊订阅同一 Feed 被误判为「您已经订阅了此源」。将仓储方法get_by_user_and_feed收紧为get_by_user_feed_session(user_id, feed_id, target_session),/sub与导入两处查重均带上会话,并避免多会话行存在时scalar_one_or_none误抛。src/domain/repositories/subscription_repository.py、src/infrastructure/persistence/subscription_repository_impl.py、src/application/commands/subscribe_feed_cmd.py、src/application/commands/import_subscriptions_cmd.pyperf(rendering):字体下载从 bootstrap 同步
await改为后台预取 + 首次渲染按需等待(仅在table_to_image开启时预取),不再阻塞插件加载;顺带修复EntryTextFormatter.configure_table_to_image不存在却被调用导致的启动AttributeError。refactor(interfaces):移除无效的
GreedyStr兼容层astrbot_compat(带默认值时框架只读默认值、greedy 分支恒不触发,注解等价于str),上传监听改用框架原生filter.event_message_type。refactor(config):
telegraph_proxy从顶层media归位到telegram_strategy模板并真正接通(此前_get_telegraph_proxy定义却从未被调用),留空即直连不继承通用代理;启动配置自愈新增旧键迁移。fix(messaging):
messaging/__init__.py用TYPE_CHECKING块消除 31 个__all__懒加载告警(运行时懒加载行为不变)。docs:整理
docs/usage/命令/配置文档,CHANGELOG 2.0.3 恢复 Added/Changed/Removed/Fixed 分节结构。This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
新增回归测试
test_subscribe_same_feed_allowed_in_different_sessions:验证同一用户同一源在不同会话各自订阅成功、相同会话仍按查重拒绝。Checklist / 检查清单
Summary by CodeRabbit
发布说明
文档
新增功能
配置调整
改进