Skip to content

Comments

feat: add plugin load&unload hook#5331

Merged
Soulter merged 5 commits intoAstrBotDevs:masterfrom
PyuraMazo:feat/add-plugin-load-unload-hook
Feb 23, 2026
Merged

feat: add plugin load&unload hook#5331
Soulter merged 5 commits intoAstrBotDevs:masterfrom
PyuraMazo:feat/add-plugin-load-unload-hook

Conversation

@PyuraMazo
Copy link
Contributor

@PyuraMazo PyuraMazo commented Feb 22, 2026

添加了两个事件钩子:插件加载和插件卸载。这两个钩子达到了监测插件生命周期的效果,为其它插件对插件列表变化的处理提供了更方便简洁的获取方式。
目前可以想到的作用包括:

  • 提示机器人使用者有哪些插件的变更,以免造成尴尬的情况
  • 对某类特定属性的插件进行特殊化管理

本来还打算添加一个插件配置更新的钩子,但是这个钩子可以被上述两个钩子结合实现相同甚至更广泛的效果,因为通过增删的插件元数据很容易获取到变动插件的配置内容,甚至更多其它有用的效果。

Modifications / 改动点

新增功能:

  • 新增了两个插件生命周期钩子事件:
    • EventType.OnPluginLoadedEvent - 当插件加载完成时触发
    • EventType.OnPluginUnloadedEvent - 当插件卸载完成时触发

修改的核心文件:

  1. astrbot/core/star/star_handler.py
    - 在 EventType 枚举中添加了两个新事件类型
    - 在 get_handlers_by_event_type 方法的过滤逻辑中添加了新事件类型的白名单
  2. astrbot/core/star/register/star_handler.py
    - 新增 register_on_plugin_loaded() 装饰器
    - 新增 register_on_plugin_unloaded() 装饰器
    - 两个装饰器都接收 metadata(StarMetadata)参数
  3. astrbot/core/star/star_manager.py
    - 在插件加载完成后触发 OnPluginLoadedEvent 事件
    - 在插件卸载完成前触发 OnPluginUnloadedEvent 事件
  4. astrbot/api/event/filter/init.py
    - 导入并导出 on_plugin_loaded 和 on_plugin_unloaded 装饰器
  5. astrbot/core/star/register/init.py
    - 导出 register_on_plugin_loaded 和 register_on_plugin_unloaded
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

AstrBot启动时:
image

重载插件时:
image


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

为插件加载和卸载添加插件生命周期钩子事件,并提供装饰器,便于其他插件对这些事件作出响应。

新特性:

  • 引入 OnPluginLoadedEventOnPluginUnloadedEvent 事件类型,用于表示插件加载和卸载的生命周期阶段。
  • 添加 register_on_plugin_loadedregister_on_plugin_unloaded 装饰器,并通过公共事件过滤器 API 重新导出它们,以便插件可以为这些生命周期事件注册处理程序。
Original summary in English

Summary by Sourcery

Add plugin lifecycle hook events for plugin load and unload and expose decorators for other plugins to react to these events.

New Features:

  • Introduce OnPluginLoadedEvent and OnPluginUnloadedEvent event types to represent plugin load and unload lifecycle stages.
  • Add register_on_plugin_loaded and register_on_plugin_unloaded decorators and re-export them through the public event filter API so plugins can register handlers for these lifecycle events.

@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Feb 22, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @PyuraMazo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在通过引入插件加载和卸载的生命周期钩子,增强 AstrBot 的插件管理能力。这些钩子为其他插件提供了监听和响应插件列表变化的能力,从而可以更方便地实现对插件状态的监控、通知用户插件变更,以及对特定属性插件进行特殊化管理。这一改动提升了系统的可扩展性和灵活性,而不会引入破坏性变更。

