Skip to content

Commit dcd40d5

Browse files
committed
platform 适配器
1 parent 6046cda commit dcd40d5

File tree

5 files changed

+181
-2
lines changed

5 files changed

+181
-2
lines changed

.vitepress/config.mjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,17 @@ export default defineConfig({
114114
},
115115
{
116116
text: '开发',
117-
base: '/dev',
117+
base: '/dev',
118118
collapsed: false,
119119
items: [
120120
{
121-
text: '几行代码实现一个插件',
121+
text: '插件基础开发',
122122
link: '/plugin'
123123
},
124+
{
125+
text: '接入平台适配器',
126+
link: '/plugin-platform-adapter'
127+
}
124128
]
125129
},
126130
{

dev/plugin-platform-adapter.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# 开发一个平台适配器
6+
7+
AstrBot 支持以插件的形式接入平台适配器,你可以自行接入 AstrBot 没有的平台。如飞书、钉钉甚至是哔哩哔哩私信、Minecraft。
8+
9+
我们以一个平台 `FakePlatform` 为例展开讲解。
10+
11+
首先,在插件目录下新增 `fake_platform_adapter.py``fake_platform_event.py` 文件。前者主要是平台适配器的实现,后者是平台事件的定义。
12+
13+
## 平台适配器
14+
15+
假设 FakePlatform 的客户端 SDK 是这样:
16+
17+
```py
18+
import asyncio
19+
20+
class FakeClient():
21+
'''模拟一个消息平台,这里 5 秒钟下发一个消息'''
22+
def __init__(self, token: str, username: str):
23+
self.token = token
24+
self.username = username
25+
# ...
26+
27+
async def start_polling(self):
28+
while True:
29+
await asyncio.sleep(5)
30+
await getattr(self, 'on_message_received')({
31+
'bot_id': '123',
32+
'content': '新消息',
33+
'username': 'zhangsan',
34+
'userid': '123',
35+
'message_id': 'asdhoashd',
36+
'group_id': 'group123',
37+
})
38+
39+
async def send_text(self, to: str, message: str):
40+
print('发了消息:', to, message)
41+
42+
async def send_image(self, to: str, image_path: str):
43+
print('发了消息:', to, image_path)
44+
```
45+
46+
我们创建 `fake_platform_adapter.py`
47+
48+
```py
49+
import asyncio
50+
51+
from astrbot.api.platform import Platform, AstrBotMessage, MessageMember, PlatformMetadata, MessageType
52+
from astrbot.api.event import MessageChain
53+
from astrbot.api.message_components import Plain, Image, Record
54+
from astrbot.core.platform.astr_message_event import MessageSesion
55+
from astrbot.api.platform import register_platform_adapter
56+
from astrbot import logger
57+
from .client import FakeClient
58+
from .fake_platform_event import FakePlatformEvent
59+
60+
61+
@register_platform_adapter("fake", "fake 适配器", default_config_tmpl={
62+
"token": "your_token",
63+
"username": "bot_username"
64+
})
65+
class FakePlatformAdapter(Platform):
66+
67+
def __init__(self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue) -> None:
68+
super().__init__(event_queue)
69+
self.config = platform_config
70+
self.settingss = platform_settings
71+
72+
async def send_by_session(self, session: MessageSesion, message_chain: MessageChain):
73+
await super().send_by_session(session, message_chain)
74+
75+
def meta(self) -> PlatformMetadata:
76+
return PlatformMetadata(
77+
"fake",
78+
"fake 适配器",
79+
)
80+
81+
async def run(self):
82+
83+
async def on_received(data):
84+
logger.info(data)
85+
abm = await self.convert_message(data=data) # 转换成 AstrBotMessage
86+
await self.handle_msg(abm)
87+
88+
self.client = FakeClient(self.config['token'], self.config['username'])
89+
self.client.on_message_received = on_received
90+
await self.client.start_polling() # 持续监听消息,这是个堵塞方法。
91+
92+
async def convert_message(self, data: dict) -> AstrBotMessage:
93+
abm = AstrBotMessage()
94+
abm.type = MessageType.GROUP_MESSAGE # 还有 friend_message,对应私聊。具体平台具体分析
95+
abm.group_id = data['group_id']
96+
abm.message_str = data['content']
97+
abm.sender = MessageMember(user_id=data['userid'], nickname=data['username'])
98+
abm.message = [Plain(text=data['content'])] # 消息链
99+
abm.raw_message = data
100+
abm.self_id = data['bot_id'],
101+
abm.session_id = data['userid']
102+
abm.message_id = data['message_id']
103+
104+
return abm
105+
106+
async def handle_msg(self, message: AstrBotMessage):
107+
message_event = FakePlatformEvent(
108+
message_str=message.message_str,
109+
message_obj=message,
110+
platform_meta=self.meta(),
111+
session_id=message.session_id,
112+
client=self.client
113+
)
114+
self.commit_event(message_event) # 提交事件到事件队列
115+
```
116+
117+
118+
`fake_platform_event.py`
119+
120+
```py
121+
from astrbot.api.event import AstrMessageEvent, MessageChain
122+
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
123+
from astrbot.api.message_components import Plain, Image
124+
from .client import FakeClient
125+
from astrbot.core.utils.io import download_image_by_url
126+
127+
class FakePlatformEvent(AstrMessageEvent):
128+
def __init__(self, message_str: str, message_obj: AstrBotMessage, platform_meta: PlatformMetadata, session_id: str, client: FakeClient):
129+
super().__init__(message_str, message_obj, platform_meta, session_id)
130+
self.client = client
131+
132+
async def send(self, message: MessageChain):
133+
for i in message.chain: # 遍历消息链
134+
if isinstance(i, Plain): # 如果是文字类型的
135+
await self.client.send_text(to=self.get_sender_id(), message=i.text)
136+
elif isinstance(i, Image): # 如果是图片类型的
137+
img_url = i.file
138+
img_path = ""
139+
# 下面的三个条件可以直接参考一下。
140+
if img_url.startswith("file:///"):
141+
img_path = img_url[8:]
142+
elif i.file and i.file.startswith("http"):
143+
img_path = await download_image_by_url(i.file)
144+
else:
145+
img_path = img_url
146+
147+
# 请善于 Debug!
148+
149+
await self.client.send_image(to=self.get_sender_id(), image_path=img_path)
150+
```
151+
152+
最后,main.py 只需这样,在初始化的时候导入 fake_platform_adapter 模块。装饰器会自动注册。
153+
154+
```py
155+
from astrbot.api.star import Context, Star, register
156+
@register("helloworld", "Your Name", "一个简单的 Hello World 插件", "1.0.0")
157+
class MyPlugin(Star):
158+
def __init__(self, context: Context):
159+
from .fake_platform_adapter import FakePlatformAdapter # noqa
160+
```
161+
162+
搞好后,运行 AstrBot:
163+
164+
![](../source/images/plugin-platform-adapter/QQ_1738155926221.png)
165+
166+
这里出现了我们创建的 fake。
167+
168+
![](../source/images/plugin-platform-adapter/QQ_1738155982211.png)
169+
170+
启动后,可以看到正常工作:
171+
172+
![](../source/images/plugin-platform-adapter/QQ_1738156166893.png)
173+
174+
175+
有任何疑问欢迎加群询问~
114 KB
Loading
32.5 KB
Loading
111 KB
Loading

0 commit comments

Comments
 (0)