本文档面向插件开发者,介绍项目架构、开发环境搭建、测试方法和代码规范。
astrbot_suwayomi_server/
├── main.py # 插件入口,薄调度层——所有业务逻辑委托给子模块
├── metadata.yaml # AstrBot 插件元数据
├── _conf_schema.json # AstrBot 配置 schema(WebUI 自动生成配置表单)
├── requirements.txt # Python 运行时依赖
├── suwayomi/
│ ├── __init__.py # PLUGIN_NAME 常量
│ ├── client.py # Suwayomi GraphQL 异步 HTTP 客户端
│ ├── config.py # 分组配置读写、旧版平铺配置迁移(get/set/flatten/migrate)
│ ├── models.py # 数据模型定义
│ ├── service.py # 业务逻辑层(漫画/章节解析、缓存策略、格式化)
│ ├── cards.py # 指令结果卡片(T2I 模板、数据准备、简介清洗、封面嵌入、渲染缓存)
│ ├── ai_service.py # Agent 结构化搜索、章节查询与订阅管理(无发送副作用)
│ ├── ai_tools.py # AstrBot FunctionTool Schema 与注册工厂
│ └── updater.py # 更新引擎(check_updates + run_update_loop)
├── utils/
│ ├── __init__.py
│ ├── downloader.py # 图片下载管道(download_one/download_images/download_cover/fetch_pages_local)
│ ├── pack.py # 图片打包工具(ZIP/CBZ/PDF)
│ ├── pusher.py # 推送投递(push_chapter_images/push_chapter_file)+ schedule_cleanup/schedule_cleanup_file
│ └── subscription.py # 订阅管理器(AstrBot KV 存储封装)
├── web/
│ ├── __init__.py
│ └── api.py # WebUI API handler 函数(依赖注入,独立可测试)
├── pages/
│ └── dashboard/
│ ├── index.html # 管理面板页面(3 Tab: 仪表盘/订阅管理/设置)
│ ├── app.js # 前端逻辑(Tab 切换、API 调用、DOM 渲染)
│ └── style.css # 样式(支持 light/dark 主题)
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Mock astrbot 模块(独立运行集成测试)
│ ├── test_pack.py # 打包功能单元测试
│ ├── test_models.py # 数据模型单元测试
│ ├── test_client.py # 客户端单元测试(mocked HTTP)
│ ├── test_downloader.py # 图片下载/封面下载单元测试
│ ├── test_subscription.py # 订阅管理单元测试
│ ├── test_web_api.py # WebUI API handler 单元测试
│ ├── test_batch_subscribe.py # 批量订阅参数解析单元测试
│ ├── test_push.py # 自动推送单元测试
│ ├── test_updater.py # 更新引擎单元测试
│ ├── test_ai_service.py # Agent Tool 服务层单元测试
│ ├── test_ai_tools.py # AstrBot Tool call() 调度回归测试
│ ├── test_list_chapters.py # /漫画 章节 封面逻辑单元测试
│ ├── test_config.py # 分组配置读写与旧版配置迁移单元测试
│ ├── test_cards.py # 卡片模块单元测试(数据准备/封面嵌入/渲染/缓存)
│ ├── test_card_commands.py # 命令卡片路径回归测试
│ ├── test_live_skip.py # live 探活助手单元测试
│ ├── test_live_api.py # Suwayomi 客户端集成测试
│ └── test_live_web_api.py # WebUI API handler 集成测试
├── docs/
│ ├── dev/ # 开发者文档(本目录)
│ └── superpowers/ # 设计文档和实现计划
├── CHANGELOG.md
├── LICENSE
└── README.md # 用户文档
┌─────────────────────────────────────────────────┐
│ AstrBot Core │
│ (Event Bus, Plugin Manager, KV Storage, Chat) │
└──────────────────┬──────────────────────────────┘
│ @filter.command / on_astrbot_loaded
┌──────────────────▼──────────────────────────────┐
│ main.py — SuwayomiPlugin │
│ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ Commands │ │ Update │ │ Search Cache │ │
│ │ (薄调度层) │ │ Loop (后台)│ │ (TTL 10min) │ │
│ └──────┬─────┘ └─────┬──────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────▼─────────────▼───────────────▼───────┐ │
│ │ suwayomi/service.py │ │
│ │ resolve_manga / resolve_chapter / │ │
│ │ get_or_fetch_chapters / fmt helpers │ │
│ └──────┬─────────────┬───────────────────────┘ │
│ │ │ │
│ ┌──────▼─────────────▼───────┐ │
│ │ suwayomi/client.py │ │
│ │ SuwayomiClient (GraphQL) │ │
│ └──────────┬────────────────┘ │
│ │ │
│ ┌──────────▼────────────────┐ │
│ │ suwayomi/models.py │ │
│ │ Source, Manga, Chapter │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────┐ ┌────────────┐ ┌────────────┐ │
│ │ utils/ │ │ utils/ │ │ utils/ │ │
│ │download.py│ │ pusher.py │ │ pack.py │ │
│ └───────────┘ └────────────┘ └────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ utils/subscription.py │ │
│ │ SubscriptionManager (KV Storage) │ │
│ └───────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
│ GraphQL over HTTP
┌──────────────────▼──────────────────────────────┐
│ Suwayomi-Server (:4567) │
│ /api/graphql (GraphQL Endpoint) │
│ /api/v1/... (REST Legacy) │
└─────────────────────────────────────────────────┘
- 继承
astrbot.api.star.Star - 使用
@filter.command_group("漫画")组织命令 __init__中初始化客户端、订阅管理器、搜索缓存,注册 7 个 WebUI API 端点,并尝试启动后台循环(热重载时事件循环已运行则立即启动)@filter.on_astrbot_loaded()中构建更新检查闭包并启动后台任务(作为首次启动的兜底)terminate()中取消后台任务、取消未执行的临时目录清理任务(cancel_pending_cleanups),并关闭 HTTP 会话- WebUI 保存配置时 (
rebuild_client) 取消旧后台任务、按新间隔重启循环,并清除搜索缓存 - 搜索缓存使用
(timestamp, {index: Manga})结构,10 分钟 TTL 自动过期,并按会话数上限(64)淘汰最旧条目(ttl_cache_store/ttl_cache_lookup) - 所有业务逻辑委托给
suwayomi/service.py、suwayomi/updater.py、utils/downloader.py、utils/pusher.py
- 配置项按功能分组存储(
server/cards/reading/pack/push/ai/advanced七组),与_conf_schema.json、WebUI 设置页一致 get_config_value(config, key, default)— 读取(分组优先,回退旧版平铺键);set_config_value(config, key, value)— 写入分组(旧平铺键仅在值为非默认时清理,默认占位键保留避免 Core 补键日志);flatten_config(config, keys)— WebUI API 平铺展开migrate_legacy_config(config)— 插件每次加载在__init__中调用,无状态幂等:非默认值的平铺键(升级残留或手改)同步进分组并清理,等于_LEGACY_KEY_DEFAULTS的占位键(Core 补回的无意义默认值)原样保留、永不覆盖分组;无变更时不触发保存_conf_schema.json保留全部旧键为invisible: true,使 AstrBot Core 的配置同步不会删除用户旧值;test_legacy_defaults_match_schema双向校验 schema 与代码定义一致
- 独立 async 函数,依赖注入参数(
client、sub_mgr、get_kv_data等) resolve_manga(client, sub_mgr, umo, name_or_id, cmd)— 按 ID 或名称模糊解析漫画resolve_chapter(chapters, chapter_num, manga_name_or_id, cmd)— 按编号或 ID 解析章节(支持重复编号检测);编号解析统一走parse_chapter_number_text(支持5、第5话、第38.5話)- 源选择:
select_search_sources(默认源:排除本地源、扩展名去重、变体补位;命令与 AI 路径共用)、match_source_hint(命令侧源提示:前缀/语言代码匹配)、split_search_query(从完整消息解析关键词与尾部源提示,多词标题用+连接) get_or_fetch_chapters(client, get_kv_data, put_kv_data, config, manga_id, force)— 智能缓存/拉取章节search_best_match(client, config, name, source_filter)— 跨源搜索最佳匹配- 格式化工具:
fmt_chapter_num、fmt_chapter_label、fmt_delivery_failure_message(按失败原因区分提示文案)、normalize_zh - 缓存管理:
get_chapter_timestamp/set_chapter_timestamp(模块级asyncio.Lock串行化读-改-写,防并发覆盖);通用 TTL 缓存助手ttl_cache_store/ttl_cache_lookup - 常量:
STATUS_EMOJI、KV_CHAPTER_TS
ai_tools.py使用显式 JSON Schema 定义并注册六个 Tool:suwayomi_search_manga、suwayomi_get_chapters、suwayomi_send_chapter、suwayomi_subscribe_manga、suwayomi_get_subscriptions、suwayomi_unsubscribe_manga- Tool 子类覆写
call()并从 AgentContextWrapper取得当前事件,不依赖star_manager只执行一次的 handler partial 绑定,因此保存配置后重新注册仍可正常调用 ai_service.py负责跨源并行搜索、漫画元数据序列化、章节选择和重复章节候选返回、订阅/取消订阅/订阅列表查询,不发送消息- 搜索与章节 Tool 返回稳定
manga_id/chapter_id,不依赖命令模式的数字编号缓存 - 阅读发送候选按
(unified_msg_origin, sender_id)隔离 10 分钟,同一时间仅允许一个发送任务(asyncio.Lock防止并发) - AstrBot 成功执行
/reset后,after_message_sent钩子按unified_msg_origin清除搜索缓存、AI 章节候选和发送锁;权限拒绝或重置失败不清理 suwayomi_send_chapter只有在allow_ai_send=true、用户意图已确认且章节来自当前发送者最近查询结果时才发送;默认打包 PDF,用户可明确指定 ZIP、CBZ 或图片- 订阅 Tool 均含
confirmed_user_intent守卫,防止 Agent 未确认即执行有副作用的 KV 写入;suwayomi_subscribe_manga支持可选push_enabled参数同时开启自动推送,已订阅时仍可补充开启推送
check_updates(client, sub_mgr, context, config, get_kv_data, put_kv_data, update_lock, push_chapter_images_fn, push_chapter_file_fn, force, render_update_card_fn)— 全部订阅检查,同步标题,检测新章节,推送通知,自动推送内容。update_library()调用有 30 秒超时,避免挂死。逐订阅检查通过asyncio.Semaphore(_UPDATE_CONCURRENCY=5)并行执行(_check_one_manga),单条损坏/失败不中断整轮;完成时写入suwayomi_last_update_check时间戳。_check_one_manga返回 6 元组(含manga_obj,供更新卡片使用封面/状态/简介);render_update_card_fn注入后,推送通知优先渲染为多部更新卡片,失败回退文本run_update_loop(interval, check_fn)— 后台循环包装器,被main.py的_start_bg_task启动。正确处理CancelledError,Task 异常退出时有日志记录。- 所有依赖通过参数注入,
push_chapter_images_fn和push_chapter_file_fn在main.py的_build_check_updates_fn中预绑定
CARD_TEMPLATE— 单个 Jinja2 HTML 模板字符串,含 7 种卡片变体(搜索/订阅确认/批量订阅/我的订阅/更新/章节头部/章节续卡),远程 T2I 服务端原生渲染build_*数据准备纯函数 — 生成tmpldata,标题/章节名等用户可控文本统一html.escape();漫画简介经clean_description清洗(去 HTML 标签/实体、折叠空白、截断)后转义,章节列表、订阅确认、更新通知卡片均展示;build_chapter_cards(manga, lines)按CHAPTER_LINES_PER_CARD=130切块(三列,最多MAX_CHAPTER_CARDS=4张),超限行原样返回作文本尾部embed_covers(client, items)— 复用utils/downloader.download_images并发下载封面(带认证头,同源策略与download_cover一致),PIL 压缩为 120px 宽 JPEG 后以 base64 data URL 嵌入;失败置cover_data_url=None(模板渲染占位块)render_card(html_render, tmpldata, timeout)—asyncio.wait_for包裹html_render(return_url=False),440px 宽 JPEG q85;任何异常/超时返回None(调用方回退纯文本)CardCache— 以sha1(tmpldata)为键的 TTL 内存缓存(默认 600s),避免相同查询重复渲染;缓存文件由schedule_cleanup_file延后清理- 命令接入统一套路:开关开 →
embed_covers→render_card_cached(成功yield图片 / 失败回退原文本)
download_one(session, url, dest, retries)— 单图下载,指数退避重试download_images(urls, concurrency, custom_tmp, retries, headers)— 并行批量下载,返回(paths, tmp_dir)。headers参数用于注入认证头(client.auth_headers),确保认证服务器下的图片下载正常download_cover(client, thumbnail_url, custom_tmp, retries, headers)— 下载单张漫画封面到临时目录,返回(local_path, tmp_dir);失败返回(None, None),供/漫画 章节列表顶部展示封面fetch_pages_local(client, chapter_id, max_pages, concurrency, custom_tmp, retries, headers)— 获取页面列表并下载到临时目录,返回(total_pages, page_urls, local_paths, tmp_dir)。透传headers到download_images
push_chapter_images(client, context, config, umo, title, chapter, fetch_pages_local_fn)— 推送章节为图片(支持send_mode=forward合并转发)push_chapter_file(context, config, umo, title, chapter, fetch_pages_local_fn)— 推送章节为打包文件(ZIP/CBZ/PDF)build_image_chain(...)— 阅读、自动推送、AI 发送共用的图片/合并转发消息链构建器schedule_cleanup(tmp_dir, delay)— 延迟清理临时目录;任务登记到_cleanup_tasks,插件卸载时由cancel_pending_cleanups()统一取消is_aiocqhttp_target(context, umo)— 检测平台是否为 aiocqhttp(用于 forward 模式判断)
- 基于
aiohttp.ClientSession的异步 HTTP 客户端 - 所有 Suwayomi 交互通过
POST /api/graphql发送 GraphQL 查询/变更 - 支持三种认证模式:无认证、Basic、JWT(自动刷新)
get_sources()结果缓存 60 秒(_sources_cache),减少命令与更新循环中的重复请求- 提供
auth_headers属性,暴露认证头供图片下载时复用(Basic 返回Basic ...,JWT 返回缓存的Bearer ...token) _post_graphql()— 底层 HTTP POST,处理 JSON 解析、错误归一化、网络异常捕获_raw_query()— 上层认证查询,调用_ensure_jwt(),通过_response_data()统一校验响应- JWT 认证使用
asyncio.Lock保护,_is_unauthorized()检测 HTTP 401 / GraphQL Unauthorized 两种失效,_renew_jwt()自动执行 refresh → re-login 降级续期
- 纯数据类(
@dataclass),无副作用 from_dict()工厂方法处理 API 返回的 JSON,强制类型转换(API 返回字符串数字)Source.id为str类型(Suwayomi 的LongString标量)
- 通过 AstrBot 的
get_kv_data()/put_kv_data()持久化 - 数据结构:
{manga_id: {title, source_id, latest_chapter_id, subscribers: {umo: {push_enabled: bool}}}} umo(unified_msg_origin)是 AstrBot 的会话唯一标识- 所有写操作(订阅/取消/推送开关/章节进度/标题同步/偏好)由内部
asyncio.Lock串行化读-改-写,防止后台更新循环与用户操作并发时互相覆盖 delete_manga(manga_id)— 删除漫画的全部订阅者(公开方法)
- 独立 async 函数,通过参数注入依赖(
client、sub_mgr、config),便于单元测试 - 8 个 handler:
api_status、api_subscriptions、api_subscription_delete、api_subscription_push、api_config_get、api_config_post、api_sources、api_update - 成功返回
dict(HTTP 200),错误返回(dict, int)元组(HTTP 4xx/5xx) main.py中通过_json_response()辅助方法统一处理返回格式
- AstrBot Plugin Pages,通过 Bridge SDK 的
postMessage机制与后端通信 - 单页面 3 Tab 结构:仪表盘(状态卡片 + 订阅总览 + 更新检查)、订阅管理(五维筛选 + 删除单条订阅)、设置(配置表单)
- 订阅表按(漫画 + UMO)展开为独立行,每行可单独删除
- 原生 HTML/CSS/JS,零外部依赖
- 支持 light/dark 主题(CSS 变量,由 AstrBot 自动设置
data-theme属性) - 事件委托模式处理按钮点击,避免 XSS 风险
- 使用自定义 DOM 弹窗(
showConfirm())替代原生confirm(),兼容 sandbox iframe(无allow-modals)
搜索流程:
用户输入 → search_manga() → 遍历目标源 → client.search_manga() → GraphQL fetchSourceManga
→ 合并结果 → 缓存到 _search_cache → 返回列表
批量订阅流程:
用户输入 → batch_subscribe() → 按逗号/分号分割名称列表
→ 逐个 suwayomi.service.search_best_match() → client.search_manga() → 取第一个结果
→ 检查是否已订阅 → sub_mgr.subscribe() + 快照章节水位线
→ 汇总报告(✅ 新增 / ⏭ 已存在 / ❌ 失败)
订阅更新流程:
updater.run_update_loop (定时) → updater.check_updates(force=True)
→ client.update_library() (30s 超时) (触发书库更新)
→ 遍历订阅 → 同步标题 + service.get_or_fetch_chapters() + 对比 latest_chapter_id
→ 发现新章节 → context.send_message() 推送到各订阅者
→ 对开启自动推送的订阅者: pusher.push_chapter_images() / pusher.push_chapter_file()
后台循环生命周期:
fresh startup: __init__ → asyncio.get_running_loop() 无运行中循环 → 跳过
→ on_astrbot_loaded() → _start_bg_task() 启动
hot reload: __init__ → asyncio.get_running_loop() 已运行 → _try_start_bg_loop() → _start_bg_task()
config save: rebuild_client() → 取消旧 bg_task → _try_start_bg_loop() → 按新间隔重启
terminate: _bg_task.cancel() → 清理
_bg_task is None 守卫防止重复启动。
更新机制核心方法:
| 方法 | 职责 | 调用者 |
|---|---|---|
updater.check_updates(force) |
主更新逻辑:同步标题、拉取章节、检测新章节、推送通知 | /漫画 更新(force=True)、后台定时更新(force=True) |
service.get_or_fetch_chapters(manga_id, force) |
章节获取:读缓存或从源拉取 | check_updates、/漫画 章节、/漫画 阅读、/漫画 下载 |
service.get_chapter_timestamp(manga_id) / service.set_chapter_timestamp(manga_id) |
管理每个漫画的章节缓存时间戳 | get_or_fetch_chapters、check_updates |
SubscriptionManager.update_latest_chapter(manga_id, chapter_id) |
更新水位线(已通知到的最大章节 ID) | check_updates |
SubscriptionManager.update_title(manga_id, new_title) |
同步漫画标题(仅在变化时写入) | check_updates |
更新判断逻辑:
latest_chapter_id = 当前水位线(按 manga_id 存储,不是章节编号)
for ch in chapters:
if ch.id > latest_chapter_id: ← 比较数据库自增 ID,不是章节编号
标记为新章节
更新水位线为 max(ch.id)
- 水位线是全局共享的(按 manga_id),不是按 UMO 隔离
- A 手动触发更新后,B 的下次更新不会重复推送已通知的章节
- 章节编号可能重复或不连续(如番外、附录),但数据库 ID 唯一递增
各入口的缓存行为:
| 入口 | force | 标题同步 | 章节来源 | 水位线更新 |
|---|---|---|---|---|
/漫画 章节 |
使用 service.get_or_fetch_chapters 决定 |
否 | 缓存(过期才拉取) | 否 |
/漫画 章节 --刷新 |
传递 force=True | 否 | 源站 | 否 |
/漫画 更新 |
force=True | 是 | 源站 | 是 |
| 后台定时更新 | force=True | 是 | 源站 | 是 |
/漫画 阅读 / /漫画 下载 |
使用 service.get_or_fetch_chapters 决定 |
否 | 缓存 | 否 |
章节缓存机制:
service.get_or_fetch_chapters(client, get_kv_data, put_kv_data, config, manga_id, force=False)管理章节数据的缓存- 缓存时间由
chapter_cache_hours配置控制(默认 6 小时) 0= 仅在 DB 为空时拉取,-1= 每次都从源刷新force=True可绕过缓存(通过--刷新参数或更新检查触发)- 每个漫画的最后拉取时间戳存储在 KV key
suwayomi_chapter_timestamps
阅读流程:
用户输入 → read_chapter() → service.resolve_manga() (ID/名称/模糊匹配)
→ service.get_or_fetch_chapters() → service.resolve_chapter()(支持 ID:xxx 语法)
→ event.send(loading hint)
→ client.fetch_chapter_pages() → 获取页面 URL 列表
→ url 模式: Comp.Image.fromURL()
→ download 模式: downloader.fetch_pages_local() + Comp.Image.fromFileSystem()
→ 逐页发送 / Comp.Node 合并转发
→ pusher.schedule_cleanup() 延迟清理临时文件
下载流程:
用户输入 → download_chapter() → service.resolve_manga() → service.get_or_fetch_chapters()
→ service.resolve_chapter()(支持 ID:xxx 语法)
→ event.send(loading hint)
→ downloader.fetch_pages_local() → 下载所有页面到临时目录
→ pack_zip/pack_pdf/pack_cbz() → 打包为文件
→ Comp.File() 发送文件 → pusher.schedule_cleanup() 延迟清理
自动推送流程:
updater.check_updates() 检测到新章节 → 遍历订阅者:
├─ pusher.push_chapter_images() — 图片模式
│ ├─ fetch 页面 URL client.fetch_chapter_pages()
│ ├─ 或 downloader.fetch_pages_local()(image_fetch_mode=download)
│ ├─ context.send_message() 发送图片/forward
│ └─ schedule_cleanup() 清理
└─ pusher.push_chapter_file() — 文件模式
├─ downloader.fetch_pages_local() 下载全部页面
├─ pack_zip/pack_pdf/pack_cbz() 打包
├─ context.send_message() 发送文件
└─ schedule_cleanup() 清理
- Python 3.12+
- uv 包管理器
- 可访问的 Suwayomi-Server 实例(用于集成测试)
cd AstrBot/data/plugins/astrbot_suwayomi_server
# uv 会自动创建 .venv 并安装依赖
uv sync
# 安装开发依赖
uv add --dev pytest pytest-asyncio# 全部单元测试(无需网络)
uv run pytest tests/test_pack.py tests/test_models.py tests/test_client.py tests/test_downloader.py tests/test_list_chapters.py tests/test_cards.py tests/test_card_commands.py tests/test_subscription.py tests/test_web_api.py tests/test_batch_subscribe.py tests/test_push.py tests/test_service.py tests/test_updater.py tests/test_ai_service.py tests/test_ai_tools.py tests/test_live_skip.py tests/test_config.py -v
# 实时 API 集成测试(需要 Suwayomi-Server 可访问)
uv run pytest tests/test_live_api.py tests/test_live_web_api.py -v -s
# 指定自定义服务器地址(推荐:先设置环境变量避免连接默认 :4567 失败)
$env:SUWAYOMI_URL="http://your-server:9330"; uv run pytest tests/test_live_api.py tests/test_live_web_api.py -v -s
# 带认证的服务器
$env:SUWAYOMI_URL="http://your-server:9330"; $env:SUWAYOMI_AUTH_MODE="basic"; $env:SUWAYOMI_USERNAME="user"; $env:SUWAYOMI_PASSWORD="pass"; uv run pytest tests/test_live_api.py tests/test_live_web_api.py -v -s
# 全部测试
uv run pytest -vpython -c "import ast; ast.parse(open('main.py', encoding='utf-8').read()); print('OK')"Suwayomi-Server 同时提供 GraphQL 和 REST API,但 GraphQL 是功能完整的主接口:
fetchSourceManga(搜索)仅 GraphQL 可用fetchChapterPages(获取页面 URL)仅 GraphQL 可用- REST 是遗留接口,功能不全
Suwayomi 的 source 字段类型是 LongString(自定义标量),不是 Long。GraphQL 变量声明必须用 $sid:LongString!,JSON 传值也必须是字符串 "524579092615598717" 而非数字。
该 Suwayomi 版本的 mangas 查询:
condition: {title: "..."}— 精确匹配,不适合模糊搜索filter: {title: {includes: "..."}}— 子串匹配,适合按标题搜索
AstrBot 加载插件时调用 __init__,此时事件循环可能尚未运行。asyncio.create_task() 需要一个运行中的事件循环。on_astrbot_loaded 钩子在 AstrBot 完全启动后触发,确保事件循环就绪。
AstrBot 的命令分发器将所有参数作为原始字符串传递,不做类型转换。类型注解 int / float 仅用于文档目的。插件需要在入口处显式 float() / int() 转换。
- 在
SuwayomiPlugin类中添加方法 - 使用
@manga_group.command("命令名")装饰器 - 第一个参数必须是
event: AstrMessageEvent - 使用
yield event.plain_result(...)返回文本 - 使用
yield event.chain_result([...])返回富媒体 - 在方法 docstring 中写明用法(AstrBot 展示给用户)
- 所有用户提示文本使用
「漫画 命令名」格式(带空格)
详见 Suwayomi API 参考。