Highlights

  • 新增插件生命周期钩子: 引入了两个新的事件钩子:EventType.OnPluginLoadedEventEventType.OnPluginUnloadedEvent,用于监测插件的加载和卸载生命周期。
  • 新增装饰器: 添加了 register_on_plugin_loadedregister_on_plugin_unloaded 装饰器,允许其他插件方便地注册对这些生命周期事件的监听。
  • 事件触发机制: 在插件加载完成后触发 OnPluginLoadedEvent,并在插件卸载完成前触发 OnPluginUnloadedEvent,确保了事件的及时性。
  • 事件类型和处理逻辑更新: 更新了 EventType 枚举以包含新的事件类型,并修改了 get_handlers_by_event_type 方法的过滤逻辑,以正确处理这些新事件。
Changelog
  • astrbot/api/event/filter/init.py
    • 导入并导出了 on_plugin_loadedon_plugin_unloaded 装饰器,使其可在 astrbot.api.event.filter 模块中直接使用。
  • astrbot/core/star/register/init.py
    • 导出了 register_on_plugin_loadedregister_on_plugin_unloaded 函数,使其可在 astrbot.core.star.register 模块中被外部引用。
  • astrbot/core/star/register/star_handler.py
    • 新增了 register_on_plugin_loaded 装饰器,用于注册插件加载完成时的处理函数。
    • 新增了 register_on_plugin_unloaded 装饰器,用于注册插件卸载完成时的处理函数。
  • astrbot/core/star/star_handler.py
    • EventType 枚举中添加了 OnPluginLoadedEventOnPluginUnloadedEvent 两个新的事件类型。
    • 更新了 get_handlers_by_event_type 方法的过滤逻辑,将 OnPluginLoadedEventOnPluginUnloadedEvent 加入白名单,确保这些事件的处理函数可以被正确获取。
  • astrbot/core/star/star_manager.py
    • 在插件加载成功后,增加了触发 OnPluginLoadedEvent 的逻辑,并遍历执行所有注册的处理器。
    • 在插件终止(卸载)前,增加了触发 OnPluginUnloadedEvent 的逻辑,并遍历执行所有注册的处理器。
    • 导入了 EventType 以便在 star_manager.py 中使用新的事件类型。
Activity
  • 此拉取请求自创建以来没有人工活动。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我在这里给出了一些整体性的反馈:

  • star_manager.py 中用于插件加载/卸载钩子的处理器调用逻辑几乎完全相同;可以考虑抽取一个小的辅助函数(例如 _dispatch_plugin_lifecycle_event(event_type, metadata))来避免重复,并让未来新增的生命周期钩子保持一致。
  • PR 描述里的注释提到在插件卸载“之前”触发卸载钩子,但在 _terminate_plugin 中,钩子是在调用 terminate 之后才被触发;最好在代码或 docstring 中把这种调用顺序明确记录下来,以便插件作者知道他们看到的是终止前还是终止后的插件状态。
面向 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- The handler invocation logic for plugin load/unload hooks in `star_manager.py` is almost identical; consider extracting a small helper (e.g. `_dispatch_plugin_lifecycle_event(event_type, metadata)`) to avoid duplication and keep future lifecycle hooks consistent.
- The comment in the PR description mentions firing the unload hook "before" plugin unload, but in `_terminate_plugin` the hook is dispatched after `terminate` is called; it would be good to explicitly document this ordering in the code/docstring so plugin authors know whether they see the plugin pre- or post-termination.

Sourcery 对开源项目是免费的——如果你觉得我们的代码审查有帮助,欢迎分享给更多人 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈来改进今后的代码审查。
Original comment in English

Hey - I've left some high level feedback:

  • The handler invocation logic for plugin load/unload hooks in star_manager.py is almost identical; consider extracting a small helper (e.g. _dispatch_plugin_lifecycle_event(event_type, metadata)) to avoid duplication and keep future lifecycle hooks consistent.
  • The comment in the PR description mentions firing the unload hook "before" plugin unload, but in _terminate_plugin the hook is dispatched after terminate is called; it would be good to explicitly document this ordering in the code/docstring so plugin authors know whether they see the plugin pre- or post-termination.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The handler invocation logic for plugin load/unload hooks in `star_manager.py` is almost identical; consider extracting a small helper (e.g. `_dispatch_plugin_lifecycle_event(event_type, metadata)`) to avoid duplication and keep future lifecycle hooks consistent.
