Skip to content

Commit 6f53164

Browse files
committed
feat: 为插件添加统一webhook
1 parent acd8b56 commit 6f53164

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

astrbot/core/star/base.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import logging
4+
from collections.abc import Awaitable, Callable
45
from typing import Any, Protocol
56

67
from astrbot.core import html_renderer
@@ -21,6 +22,14 @@ class Star(CommandParserMixin, PluginKVStoreMixin):
2122
class _ContextLike(Protocol):
2223
def get_config(self, umo: str | None = None) -> Any: ...
2324

25+
def register_unified_webhook(
26+
self,
27+
webhook_uuid: str,
28+
view_handler: Callable[..., Awaitable[Any]],
29+
methods: list[str] | None = None,
30+
desc: str = "",
31+
) -> None: ...
32+
2433
def __init__(self, context: _ContextLike, config: dict | None = None) -> None:
2534
self.context = context
2635

@@ -77,6 +86,28 @@ async def html_render(
7786
options=options,
7887
)
7988

89+
def register_unified_webhook(
90+
self,
91+
webhook_uuid: str,
92+
view_handler: Callable[..., Awaitable[Any]],
93+
methods: list[str] | None = None,
94+
desc: str = "",
95+
) -> None:
96+
"""注册统一 Webhook 回调。
97+
98+
插件可以通过该方法注册回调,Dashboard 会通过
99+
/api/plug/webhook/<webhook_uuid> 转发到对应处理函数。
100+
"""
101+
register = getattr(self.context, "register_unified_webhook", None)
102+
if not callable(register):
103+
raise RuntimeError("Context does not support unified webhook registration")
104+
register(
105+
webhook_uuid=webhook_uuid,
106+
view_handler=view_handler,
107+
methods=methods,
108+
desc=desc,
109+
)
110+
80111
async def initialize(self) -> None:
81112
"""当插件被激活时会调用这个方法"""
82113

astrbot/core/star/context.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class Context:
5757
"""暴露给插件的接口上下文。"""
5858

5959
registered_web_apis: list = []
60+
registered_unified_webhooks: dict[
61+
str, tuple[Callable[..., Awaitable[Any]], list[str], str]
62+
] = {}
6063

6164
# 向后兼容的变量
6265
_register_tasks: list[Awaitable] = []
@@ -519,6 +522,31 @@ def register_web_api(
519522
return
520523
self.registered_web_apis.append((route, view_handler, methods, desc))
521524

525+
def register_unified_webhook(
526+
self,
527+
webhook_uuid: str,
528+
view_handler: Callable[..., Awaitable[Any]],
529+
methods: list[str] | None = None,
530+
desc: str = "",
531+
) -> None:
532+
"""注册统一 Webhook 回调。
533+
534+
Args:
535+
webhook_uuid: Webhook 唯一标识。
536+
view_handler: 异步视图处理函数。
537+
methods: HTTP 方法列表,默认 ["GET", "POST"]。
538+
desc: 回调描述。
539+
540+
Note:
541+
如果相同 webhook_uuid 已注册,会覆盖原有回调。
542+
"""
543+
normalized_methods = [method.upper() for method in (methods or ["GET", "POST"])]
544+
self.registered_unified_webhooks[webhook_uuid] = (
545+
view_handler,
546+
normalized_methods,
547+
desc,
548+
)
549+
522550
"""
523551
以下的方法已经不推荐使用。请从 AstrBot 文档查看更好的注册方式。
524552
"""

astrbot/dashboard/server.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ def __init__(
145145
view_func=self.srv_plug_route,
146146
methods=["GET", "POST"],
147147
)
148+
self.app.add_url_rule(
149+
"/api/plug/webhook/<webhook_uuid>",
150+
view_func=self.srv_plug_webhook_route,
151+
methods=["GET", "POST"],
152+
)
148153

149154
self.shutdown_event = shutdown_event
150155

@@ -159,6 +164,21 @@ async def srv_plug_route(self, subpath, *args, **kwargs):
159164
return await view_handler(*args, **kwargs)
160165
return jsonify(Response().error("未找到该路由").__dict__)
161166

167+
async def srv_plug_webhook_route(self, webhook_uuid, *args, **kwargs):
168+
"""插件统一 Webhook 路由"""
169+
registered_unified_webhooks = (
170+
self.core_lifecycle.star_context.registered_unified_webhooks
171+
)
172+
callback = registered_unified_webhooks.get(webhook_uuid)
173+
if not callback:
174+
return jsonify(Response().error("未找到对应 webhook").__dict__), 404
175+
176+
view_handler, methods, _ = callback
177+
if request.method not in methods:
178+
return jsonify(Response().error("请求方法不被允许").__dict__), 405
179+
180+
return await view_handler(*args, **kwargs)
181+
162182
async def auth_middleware(self):
163183
if not request.path.startswith("/api"):
164184
return None
@@ -199,6 +219,7 @@ async def auth_middleware(self):
199219
allowed_endpoints = [
200220
"/api/auth/login",
201221
"/api/file",
222+
"/api/plug/webhook",
202223
"/api/platform/webhook",
203224
"/api/stat/start-time",
204225
"/api/backup/download", # 备份下载使用 URL 参数传递 token

0 commit comments

Comments
 (0)