/添加广告 广告内容 直接添加文字广告,并保留下一条消息录入模式;增加 /加广告 和 /新增广告 别名。All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
/宝塔 <子命令>)
/宝塔 系统状态、磁盘信息、内存详情、CPU详情、系统负载、网络流量、释放内存、重启面板、清理系统、服务 <重启|启动|停止|重载> <服务名>网站列表、网站开启|网站停止|网站备份 <域名>、网站SSL <域名>数据库列表、数据库状态、MySQL配置、数据库备份 <库名>计划任务、任务启用|任务暂停 <ID>、任务日志 <ID>FTP列表、后台任务、安全扫描、安全评分、帮助request_time + md5(request_time + md5(API_SK)) 表单 POST),支持自签证书btpanel_enabled、btpanel_url、btpanel_api_sk,均可通过 AstrBot 设置界面配置/狐狸菜单、/狐狸记录 帮助 帮助文本未包含广告助手指令,现补充「📢 广告助手」章节,与 README 指令文档对齐context.send_message 向任意平台群聊发送广告dsgg_platforms(白名单)与 dsgg_exclude_platforms(黑名单),默认全部平台参与广播disable_gids 群黑名单支持「群ID」(屏蔽所有平台该群)与「platform:群ID」(精确到平台)两种格式/开启广告、/关闭广告(所有人,群级开关)、/广告群列表、/添加广告(30 秒富媒体采集,支持「取消」)、/删除广告、/广告列表、/查看广告、/定时广告 HH:MM[,HH:MM...]、/停止广告(管理类命令)dsgg_enabled、dsgg_platforms、dsgg_exclude_platforms、disable_gids、dsgg_send_interval,均可通过 AstrBot 设置界面配置/狐狸工具 请求(类 curl 发送 HTTP 请求)、url检测、测速、ping、帮助,其中请求命令支持 -X/-H/-d/-b 参数解析,长响应自动用 LLM 总结并降级截断请求/url检测/测速 复用媒体下载的 _is_safe_url 校验初始 URL,并改为逐跳校验重定向目标,封堵通过重定向绕过访问内网/云元数据地址(169.254.169.254 等)- 开头(-f 洪泛、-i 篡改间隔等)统计/今日/昨日/历史 此前任何群成员可查看全部消息记录(含私聊),现统一要求管理员权限generate_payment_url 对 user_id/remark/price 统一 URL 编码,并移除含参数的 debug 日志_establish 连接失败时未关闭新建的 Redis 客户端,重连循环会累积底层 TCP 连接;现失败路径统一关闭新建与旧客户端parse_natural_time 对超大数值(如 last999999999d)timedelta 溢出未被捕获,且超长数字串会触发 int() 限制;现统一捕获溢出并限制数字位数为 9 位_draw_header 对超范围/非法时间戳调用 time.localtime 抛异常,现通过 _safe_time_str 安全降级format_time/赞助金额解析对非数字或超范围值返回空而非崩溃;订单 sku_detail 序列化对非序列化值降级为字符串;API 响应 JSON 解析失败返回友好错误测速 秒数、ping 次数/主机名对非数字或 None 参数返回参数错误而非命令异常_save_messages_batch_mysql 内层与外层 save_messages_batch 对同一批记录重复执行最近消息缓存与统计增量,导致 MySQL 批量保存时统计翻倍;现批量路径下缓存与统计增量仅由外层入口执行一次/查询记录 等触发媒体解析时,http(s) 链接此前仅经 MediaResolver 直接下载、无内网地址校验;现解析器路径增加与普通下载一致的 SSRF 前置校验,封堵内网探测(169.254.169.254 等)/狐狸记录 管理命令未鉴权:清理/查询/搜索/表列表/快照子命令此前任何会话成员均可触发,现统一要求管理员权限,帮助文本同步标注 [管理员]/media 接口与 zip 导入的媒体文件按 Content-Type 白名单过滤(仅图片/音频/视频),拦截 html/svg/xml 等脚本载体,防止存储型 XSShmac.compare_digest 恒定时间比较(留空表示不校验的既有行为不变);0.00 元订单(金额下限 <0.01)被拒绝/狐狸记录 表列表 的 SQL 查询拦截 @@version/@@datadir 等系统变量读取,避免泄露服务器敏感信息ESCAPE '\\',与 SQLite 行为对齐,避免 %/_ 通配符绕过精确匹配redis_cache_refresh_interval,默认 1800 秒即 30 分钟),每次从数据库重建最近消息缓存并强制回源刷新统计缓存,消除长期增量累积导致的数据漂移redis_recent_window_seconds(默认 1800 秒即 30 分钟)时间窗口裁剪,只保留窗口内的最新消息_cache_recent_messages 逐条推送时仅取最后 20 条,批量保存超过 20 条时中间部分消息不会进入缓存;现改为全部推入,由 Redis 端负责窗口裁剪与条数上限/狐狸记录(/huli_record 保留为别名),子命令(统计/清理/查询/搜索/帮助/今日/昨日/历史/表列表/快照)与爱发电指令均改为中文主命令,旧英文指令仍可作为别名使用;帮助菜单同步更新并增加别名提示connection_max_retries 控制(默认 5,最小 1),达到上限后停止自动重连,分别进入 SQLite 降级 / 无缓存模式,并记录日志提示;运行中 Redis 断连期间自动以无缓存模式运行,恢复后自动切回缓存_conf_schema.json 与 README 配置章节统一按「功能配置 → MySQL → SQLite 兜底 → 爱发电 → Redis」分组排列,方便查阅与配置using_fallback 反推连接状态,在”MySQL 故障但未启用兜底”场景会误报「已连接」;现改用连接池真实就绪状态判定,全部状态(已连接/已降级/未连接)准确反映 MySQL 实际可用性_recovery_loop 无限周期重连,MySQL 长时间不可用时持续占用后台资源;现达到 connection_max_retries 上限后停止自动重连,保持降级模式OrderDB 现在会周期检测主库恢复(每 30 秒,可通过 afdian_recovery_check_interval 配置),恢复后自动重新绑定连接池并把降级期间积累的订单幂等回写 MySQL(INSERT IGNORE 去重),消除此前”降级永久化、SQLite 与 MySQL 分脑”的问题busy_timeout(30 秒)+ synchronous=NORMAL,并发写入(Webhook 与轮询并存)时的锁定异常被捕获记录日志而非上抛导致订单处理失败/狐狸菜单 与 /hulihelp 此前未列出爱发电模块指令;现新增「⚡ 爱发电」分类,完整展示 /发电 [金额](普通用户)及 5 个 [管理员] 指令(爱发电测试、同步历史订单、查询订单、查询发电、开启发电通知),与真实注册指令一一对应INSERT IGNORE + content_hash 唯一索引去重,默认每 30 秒检测一次、单批 500 条)。降级期间查询、统计、排行、时间线、导出、快照全部保持可用,Web 面板状态卡片展示当前存储后端与未同步消息数;新增 storage_fallback_enabled(默认开启,零配置兜底)、recovery_check_interval、backfill_batch_size、sqlite_max_retention_days 四个配置项(SQLite 兜底库自动清理已同步旧消息控制文件增长)synced=1)数据,未同步消息完整保留至补写成功SELECT 1 探测,无法重建已失效的连接池,导致 MySQL 恢复后永不切回;现每次恢复检测重新建立连接池并初始化表结构,成功后才切回主存储并补写storage_fallback_enabled=false 配置项容错:main.py 读取新增数值配置项时改用 _safe_int 容错,用户填非法值时回退默认值,避免插件初始化崩溃_import_zip_package 解压媒体文件此前无任何大小/数量限制,恶意或异常导入包可把磁盘撑爆、data.json 全量读入内存无上限。新增三个上限:data.json 最大 512MB、媒体解压总量最大 8GB、媒体条目数最大 10000,超限直接拒绝导入media_downloader 对服务器返回的非数字 Content-Length 头直接 int() 抛 ValueError,该异常不在现有 except 范围内导致该次下载静默失败,现已容错为 0 继续按实际内容大小限制校验_ensure_limit 仅识别 LIMIT <数字>,对参数化 LIMIT ?/LIMIT %s/LIMIT ALL 等已有 LIMIT 检测不到而追加第二个 LIMIT,导致 SQL 语法错误;现对任意形式的已有 LIMIT 均不再追加,仅钳制其中的数字 count/huli_record snapshot 生成图片后在命令生成器 finally 中立即删除文件,而 AstrBot 框架在生成器结束后才实际发送图片,发送失败率高;改为登记延迟删除任务(30 秒后清理,纳入 _pending_tasks 生命周期管理)_pending_tasks 无上限:消息监听为每条消息新建后台任务并无限累积,消息洪峰时内存持续增长;新增 MAX_PENDING_TASKS=200 上限,超过时先等待任一任务完成再登记,形成自然背压v2.4.3 与当前版本脱节,改为从 metadata.yaml 动态读取版本号total_count、平台分布、group/private/channel 桶、时间区间),TTL 滑动续期,get_stats 命中缓存即可返回最新数据,无需等待 TTL 过期回源。Redis 不可用或缓存缺失(TTL 过期、从未回源)时自动回源数据库重建兜底,不阻塞消息保存主流程media_downloader 通过 AstrBot MediaResolver 读取媒体时,file:/// 与绝对本地路径此前不经过校验,任意群成员发送 file:///etc/passwd 类图片消息即可触发宿主机任意文件读取并落盘。新增 _safe_local_read_path 白名单校验:仅放行 AstrBot 数据目录(含 OneBot 客户端缓存)之内的路径;OneBot 兜底 download_file/get_image 返回的本地路径同步纳入校验parse_date 对 2024-13-40、2024-02-30 等格式合法但月/日越界的输入抛未捕获 ValueError,穿透 parse_time_range 使 Web 查询与 /huli_record history 功能失效。现在返回 None 并正常回退最近 24 小时limit 参数为负数时 SQL 报错:api_stats_senders/api_stats_groups/api_stats_senders 排行等接口对 ?limit=-1 生成 LIMIT -1,MySQL 报 Incorrect arguments to LIMIT。_safe_int 增加 min_val/max_val 钳制,负数归零、超大值截断到上限cleanup_expired_tasks 清理过期/崩溃的导入任务时未删除上传文件(对比导出任务分支会删除),中断的导入文件永久残留磁盘。现已与导出任务一致删除 file_pathexports/ 目录残留:_cleanup_temp_dir 启动清扫只清理 temp/,崩溃重启后旧导出文件残留。现已同时清理 exports/+1d 相对时间无法解析:parse_relative_time 正则仅接受 -,与文档声称支持 +1d 不符,已支持 + 前缀;极端天数(如 999999999999d)触发 OverflowError 已捕获_to_int 不兼容 Decimal:snapshot_renderer._to_int 声称兼容 MySQL 驱动的 Decimal,但 Decimal 实例会落入默认值返回 0,已改用 numbers.Number 判定/发电 生成的付款链接备注(32 位 hex)可能与爱发电回调备注的字符串大小写不一致,导致 on_afdian_new_order 无法匹配待确认订单。新增 _match_pending_sender 按备注精确/忽略大小写匹配int() 转换,走通用告警路径parse_order 输出改为分栏布局(标题分隔线 + 字段 + SKU 列表),金额统一 ¥xx.xx 格式化,订单状态显示为「待支付/已支付」可读文案而非裸数字sys_util.py 及其测试:进程资源采集(CPU/内存/运行时长)在 Dashboard 移除「资源占用」卡片后已无任何生产调用方,仅被测试引用order_db.py 死代码:using_mysql 属性与 get_order_by_id/get_orders_by_user/get_orders_by_status 查询方法(无调用方)web_api.py 死代码:未使用的 IMPORT_RECORD_TIMEOUT 常量、_safe_float_metric 辅助函数与 List importserializer.py 无生产引用的常量组(_INTERACTIVE_COMPONENT_TYPES/_RICH_MEDIA_COMPONENT_TYPES/ALL_KNOWN_COMPONENT_TYPES,保留有测试保护的 MEDIA_COMPONENT_TYPES 与 COMPONENT_TYPE_MEDIA_MAP)conftest.py/test_api.py/test_database.py/test_media_downloader.py/test_models.py/test_platform_adapter.py/test_redis_cache.py/test_serializer.py/test_time_utils.py 中未使用的 import 与局部变量main.py 扩展名映射去重:移除重复的 "ts" key(原被后置 FileVideo 覆盖),行为不变,消除字典重复键告警snapshot_renderer.py 清理未使用变量:_truncate_middle 的 ew、玻璃卡片绘制的 pad/glass_draw、状态卡片未接收的返回值.gitignore 粘连行:CLAUDE.md 与 test_glassmorphism_snapshot.png 两个条目间缺失换行符/orders 端点强制令牌:未配置 afdian_webhook_token 时不再放行查询(原逻辑无令牌即 403,但未配置时返回所有订单,含收货手机号/地址等隐私数据);现未配置或令牌不匹配一律 403?token=xxxgenerate_payment_url 对 remark 做 urllib.parse.quote,防止特殊字符污染 query 参数/发电 金额上下限校验:金额为 0/负数/超过 100000 时拒绝并提示,校验前置到待确认订单登记之前(非数字文本仍按既有设计回退默认金额)use_polling=False 时轮询循环永不启动,afdian_pending_orders/限流状态改为在每次 /发电 时惰性清理,防止字典无限增长get_content_type_stats/get_platform_detail_stats/get_timeline_stats/get_group_ranking 全表拉取标注 ponytail 注释(百万级消息时内存峰值明显,需改 SQL 聚合)_fetch_via_media_resolver 的任意本地文件读取风险以注释说明权衡(兼容 OneBot 客户端缓存下载),未改变行为cmd_afdian_simulate,/爱发电测试 成为唯一测试指令(别名含 发电测试/发电模拟/模拟发电/模拟发电订单/爱发电模拟),统一走完整订单链路afdian_simulate_new_order 完整推送所有已配置通知会话,并在当前聊天会话不在通知会话时补推,便于验证推送链路/history 使用已解析的 start_time/end_time 查询,避免时间范围二次解析total_records 后又随循环累加,导致总数翻倍;移除预置统一由循环计数_migrate_v3 幂等性:全新数据库建表已含 content_types,迁移 v3 重复 ADD COLUMN 会崩溃;现用 INFORMATION_SCHEMA 检查列/索引存在性后跳过_query_mysql 新增 sqlite_sql 参数,MySQL 故障降级后走 SQLite 查询on_afdian_new_order 兜底私聊内容错误:原发订单文本 message,改为发 default_reply_parse_query_filter limit 无下限:limit 为 0 或负值时钳制到 [1, 200]order_db.py 中无调用方的 save_order/_save_mysql/_save_sqlite、afdian_api.py 的 ping()、media_downloader.py 的 cleanup_orphaned_media、star.py 只写不读的 afdian_started、_MockAfdianClient.query_sponsor、web_api.py 未使用的 sys_util import、snapshot_renderer.py return 后的不可达代码debug_*.py、final_*.py、quick_test.py、second_check.py、test_*.py 等)及 3 个过期文档(PROJECT_SUMMARY.md、README_TEST_IMAGES.md、RENDERER_FIX_SUMMARY.md)已注册 26 个 Web API,改为运行时计数(当前 31 个)generate_payment_url 失败(如 API 异常)也会累加次数甚至误触发拉黑;现拆分为 _afdian_rate_limit_exceeded(判断是否达上限)与 _afdian_record_order(链接成功生成后才计数),失败不计数、不误拉黑afdian_pending_orders 中的未支付记录,避免轮询持续为其工作_afdian_cleanup_rate_limit_state,随轮询定期清理已到期拉黑与窗口外计数,防止 afdian_order_history / afdian_blacklist 无限增长/发电 防刷限流:同一用户在 afdian_rate_limit_window(默认 60 秒)内发起订单次数达到 afdian_rate_limit_max_orders(默认 3 次)时,拒绝本次 /发电 并拉黑 afdian_rate_limit_ban_seconds(默认 1 小时);拉黑期间该用户 /发电 直接返回临时限制提示。可通过 afdian_rate_limit_enabled 开关关闭/发电模拟 命令触发受限问题:原命令走 _afdian_check(要求真实 API 凭据已配置),但模拟命令使用 mock 客户端、不请求真实接口,未配置 user_id/token 时会被直接拦截;现改为仅要求功能开关开启,未配置凭据也可用于验证推送链路out_trade_no 使用秒级时间戳 TEST{now},同一秒内重复执行会被 save_order_if_new 按交易号去重而跳过推送;现追加 uuid 短后缀保证每次订单号唯一/发电模拟 测试指令(别名 /模拟发电、/模拟发电订单、/爱发电模拟):模拟一笔新订单并走完整检测链路——构造模拟订单 → 注入 mock API 客户端走 afdian_poll_once(拉单 → save_order_if_new 按交易号去重入库 → on_afdian_new_order 推送到所有已设置的通知会话/推送群,并按备注匹配向付款用户回复);不发起任何真实网络请求,用于验证「用户发电 → 自动检测 → 订单成功 → 推送到推送群」全链路afdian_use_polling 就常驻每 afdian_poll_interval 秒请求一次接口,导致后台日志刷屏;现改为用户 /发电 生成待确认订单时才启动轮询,每 afdian_poll_interval 秒(默认 5 秒)拉取一次,最多持续 afdian_poll_timeout 秒(默认 5 分钟),待确认订单处理完或窗口到期即自动停止,无人发电时不再请求接口query_order / query_sponsor 不再每次打印完整接口响应,改为有数据时打印返回条数、无数据时降为 debug 级别out_trade_no 去重的原子写入(INSERT IGNORE),旧订单不会被重复覆盖query_sponsor 原先默认把创作者自己的 user_id 当作赞助者筛选条件传给接口,导致按”创作者作为赞助者”过滤恒为空;现默认不传 user_id 查询全部赞助者,仅当显式指定赞助者用户 ID 时才按该 ID 筛选cardFloat 悬浮效果,风格统一t2i_template.html 的 markdown 源文本原先使用 `` 不做 HTML 转义,当订单备注、计划标题等含 <、&、</textarea> 字符时会破坏页面结构导致渲染空白或乱码;现改为强制转义,插件名与版本号同步转义/huli_record snapshot 快照在统计卡片下方展示 Redis 状态摘要RedisCache.status():新增状态摘要方法,供 WebUI 与快照读取缓存健康信息/同步历史订单 命令:通过爱发电 API 主动分页拉取全部历史订单入库(按交易号去重),随时可手动补拉,不依赖 WebhookOrderDB 检测到池已关闭/acquire 失败时永久降级 SQLite(解除池引用),不再每轮轮询重复尝试 MySQL 并刷屏 Cannot acquire connection after closing pool;terminate 先停爱发电轮询/同步/Webhook 再关闭连接池/同步历史订单 命令说明与历史订单同步说明fox_toolbox/redis_cache.py 封装 redis.asyncio,为 WebUI 高频查询缓存消息统计(get_stats,按 redis_cache_ttl TTL 刷新)与最近消息列表redis_enabled / redis_host / redis_port / redis_password / redis_db / redis_cache_ttl(已同步 _conf_schema.json 与 README 配置说明)media_downloader.py):下载文件落盘前校验路径归属媒体目录,拒绝 ../ 或绝对路径等越界文件名media_downloader.py):下载请求改为逐跳校验重定向目标,封禁内网/链路本地地址,最多跟随 5 跳web_api.py):打包媒体附件前校验目标路径归属媒体目录,拒绝越界文件被导出web_api.py):无 ijson 回退到 json.load 时限制单条记录 64MB 上限,异常记录跳过并记录database.py get_stats):改为 MySQL 端 COUNT(*) + GROUP BY 聚合,不再整表拉入 Python 内存database.py):关键词含 +-<>()~*"@ 保留字符时自动降级 LIKE 匹配record.id(lastrowid),保证缓存项可追溯asyncio.to_thread 异步化_assemble_chunks 异常时清理临时目录与会话,api_import_complete 防止 NameErrorLIMIT offset, count 的 offset 与 count 均钳制;新增危险函数拦截(INTO OUTFILE / LOAD_FILE / SLEEP / BENCHMARK / LOAD DATA)main.py 初始化异常时正确关闭数据库连接池afdian_webhook_token 后仅接受 URL query 携带正确 token 的回调,未配置保持原行为向后兼容cleanup_by_age / cleanup_by_limit 改为 keyset 分页收集 + 分批 DELETEdownload_name / attachment_filename 参数OrderDB.save_order_if_new 使用 INSERT IGNORE / INSERT OR IGNORE 原子判重(替代”先查再存”),Webhook handle_order、轮询、历史同步统一走该路径,杜绝 TOCTOU 竞态导致的同一订单重复通知afdian_orders 的 idx_remark 对 VARCHAR(512) utf8mb4 建全列索引超过 MySQL 5.7 的 767 字节索引上限(512×4=2048),改为前缀索引 remark(191)afdian_poll_once 由只拉第一页改为循环分页拉取,遇到已入库订单即停,避免突发大量订单时漏掉新订单handle_order 在保存订单前先查库判重,同一订单仅由先到达的通道处理并触发一次回调,避免重复通知afdian_poll_loop 启动时先等待历史同步任务完成,避免历史订单被误判为新订单触发通知afdian_poll_timeout 自动回收/发电 金额展示崩溃风险:金额可能以字符串传输,增加类型转换容错/查询订单 与 /查询发电(/查询赞助)共用同一渲染模板,仅指令不同self.html_render(官方推荐 API),移除对 astrbot.core.html_renderer 内部单例的直接依赖parse_order / parse_sponsors 输出升级为 markdown 结构(加粗字段、条目标题),图片渲染层次更清晰/发电(别名 /赞助)、/爱发电测试、/查询订单、/查询发电(别名 /查询赞助)、/开启发电通知(别名 /发电通知、/爱发电通知)afdian_enabled、afdian_webhook_host、afdian_webhook_port、afdian_api_base_url、afdian_api_user_id、afdian_api_token、afdian_default_price、afdian_default_reply、afdian_notice_sessionsafdian/orders.db,Webhook 服务独立于消息记录功能启动,失败不影响主功能afdian_orders 表(与消息记录同一连接池),MySQL 不可用时自动回退 SQLite 兜底/查询订单、/查询发电 结果图片顶部水印改为插件名 + 插件版本(自定义 T2I 模板,替代默认框架水印)afdian_use_polling / afdian_poll_interval / afdian_poll_timeout 配置,用户发电后提示限时完成支付,插件定时(默认每 5 秒)拉取订单检测新订单,备注匹配与自动回复逻辑与 Webhook 完全一致,无需公网回调v2.4.1ImageFont.load_default() 无法渲染中文。现在渲染器会在常见字体目录中自动搜索 CJK / emoji 字体,无需人工安装hulihelp / 狐狸菜单 顶层命令:发送 /hulihelp 或 /狐狸菜单 即可查看全部可用指令MessageType 按上下文回退到群聊、私聊、频道,减少统计口径漂移v2.3.22.3.2RENDERER_FIX_SUMMARY.md 版本记录到 v2.3.2message_type 原值,历史 other / forum / 脏数据会结合 group_id 与 channel_id 自动归类到群聊、私聊、频道,顶部统计卡、时间趋势、平台详情、群组排行恢复正常other 消息现在会参与堆叠柱状图统计,柱顶总量标签改为显示在柱体上方,0 值平台保留基线提示content_types 兼容逗号串、JSON 数组、空值回退到纯文本推断,旧数据也能生成内容类型分布图v2.3.12.3.12.2.2。/huli_record snapshot 快照图大面积变黑(旧版圆环图 putalpha bug):旧版 _draw_platform_donut 用全不透明掩码绘制内圆镂空,导致非圆环区域 RGB 置黑后以 alpha 255 合成,整幅图被黑色覆盖;改为在圆环图层上用透明色重绘内圆镂空,彻底解决黑屏_make_background putpixel 缺少 alpha 导致背景出现透明黑(改用 1px 渐变条 resize 方案)_draw_ranking 空数据分支引用未定义变量 inner_h、_draw_glass_card 内发光椭圆坐标越界_draw_content_types 由旧版独立 hex 色饼图重构为与平台分布一致的玻璃态圆环图(RGBA 分段、发光中心文字、右侧图例进度条、统一配色)ImportError: cannot import name '_to_int'(热重载缓存根因):main.py 全部 11 处 from fox_toolbox.xxx import ... 改为相对导入 from .fox_toolbox.xxx import ...,符合 AstrBot 官方插件规范
fox_toolbox 作为顶层包缓存于 sys.modules,AstrBot 热重载只清理 data.plugins.<插件名>.* 前缀模块,旧版模块残留导致更新文件后仍报错data.plugins.astrbot_plugin_fox_toolbox.* 前缀命名,热重载可正确清理,更新文件后无需完全重启进程scripts/fix_deploy.sh:改用 cp -r src/. dest/ 合并覆盖语法,确保已存在的旧目录内文件被更新;新增同步后校验输出data.plugins.astrbot_plugin_fox_toolbox.fox_toolbox.*scripts/fix_deploy.sh 一键同步脚本:在 AstrBot 部署服务器上执行,将插件代码完整对齐到远程最新版本(fox_toolbox/、main.py、pages/、metadata.yaml),避免 main.py 与 fox_toolbox/ 文件版本混用导致的 ImportError: cannot import name '_to_int'/huli_record snapshot 报错 'dict' object cannot be interpreted as an integer:新增 _to_int 安全类型转换函数,对所有统计数值(时间趋势、排行、平台分布、平台详情、内容类型、数据表数量)做防御性转换,兼容 MySQL 驱动的 Decimal、None、字符串数字、dict 等异常类型,杜绝渲染崩溃_draw_content_types 饼图中 math 未导入导致的 NameError_draw_content_types 饼图引用不存在的 _TEXT_DARK、_GLASS_BG 常量导致的 NameError(改用 _TEXT、_TRACK)_draw_platform_detail、_draw_content_types 兼容 dict 类型输入(自动转换为列表结构)/huli_record snapshot 增加渲染兜底:将快照渲染调用包入 try-except,即使渲染过程出现任何异常,命令也会返回友好提示并记录完整日志,不再让 astrbot 弹出崩溃异常_draw_header 最新消息时间戳:newest_timestamp 统一经 _to_int 安全转换,异常类型(dict 等)不再导致 TypeError/huli_record stats 最早/最新消息时间戳:同样经 _to_int 安全转换后再做 /1000 运算/huli_record stats 与 /huli_record snapshot 对异常数据达到同等防御水平huli_record(原 msg_record),所有子命令同步更新_draw_content_types 渲染饼图(甜甜圈),展示各内容类型消息占比。饼图中心显示总消息数,右侧图例含颜色圆点/类型名/进度条/百分比数值,使用与 WebUI 一致的配色方案(#4fc3f7、#29b6f6 等),支持最多8种内容类型显示render_snapshot 画布高度增加到 _PX(2800),容纳新增的内容类型卡片_PX(2500) -> _PX(2800),容纳新增的内容类型卡片0.2.10_draw_platform_detail 渲染堆叠柱状图,展示各平台群聊/私聊/频道消息分布。每平台包含三色堆叠柱(群聊#4fc3f7、私聊#29b6f6、频道#81d4fa),顶部显示平台总消息数,左上角图例含三色圆点/系列名,X轴平台名自动映射(Telegram/Discord/QQ 官方/微信等),位置在平台分布与内容类型之间render_snapshot 新增 platform_detail 参数(默认回退数据库查询结果);main.py 的 cmd_snapshot 新增 platform_detail 数据获取_draw_platform_detail 堆叠柱状图绘制函数,支持多系列堆叠、圆角柱体、网格线、峰值标注_PX(2200) -> _PX(2500),容纳新增的平台详情卡片0.2.9app.js 的 BUILD_VERSION 与版本号同步问题(0.2.2 → 0.2.9)_draw_platform_donut 渲染圆环图(甜甜圈)+ 右侧图例,展示各平台消息占比。圆环中心显示总消息数(蓝色渐变)+”总消息”标签;右侧每行含颜色圆点、平台名称、进度条、数值百分比;平台名映射(telegram->Telegram、discord->Discord、qq_official->QQ 官方、wechat->微信等),未映射则显示原始 key。位置在时间趋势与排行之间render_snapshot 新增 platform_stats 参数(默认回退 stats.platform_stats);main.py 的 cmd_snapshot 传入 stats.platform_stats_PLATFORM_LABELS 平台名映射表_PX(1900) -> _PX(2200),容纳新增的平台分布卡片0.2.8python3 -m py_compile fox_toolbox/snapshot_renderer.py main.py 通过node --check pages/recorder/app.js 通过PYTHONPATH=/workspace python3 -m pytest -q 与基线一致(无回归)_draw_timeline 重写,按 count / group_count / private_count / channel_count 四系列绘制四条折线(总消息-蓝、群聊-绿、私聊-橙、频道-红),左上角图例带对应颜色圆点,”总消息”系列保留浅蓝区域填充,各系列统一 Y 轴刻度app.js 在移动端(≤768px)将平台分布/内容类型圆环图的图例改为底部横向滚动(orient: 'horizontal', type: 'scroll')、圆环缩小并上移,避免图例与圆环重叠;style.css 手机端图表容器高度 210→280px、主内容底部 padding 增加 2.5rem,防止圆环图底部被浏览器导航栏遮挡app.js 与 style.css 查询参数升级至 0.2.7python3 -m py_compile fox_toolbox/snapshot_renderer.py 通过node --check pages/recorder/app.js 通过PYTHONPATH=/workspace python3 -m pytest -q 与基线一致(无回归)Image.load() 像素访问器直接索引赋值在 resize 时数据丢失,导致渐变背景生成全黑。改用 Image.paste 填充后恢复浅色渐变背景python3 -m compileall fox_toolbox main.py 通过PYTHONPATH=/workspace python3 -m pytest -q 与基线一致(10 failed, 202 passed, 71 errors,无回归)python3 -m compileall fox_toolbox main.py 通过PYTHONPATH=/workspace python3 -m pytest -q 与基线一致(10 failed, 202 passed, 71 errors,无回归)python3 -m compileall fox_toolbox main.py 通过PYTHONPATH=/workspace python3 -m pytest -q 与改动前基线一致(未引入回归)/huli_record snapshot 指令:将数据库统计渲染成与 WebUI 风格一致的 PNG 快照图并发到聊天,包含统计卡片、消息时间趋势、发送者/群组排行 Top 8、内容类型分布fox_toolbox/snapshot_renderer.py,基于 Pillow 渲染 Liquid Glass 风格仪表盘快照,零新增重依赖(仅复用已存在的 Pillow)/huli_record help 帮助文本补充 snapshot 指令说明python3 -m compileall fox_toolbox main.py 通过render_snapshot 参数签名与 main.py 调用一致,复用 Database.get_stats/get_timeline_stats/get_sender_ranking/get_group_ranking/get_content_type_stats/get_table_count 现有方法PYTHONPATH=/workspace python3 -m pytest -q 与改动前基线一致(未引入回归)web_api.py 与新版 main.py 混用时注册崩溃:register_all_web_apis 恢复为 2 参数签名,初始化失败原因改由 context.fox_toolbox_db_error 透传,避免 takes 2 positional arguments but 3 were given 导致页面/API 全部未注册status/stats 接口的 db_status 携带具体连接错误(如 Can't connect to MySQL server),前端状态卡片与 dashboard 顶部横幅直接展示原因app.js 与 style.css 查询参数升级至 0.2.2PYTHONPATH=/workspace python3 -m pytest -q(212 passed / 71 skipped)python3 -m compileall fox_toolbox main.pynode --check pages/recorder/app.jsregister_all_web_apis(ctx, None) 2 参数签名注册 30 个 API 成功,status/stats 降级数据携带 MySQL 错误详情initialize() 中 Database.init() 连接失败会直接导致整个插件初始化失败,_register_web_apis() 不被调用,页面与所有 API 均未注册,前端整页空白。现在初始化失败后仍注册全部 Web API(db 传 None),页面可正常打开并展示降级状态status/stats 接口的 db_status 新增 error 字段携带具体连接错误(如 Can't connect to MySQL server),前端状态卡片与 dashboard 顶部错误横幅直接展示原因,便于排查配置app.js 与 style.css 查询参数升级至 0.2.1PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox main.pynode --check pages/recorder/app.jsregister_all_web_apis(ctx, None) 注册 30 个 API 成功,status/stats 返回降级数据并携带 MySQL 错误详情updateDbStatusCard 不再因 db_status 缺失而提前返回,缺失时显示 -- 并清除加载骨架;stats 响应缺少 db_status 时自动改用 status 接口兜底,避免卡片停留在骨架态DbExplorer.list_tables() 兼容 tuple 与 dict 两种游标行(此前默认游标返回 tuple,rows[0].keys() 抛 AttributeError)_ensure_limit 不再对 SHOW / DESCRIBE / DESC 追加 LIMIT(避免 MySQL 语法错误),并对已存在且超限的 LIMIT 数值钳制到 ABSOLUTE_MAX_ROWS 以内top: 46 / bottom: 2%),图例紧凑排布,消除图表下方大片空白app.js 与 style.css 查询参数升级至 0.2.0PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recordernode --check pages/recorder/app.jsfox_toolbox/db_explorer.py,仅允许 SELECT / SHOW / DESCRIBE / DESC 前缀语句;拦截 DROP、TRUNCATE、FLUSH、GRANT、REVOKE、ALTER/CREATE USER、RENAME TABLE、LOCK 与注释注入(--、/* */);自动追加 LIMIT 防止大表全量拉取;查询超时上限 15 秒/huli_record tables 命令,可在聊天中查看数据库业务表列表(自动跳过 _schema_meta 系统表)GET db/tables、GET db/schema?table=、GET db/data?table=&limit=&offset=、POST db/query(接收 sql 与 max_rows),共注册 26 个 APIauroraFlow 动画,页面背景呈现缓慢流动的极光渐变;遵循 prefers-reduced-motion 时自动关闭动画app.js 与 style.css 查询参数升级至 0.1.12LIKE '%grant%')误判为危险操作,同时保留对真实 DDL/DML 命令与注释注入的拦截PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recordernode --check pages/recorder/app.jscardFloat 无限浮动动画(含移动端变体),卡片只在首次入场时淡入一次,之后完全静止get_table_count() 查询失败改为返回 -1,_build_db_status_payload 不再先执行 ping() 前置判断,直接以表数量查询结果为准,避免因 ping 失败而永远拿不到表数量status 接口,默认复用 stats 响应里的 db_status;仅在 stats 请求失败时才用 status 接口兜底,减少一次冗余请求--app.js 与 style.css 查询参数升级至 0.1.11,确保 AstrBot 刷新后加载最新页面资源_safe_db_ping() 辅助函数,简化后端状态数据链路PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recordernode --check pages/recorder/app.jsstatus 接口兜底拉取表数量,不再完全依赖 stats 接口成功与否;若 stats 失败卡片依然能展示数据库连接状态12 张),连接失败时才显示“未连接”,移除了此前“运行中/表数量”的 3 秒轮换逻辑,避免反复刷新观感stats 接口的 running/table_count 与 status 接口的 database/table_count 两种返回结构app.js 与 style.css 查询参数升级至 0.1.10,确保 AstrBot 刷新后加载最新页面资源PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recorderstats 接口移除未使用的 plugin_status 计算,只保留轻量 db_status 数据,减少不必要的数据库探测和资源采集PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recorderstats 响应新增 db_status 字段,前端直接从同一条统计响应里更新数据库状态卡片,避免额外独立请求app.js 与 style.css 查询参数升级至 0.1.8,确保 AstrBot 刷新后加载最新页面资源PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recorderCHANGELOG.md 顶部重复空版本标题,保留每个版本的具体更新内容;同步更新 README.md 更新日志PYTHONPATH=/workspace python3 -m pytest -qpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox pages/recorderindex.html 为 app.js 与 style.css 增加 ?v=0.1.6 查询参数,强制刷新页面时拉取最新前端资源- 调整为“检测中 / – / 100 / – MB”,降低旧缓存与加载中的误判PYTHONPATH=/workspace python3 -m pytest -q:176 passed, 63 skippedpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolbox/proc 环境下资源卡片恒为 0.0 MB:sys_util 新增跨平台回退逻辑,进程内存支持 Windows GetProcessMemoryInfo,CPU 与运行时长支持 time.process_time() / time.monotonic() 回退stats 接口改为即使数据库统计失败也返回 plugin_status 与空统计数据,插件状态、健康度、内存占用可以独立显示PYTHONPATH=/workspace python3 -m pytest -q:176 passed, 63 skippedpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolboxstats 接口新增 plugin_status 字段,前端状态卡片改为直接读取 stats.plugin_status,首屏渲染与 30 秒刷新都复用同一条已验证可用的数据链路status 接口与 stats.plugin_status 现在共用同一份状态构造逻辑,避免两条链路行为不一致PYTHONPATH=/workspace python3 -m pytest -q:176 passed, 63 skippedpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolboxplugin/status 调整为插件内相对路径 status,后端新增 /{plugin_name}/status 主路由,并保留 /{plugin_name}/plugin/status 兼容旧路径bridge.apiGet() 调用插件内相对路径Plugin Pages 文档核对 register_web_api() 与 bridge.apiGet() 的路由规则-:前端不再把有效的 0 值当作空值处理,内存和 CPU 现在稳定显示 0.0 MB / 0.0%,接口失败时明确显示错误状态而不是单个 -plugin/status 容错:后端按指标分别采集数据库、内存、CPU、运行时长,单项失败时仍返回可显示结果;接口内部异常时返回兜底状态数据,避免整个卡片区失效astrbot_plugin_fox_toolbox 兼容包路径,恢复测试套件按项目包名导入PYTHONPATH=/workspace python3 -m pytest -q:176 passed, 63 skippedpython3 -m compileall fox_toolbox astrbot_plugin_fox_toolboxPYTHONPATH=/workspace python3 -c "import conftest, main; print('main import ok')"console.log 诊断日志定位 plugin/status API 失败原因,新增 clearStatusSkeletons() 安全函数兜底清除骨架屏success: true + 空数据,前端拿不到错误信息导致图表区域空白。现改为异常时返回 success: false,前端 catch 块显示”暂无数据”占位符和错误原因plugin/status API 失败时,catch 块只记日志不清 skeleton,导致卡片永远卡在加载动画。现 catch 块正确清除 skeleton 并显示”加载失败”clearStatusSkeletons() 安全函数,在 loadDashboardData 的 catch/finally 块中兜底清除骨架屏,确保无论 API 成功或失败,状态卡片都不会永远卡在加载动画0.0.11 升级至 0.1.0,遵循语义化版本规范——0.0.10 之后进入 0.1.x 系列_build_query_filter_from_dict 新增 order 白名单校验(仅允许 asc/desc)api_import_init 新增 file_size > 0 校验,chunk_size 钳位到 [1MB, 5MB]cleanup_expired_tasks 新增崩溃任务清理(2 小时未完成视为崩溃)Database.ping() 轻量探测数据库连通性,正常显示绿色「健康」,异常显示红色「异常」并附悬停提示GET /fox_toolbox/plugin/status,返回健康状态、数据库连接、CPU/内存/运行时长与 schema 版本fox_toolbox/sys_util.py:基于标准库读取进程资源(Linux 读取 /proc,macOS/BSD 回退 resource 模块),不引入第三方依赖auto-fit + minmax),从 5 列调整至可容纳 7 张卡片.stat-plain 样式类:状态/资源卡片禁用渐变透明文字,确保状态颜色与数值可读channel_message_count(后端统计接口与数据库查询此前已支持该字段,本次仅补充前端展示)prefers-reduced-motion 适配.card、图表容器 .chart-container、筛选区 .filter-section)的持续浮动动画,仅保留一次性入场动画,显著降低 backdrop-filter 持续合成开销background-attachment: fixed,消除移动端固定背景的滚动性能问题requestAnimationFrame),避免一次插入上百条卡片长时间阻塞主线程downloadExportFile 读取 export/download_data 响应时缺少 extractData(),导致 base64 字段始终取不到,≤50MB 的文件下载必定失败;已改用 extractData(result) 后再读取 data.base64/data.mimetype/data.filenametest_schema_version 测试断言过时:SCHEMA_VERSION 已升级至 3,测试断言仍为 2,已同步更新width=device-width, initial-scale=1.0),修复手机端默认缩放问题theme-color 和 apple-mobile-web-app-capable meta 标签,优化移动端浏览器体验overflow-x: auto + -webkit-overflow-scrolling: touch),保持表格完整性.card 浮动动画不生效:移除 contain: layout paint(与 translate 动画冲突导致大卡片浮动不可见),替换为 will-change: translatedetail-row 改为垂直排列,标签和值分两行显示get_timeline_stats 未返回 channel_count 字段,导致前端频道折线图始终为 0safeUrl() 函数,仅允许 http/https 协议,防止 javascript: 等恶意协议setOption 添加 notMerge: true,确保频道数据系列完整渲染|| 0 回退,防止 undefined 导致折线断裂.chart-container)、筛选区(.filter-section)现在与统计卡片、内容卡片一样拥有浮动动画效果cardFloat 动画统一应用于所有玻璃卡片元素(.card、.stat-card、.chart-container、.filter-section)prefers-reduced-motion 媒体查询同步更新,覆盖所有新增浮动元素media_downloader.py 的 KNOWN_EXTENSIONS 同步扩展,确保文件保存时正确识别扩展名LIKE '%Image%' 改为 FIND_IN_SET('Image', content_types)
LIKE '%Image%' 会同时匹配 Image 和 FileImage,导致图片计数偏高LIKE '%Video%' 会同时匹配 Video 和 FileVideo,导致视频计数偏高LIKE '%File%' 需要复杂的 NOT LIKE 排除链,容易遗漏FIND_IN_SET 精确匹配逗号分隔值,彻底解决子串误匹配问题LIKE '%At%' 会匹配 AtAll,改用 FIND_IN_SET('At') + FIND_IN_SET('AtAll')get_content_type_stats 和 get_platform_detail_stats 的 SQL 全部改用 FIND_IN_SETget_platform_detail_stats 新增 archive_count 和 code_count 返回字段updatePlatformDetailTable 同步渲染新增的压缩包和代码列content_types 数据库字段,记录每条消息包含的组件类型stats/content-types、stats/platformsmessage_str 现在会自动生成摘要(如 [图片]、[文件]、[视频]),方便搜索和展示content_types 列)MessageRecord 新增 content_types 字段content_types 字段models.py 中新增 DEPRECATED_TABLES 列表,用于管理需要清理的弃用表messages_fts(原 SQLite FTS5 虚拟表,迁移到 MySQL 后已无用)metadata.yaml 的 repo 字段添加 /tree/main 分支指定
master 分支,但本仓库使用 main,导致更新失败main 分支后,强制更新可正常工作database.py 导入 DEPRECATED_TABLES,init() 中新增 _cleanup_deprecated_tables() 调用get_timeline_stats 为 Python 端分组,不再使用 MySQL 日期函数showSectionError 调用,改为静默处理狐狸插件首个正式版本(基于 astrbot_plugin_message_recorder 重构)。
astrbot_plugin_message_recorder(消息记录器)更名为 astrbot_plugin_fox_toolbox(狐狸插件)message_recorder/ 目录更名为 fox_toolbox/[MessageRecorder] 变更为 [FoxToolbox]/message_recorder/api/ 变更为 /astrbot_plugin_fox_toolbox/backdrop-filter 实现半透明磨砂玻璃效果,配合渐变高光模拟玻璃折射backdrop-filter 的浏览器自动回退为不透明背景prefers-reduced-motion 偏好,自动禁用动画mysql_host、mysql_port、mysql_user、mysql_password、mysql_database)__init__.py_conf_schema.json 中 JSON 语法错误(hint 字段未转义双引号导致安装失败)webchat 平台声明register 导入get_timeline_stats SQL 查询在 MySQL 5.7 下兼容性问题
timestamp 列名添加反引号避免保留字冲突DIV 替代 / 确保整数除法(避免 DECIMAL 类型问题)GROUP BY / ORDER BY 使用完整表达式替代别名(避免 ONLY_FULL_GROUP_BY 模式冲突)db_path 属性和本地文件路径依赖keywords 字段(metadata.yaml)以下为原插件 astrbot_plugin_message_recorder 的变更记录,仅供参考。
MessageType 枚举ChannelBasedAdapter 频道型平台适配器message_recorder/ 子目录