- The comment in the PR description mentions firing the unload hook "before" plugin unload, but in `_terminate_plugin` the hook is dispatched after `terminate` is called; it would be good to explicitly document this ordering in the code/docstring so plugin authors know whether they see the plugin pre- or post-termination.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added the feature:plugin The bug / feature is about AstrBot plugin system. label Feb 22, 2026
@dosubot
Copy link

dosubot bot commented Feb 22, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces two key event hooks, OnPluginLoadedEvent and OnPluginUnloadedEvent, for plugin lifecycle management, enabling developers to monitor plugin list changes. However, the current implementation of triggering these hooks in star_manager.py is vulnerable to Denial of Service (DoS) attacks, as malicious plugins could block the entire plugin management system or crash the bot process. It is recommended to execute these hooks concurrently with a timeout and ensure all exceptions are caught to maintain system stability. Additionally, consider adding type overloading in StarHandlerRegistry and optimizing error logging for better observability.

Comment on lines +779 to +786
for handler in handlers:
try:
logger.info(
f"hook(on_plugin_loaded) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}",
)
await handler.handler(metadata)
except Exception:
logger.error(traceback.format_exc())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The newly introduced plugin lifecycle hooks are executed sequentially and awaited while holding the global plugin management lock (_pm_lock). This creates a Denial of Service (DoS) vulnerability, as a malicious or poorly implemented plugin could indefinitely block the entire plugin system by providing a hook that never returns. Additionally, when triggering plugin load hooks, it's recommended to include the plugin's name (metadata.name) in the logs and explicitly state which plugin's hook failed during exception handling to improve observability and troubleshooting.

                        logger.info(
                            f"hook(on_plugin_loaded): plugin {metadata.name} loaded -> handled by {star_map[handler.handler_module_path].name}.{handler.handler_name}",
                        )
                        await handler.handler(metadata)
                    except Exception as e:
                        logger.error(f"Error in on_plugin_loaded hook of plugin {star_map[handler.handler_module_path].name}: {e}")
                        logger.error(traceback.format_exc())

Comment on lines +1179 to +1186
for handler in handlers:
try:
logger.info(
f"hook(on_plugin_unloaded) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}",
)
await handler.handler(star_metadata)
except Exception:
logger.error(traceback.format_exc())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

In the _terminate_plugin method, the hook execution is wrapped in a try...except Exception block, which does not catch BaseException types like SystemExit. A malicious plugin could register an on_plugin_unloaded hook that raises SystemExit, causing the entire bot process to terminate during a plugin reload or uninstallation. It is recommended to catch BaseException here or execute hooks in a way that they cannot crash the main process.

@Soulter Soulter changed the title fear:add plugin load&unload hook fear: add plugin load&unload hook Feb 23, 2026
@PyuraMazo PyuraMazo changed the title fear: add plugin load&unload hook feat: add plugin load&unload hook Feb 23, 2026
@Soulter Soulter merged commit 28bfb3b into AstrBotDevs:master Feb 23, 2026
5 checks passed
astrbot-doc-agent bot pushed a commit to AstrBotDevs/AstrBot-docs that referenced this pull request Feb 23, 2026
@astrbot-doc-agent
Copy link

Generated docs update PR (pending manual review):
AstrBotDevs/AstrBot-docs#142
Trigger: PR merged


AI change summary:

  • dev/star/guides/listen-message-event.md 的“事件钩子”章节新增插件生命周期钩子文档。
  • 新增 on_plugin_loaded 钩子:当插件加载完成时触发,可获取 StarMetadata
  • 新增 on_plugin_unloaded 钩子:当插件卸载完成时触发,可获取 StarMetadata
  • i18n:中英文文档均已更新。

Experimental bot notice:

  • This output is generated by AstrBot-Doc-Agent for review only.
  • It does not represent the final documentation form.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:plugin The bug / feature is about AstrBot plugin system. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants