Conversation
Updated command descriptions and added random speaker plugin details.
Refactor bot initialization and configuration loading. Removed group_checker function and adjusted logging setup.
There was a problem hiding this comment.
Pull request overview
This PR adds a new “随机发言” plugin that periodically generates proactive group-chat replies using an OpenAI-compatible API, and updates bot startup/config handling plus README documentation.
Changes:
- Added
random_speakerplugin with message caching, activity-based probability, and APScheduler-based rescheduling. - Refactored
bot.pyto load configuration fromconfig.jsonand load all plugins fromnahida_bot/plugins. - Updated
README.mdwith random speaker documentation and other formatting/content edits.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
nahida_bot/plugins/random_speaker.py |
New plugin implementing random proactive replies, state persistence for per-group enable switch, and dynamic rescheduling. |
bot.py |
Startup flow changed to JSON-based config loading, plugin loading behavior adjusted, and permission init behavior modified. |
README.md |
Added random speaker documentation; modified existing command/config/license text and formatting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| async def cache_message_listener(event: GroupMessageEvent): | ||
| """缓存群消息""" | ||
| add_message_to_cache(event) |
There was a problem hiding this comment.
on_message() 会匹配私聊/群聊等所有 MessageEvent,但这个 handler 直接把 event 当作群事件处理并访问 event.group_id,私聊触发时会抛异常。建议在 matcher 上限制仅处理群消息,或在 handler 内先 isinstance(event, GroupMessageEvent) 判断再缓存。
| async def cache_message_listener(event: GroupMessageEvent): | |
| """缓存群消息""" | |
| add_message_to_cache(event) | |
| async def cache_message_listener(event: MessageEvent): | |
| """缓存群消息""" | |
| if isinstance(event, GroupMessageEvent): | |
| add_message_to_cache(event) |
| json_config_path = getattr(driver.config, "json_config_path", "config.json") | ||
|
|
||
| # Set configuration to nonebot driver | ||
| # Convert all config fields to uppercase for nonebot compatibility | ||
| driver.config = merge_pydantic_models(driver.config, app_config.core) | ||
| logger.info(f"Loading config from: {json_config_path}") | ||
|
|
||
| # Register adapters after custom config is loaded | ||
| driver.register_adapter(OnebotAdapter) | ||
| with open(json_config_path, "r", encoding="utf-8") as f: |
There was a problem hiding this comment.
这里改为读取 config.json 并直接写入 driver.config,但仓库内多个插件(如 plugins/openai_bot、plugins/pixiv 等)在 import 时调用 nahida_bot.config.get_config();如果不再调用 init_config() 初始化全局配置,会直接抛 RuntimeError 导致启动失败。建议要么恢复/保留 YAML 配置初始化流程并同步到 driver.config,要么同步迁移所有依赖 get_config() 的插件。
nahida_bot/plugins/random_speaker.py
Outdated
| # 每条消息都触发间隔缩短(不再限流) | ||
| asyncio.create_task(try_shorten_interval()) |
There was a problem hiding this comment.
每条消息都会 asyncio.create_task(try_shorten_interval()),活跃群里会快速堆积大量并发任务并频繁 reschedule_job,可能造成 event loop 压力和调度抖动。建议做去抖/合并(例如同一时刻只允许一个 shorten 任务运行,或设置最小重调度间隔)。
| 由于 pixiv 可能会有 reCAPTCHA 验证,因此你需要修改 `gppt` 包中的一个 `timeout` | ||
| 参数。具体参考 [gppt issue #183](https://github.com/eggplants/get-pixivpy-token/issues/183)。 | ||
| 参数。具体参考[gppt 问题 #183](https://github.com/eggplants/get-pixivpy-token/issues/183)。 |
There was a problem hiding this comment.
README 的配置章节仍说明“配置文件已经全面迁移到 config.yaml”,但本 PR 的 bot.py 改为读取 config.json 并写入 driver.config。建议统一:要么更新 README 说明当前实际读取的配置文件与键名,要么恢复代码以 config.yaml 为主。
| ## 注意事项 | ||
|
|
||
| - 需要正确配置 OpenAI 相关参数(`OPENAI_API_URL`、`OPENAI_API_TOKEN`、`OPENAI_MODEL_NAME`),否则无法生成回复。 | ||
| - 机器人只会监听和缓存开启了本功能的群的消息,未开启的群不受影响。 |
There was a problem hiding this comment.
这里写到“机器人只会监听和缓存开启了本功能的群的消息,未开启的群不受影响”,但当前实现会缓存所有群消息并触发动态缩短逻辑。建议修正实现或调整这段说明,避免用户误解。
| - 机器人只会监听和缓存开启了本功能的群的消息,未开启的群不受影响。 | |
| - 机器人会监听所有群消息用于缓存和动态缩短逻辑,但只会在开启了本功能的群中发送随机发言;未开启的群不会被主动打扰。 |
| 此外,你也可以使用根目录的 `get_token.py` 来获取 `token`。 | ||
|
|
||
| ## License | ||
| ##许可证 |
There was a problem hiding this comment.
##许可证 缺少空格会导致 Markdown 标题不规范/可能无法被部分渲染器识别为二级标题。建议改回 ## License 或 ## 许可证(带空格)。
| ##许可证 | |
| ## 许可证 |
|
|
||
| def add_message_to_cache(event: GroupMessageEvent): | ||
| """将群消息加入缓存,并处理动态缩短间隔""" | ||
| group_id = str(event.group_id) |
There was a problem hiding this comment.
当前对所有群消息无条件缓存(并会进一步影响活跃判断/动态缩短);如果希望“未开启的群不受影响/只缓存开启群”(README 也这么写),建议在这里按 is_group_enabled(group_id) 过滤掉未启用群的消息。
| group_id = str(event.group_id) | |
| group_id = str(event.group_id) | |
| # 仅对已启用随机发言的群进行缓存与活跃度统计 | |
| if not is_group_enabled(group_id): | |
| logger.debug(f"群 {group_id} 未启用随机发言,本条消息不参与缓存与统计") | |
| return |
| # Initialize permission system with database path | ||
| permission_db_path = os.path.join(full_data_dir, "permission.db") | ||
| permission.init(permission_db_path) | ||
| permission.init() |
There was a problem hiding this comment.
permission.init() 现在不再传入基于 data_dir 的数据库路径,会退回默认的相对路径 data/permission.db,从而忽略用户配置的数据目录(也可能导致测试/部署环境下权限库写到意外位置)。建议用 full_data_dir 组装 db path 并传入 permission.init(db_path)。
| permission.init() | |
| permission_db_path = os.path.join(full_data_dir, "permission.db") | |
| permission.init(permission_db_path) |
| ### `OpenAI` | ||
|
|
||
| - `!prompt <prompt>`: 设置机器人的 `prompt`。该设置在当前会话生效。 | ||
| :设置机器人的``。该设置在当前会话生效。 |
There was a problem hiding this comment.
这一行把 !prompt <prompt> 的命令说明删坏了:现在只剩“:设置机器人的``”,既缺少命令名也缺少 prompt 字段,且 Markdown 行首不再是列表项。建议恢复为原来的命令说明格式(与同章节其他命令一致)。
| :设置机器人的``。该设置在当前会话生效。 | |
| - `!prompt <prompt>`: 设置机器人的 `prompt`。该设置在当前会话生效。 |
nahida_bot/plugins/random_speaker.py
Outdated
| # 获取所有启用了该功能的群 | ||
| target_groups = [gid for gid, enabled in enabled_groups.items() if enabled] | ||
| logger.info(f"候选群列表: {target_groups}") | ||
|
|
||
| if not target_groups: | ||
| logger.info("没有启用随机发言的群,重新调度") |
There was a problem hiding this comment.
注释/is_group_enabled() 表示“默认所有群启用”,但这里的 target_groups 只来自 enabled_groups 中显式记录为 True 的群;当 state 为空(常见初始状态)时会导致没有候选群、任务永远不会发言。建议把候选群来源改为“已缓存消息的群”或从 bot 拉取群列表,并对每个群用 is_group_enabled() 过滤。
| # 获取所有启用了该功能的群 | |
| target_groups = [gid for gid, enabled in enabled_groups.items() if enabled] | |
| logger.info(f"候选群列表: {target_groups}") | |
| if not target_groups: | |
| logger.info("没有启用随机发言的群,重新调度") | |
| # 获取候选群:使用已缓存消息的群,再通过 is_group_enabled 过滤 | |
| try: | |
| candidate_groups = list(message_cache.keys()) | |
| except NameError: | |
| candidate_groups = [] | |
| logger.info(f"缓存候选群列表: {candidate_groups}") | |
| # 按启用状态过滤(默认所有群启用,除非显式关闭) | |
| target_groups = [gid for gid in candidate_groups if is_group_enabled(gid)] | |
| logger.info(f"最终启用随机发言的群列表: {target_groups}") | |
| if not target_groups: | |
| logger.info("没有可用的启用随机发言的群(或尚未缓存任何群消息),重新调度") |
pls read README.md