nonebot-plugin-aigfm-peer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Funny1Potato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: nonebot-plugin-aigfm-peer
3
+ Version: 0.1.0
4
+ Summary: 跨bot通信代理插件:捕获本bot插件输出推送给主bot,并支持远程插件调用
5
+ License-Expression: MIT
6
+ Requires-Python: <4.0,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: nonebot2>=2.3.0
10
+ Requires-Dist: nonebot-adapter-onebot>=2.2.0
11
+ Requires-Dist: httpx>=0.24.0
12
+ Dynamic: license-file
13
+
14
+ # nonebot-plugin-aigfm-peer
15
+
16
+ 跨 bot 通信代理插件,安装到其它 bot 上,配合主插件 [nonebot-plugin-aigf-master](https://github.com/Funny1Potato/nonebot-plugin-aigf-master) 使用。
17
+
18
+ ## 📖 介绍
19
+
20
+ - **捕获输出并推送**:拦截本 bot 所有插件的输出(文本/图片),通过 HTTP 推送给主 bot(aigf-master),进入其 LLM 上下文(标注 `[bot名]`)
21
+ - **远程插件调用**:提供 `/peer/invoke` 端点,接收主 bot 的远程命令调用,用 synthetic event 执行本 bot 的本地插件
22
+ - **命令推送**:启动时扫描本 bot 已注册的 `on_command` 命令,随推送告知主 bot,让 LLM 了解可调用命令
23
+
24
+ ## 💿 安装
25
+
26
+ ```bash
27
+ pip install nonebot-plugin-aigfm-peer
28
+ ```
29
+
30
+ 在 `pyproject.toml` 中添加:
31
+
32
+ ```toml
33
+ [tool.nonebot]
34
+ plugins = ["nonebot-plugin-aigfm-peer"]
35
+ ```
36
+
37
+ ## 配置
38
+
39
+ 在 `.env` 中添加:
40
+
41
+ ```env
42
+ # 主插件(Bot A)的 HTTP 端口
43
+ AIGFM_PEER_PUSH_PORT=14514
44
+ # 共享 token(与主插件 AIGFM_PEER_BOTS 中对应项一致)
45
+ AIGFM_PEER_TOKEN="shared_secret"
46
+ # 本 bot 名称(与主插件 AIGFM_PEER_BOTS 的 name 对应)
47
+ AIGFM_PEER_BOT_NAME="botB"
48
+ # 要捕获输出的插件名列表,为空则捕获所有
49
+ AIGFM_PEER_CAPTURE_PLUGINS=[]
50
+ ```
51
+
52
+ ## 与主插件配合
53
+
54
+ 1. 在主插件 `.env` 配置 `AIGFM_PEER_BOTS`(含本 bot 的 name/port/token)
55
+ 2. 两端 token 必须一致
56
+ 3. 主插件与 peer 插件的前缀(`COMMAND_START`)各自独立配置,远程调用时按各 bot 的前缀自动适配
57
+
58
+ ## License
59
+
60
+ MIT
@@ -0,0 +1,47 @@
1
+ # nonebot-plugin-aigfm-peer
2
+
3
+ 跨 bot 通信代理插件,安装到其它 bot 上,配合主插件 [nonebot-plugin-aigf-master](https://github.com/Funny1Potato/nonebot-plugin-aigf-master) 使用。
4
+
5
+ ## 📖 介绍
6
+
7
+ - **捕获输出并推送**:拦截本 bot 所有插件的输出(文本/图片),通过 HTTP 推送给主 bot(aigf-master),进入其 LLM 上下文(标注 `[bot名]`)
8
+ - **远程插件调用**:提供 `/peer/invoke` 端点,接收主 bot 的远程命令调用,用 synthetic event 执行本 bot 的本地插件
9
+ - **命令推送**:启动时扫描本 bot 已注册的 `on_command` 命令,随推送告知主 bot,让 LLM 了解可调用命令
10
+
11
+ ## 💿 安装
12
+
13
+ ```bash
14
+ pip install nonebot-plugin-aigfm-peer
15
+ ```
16
+
17
+ 在 `pyproject.toml` 中添加:
18
+
19
+ ```toml
20
+ [tool.nonebot]
21
+ plugins = ["nonebot-plugin-aigfm-peer"]
22
+ ```
23
+
24
+ ## 配置
25
+
26
+ 在 `.env` 中添加:
27
+
28
+ ```env
29
+ # 主插件(Bot A)的 HTTP 端口
30
+ AIGFM_PEER_PUSH_PORT=14514
31
+ # 共享 token(与主插件 AIGFM_PEER_BOTS 中对应项一致)
32
+ AIGFM_PEER_TOKEN="shared_secret"
33
+ # 本 bot 名称(与主插件 AIGFM_PEER_BOTS 的 name 对应)
34
+ AIGFM_PEER_BOT_NAME="botB"
35
+ # 要捕获输出的插件名列表,为空则捕获所有
36
+ AIGFM_PEER_CAPTURE_PLUGINS=[]
37
+ ```
38
+
39
+ ## 与主插件配合
40
+
41
+ 1. 在主插件 `.env` 配置 `AIGFM_PEER_BOTS`(含本 bot 的 name/port/token)
42
+ 2. 两端 token 必须一致
43
+ 3. 主插件与 peer 插件的前缀(`COMMAND_START`)各自独立配置,远程调用时按各 bot 的前缀自动适配
44
+
45
+ ## License
46
+
47
+ MIT
@@ -0,0 +1,283 @@
1
+ """nonebot-plugin-aigfm-peer — 跨 bot 通信代理(安装在其它 bot 上)
2
+
3
+ 职责:
4
+ 1. 捕获本 bot 所有插件输出(文本/图片),通过 HTTP 推送给 Bot A(aigf_manager)
5
+ 2. 提供 POST /peer/invoke 端点,接收 Bot A 的远程命令调用,用 synthetic event 执行本地插件
6
+ """
7
+
8
+ from datetime import datetime
9
+
10
+ import asyncio
11
+ import httpx
12
+ from nonebot import Bot, get_bot, get_driver, logger
13
+ from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, MessageSegment
14
+ from nonebot.internal.matcher import current_matcher
15
+ from nonebot.matcher import matchers
16
+ from nonebot.message import handle_event
17
+ from nonebot.plugin import PluginMetadata
18
+ from nonebot.rule import CommandRule
19
+
20
+ from .config import PluginConfig, plugin_config
21
+
22
+ __plugin_name__ = "nonebot-plugin-aigfm-peer"
23
+
24
+ __plugin_meta__ = PluginMetadata(
25
+ name="aigfm-peer", description="跨 bot 通信代理插件:捕获本 bot 插件输出推送给主 bot,并支持远程插件调用",
26
+ usage="安装到其它 bot 后自动工作;配置 AIGFM_PEER_PUSH_PORT / AIGFM_PEER_TOKEN / AIGFM_PEER_BOT_NAME",
27
+ type="application",
28
+ config=PluginConfig, supported_adapters={"~onebot.v11"},
29
+ homepage="https://github.com/Funny1Potato/nonebot-plugin-aigf",
30
+ extra={"author": "Funny1Potato"},
31
+ )
32
+
33
+ _invoke_tasks: set = set()
34
+ _commands_cache: list[dict] = []
35
+
36
+
37
+ def _scan_commands() -> list[dict]:
38
+ """扫描本 bot 已注册的 on_command 命令,供 Bot A 了解可用命令"""
39
+ commands: list[dict] = []
40
+ seen = set()
41
+ for priority, matcher_list in matchers.items():
42
+ for matcher_cls in matcher_list:
43
+ try:
44
+ rule = matcher_cls.rule
45
+ if not rule or not rule.checkers:
46
+ continue
47
+ for checker in rule.checkers:
48
+ if isinstance(checker.call, CommandRule):
49
+ cmd_rule = checker.call
50
+ if not cmd_rule.cmds:
51
+ continue
52
+ main_cmd = cmd_rule.cmds[0]
53
+ name = main_cmd[0] if main_cmd else ""
54
+ if not name or name in seen:
55
+ continue
56
+ seen.add(name)
57
+ description = ""
58
+ if matcher_cls.plugin and hasattr(matcher_cls.plugin, "metadata"):
59
+ meta = matcher_cls.plugin.metadata
60
+ if meta:
61
+ description = meta.description or ""
62
+ commands.append({"name": name, "description": description})
63
+ break
64
+ except Exception:
65
+ continue
66
+ return commands
67
+
68
+
69
+ # ========== 消息解析(从 aigf_manager api_hooks 抽取,不包含 VLM 描述) ==========
70
+
71
+ def _parse_segments(message) -> list[dict]:
72
+ """将 OneBot v11 消息统一解析为段落列表(文本 + 图片 url/base64)"""
73
+ if isinstance(message, str):
74
+ try:
75
+ msg = Message(message)
76
+ return _parse_segments(msg)
77
+ except Exception:
78
+ return [{"type": "text", "text": message}]
79
+
80
+ if isinstance(message, Message):
81
+ result = []
82
+ try:
83
+ for seg in message:
84
+ seg_type = getattr(seg, "type", None)
85
+ seg_data = getattr(seg, "data", {})
86
+ if seg_type == "text":
87
+ text = seg_data.get("text", "") if isinstance(seg_data, dict) else ""
88
+ if text:
89
+ result.append({"type": "text", "text": text})
90
+ elif seg_type == "image":
91
+ data = seg_data if isinstance(seg_data, dict) else {}
92
+ result.append(_extract_image_data(data))
93
+ except Exception as e:
94
+ logger.error(f"[PeerAgent] Message 迭代失败: {e}")
95
+ return result
96
+
97
+ if isinstance(message, list):
98
+ result = []
99
+ for seg in message:
100
+ if isinstance(seg, dict):
101
+ if seg.get("type") == "text":
102
+ result.append({"type": "text", "text": seg.get("data", {}).get("text", "")})
103
+ elif seg.get("type") == "image":
104
+ result.append(_extract_image_data(seg.get("data", {})))
105
+ return result
106
+
107
+ return []
108
+
109
+
110
+ def _extract_image_data(data: dict) -> dict:
111
+ """从图片段落的 data 中提取图片数据,处理 base64:// 前缀"""
112
+ file_value = data.get("file", "")
113
+ url = data.get("url", "")
114
+ b64 = data.get("base64", "")
115
+
116
+ if file_value.startswith("base64://"):
117
+ b64 = file_value[9:]
118
+ file_value = ""
119
+
120
+ return {"type": "image", "url": url, "file": file_value, "base64": b64}
121
+
122
+
123
+ # ========== 推送 ==========
124
+
125
+ async def _push(plugin: str, group_id: int, text: str = "", image_url: str = "", image_base64: str = ""):
126
+ """将捕获到的插件输出推送给 Bot A 的 /peer/capture"""
127
+ if not plugin_config.aigfm_peer_push_port or not plugin_config.aigfm_peer_token:
128
+ return
129
+ payload = {
130
+ "bot_name": plugin_config.aigfm_peer_bot_name,
131
+ "plugin": plugin,
132
+ "group_id": group_id,
133
+ "text": text,
134
+ "image_url": image_url,
135
+ "image_base64": image_base64,
136
+ "commands": _commands_cache,
137
+ }
138
+ headers = {"Authorization": f"Bearer {plugin_config.aigfm_peer_token}"}
139
+ url = f"http://127.0.0.1:{plugin_config.aigfm_peer_push_port}/peer/capture"
140
+ try:
141
+ async with httpx.AsyncClient(timeout=30.0) as client:
142
+ resp = await client.post(url, json=payload, headers=headers)
143
+ resp.raise_for_status()
144
+ logger.success(f"[PeerAgent] 推送成功: [{plugin}] → {plugin_config.aigfm_peer_bot_name}")
145
+ logger.info(f"[PeerAgent] 推送内容: plugin={plugin}, group={group_id}, "
146
+ f"text={text[:100] if text else ''}, "
147
+ f"image_url={image_url[:100] if image_url else ''}, "
148
+ f"image_base64={'有' if image_base64 else '无'}")
149
+ except Exception as e:
150
+ logger.exception(f"[PeerAgent] 推送失败: {e}")
151
+
152
+
153
+ # ========== 钩子:捕获本 bot 插件输出 ==========
154
+
155
+ @Bot.on_calling_api
156
+ async def capture_outgoing(bot: Bot, api: str, data: dict):
157
+ """拦截本 bot 插件发出的 API 调用,推送给 Bot A"""
158
+ if api not in ("send_msg", "send_group_msg", "send_private_msg"):
159
+ return
160
+ if not plugin_config.aigfm_peer_push_port:
161
+ return
162
+
163
+ group_id = data.get("group_id")
164
+ if not group_id:
165
+ return
166
+
167
+ try:
168
+ matcher = current_matcher.get()
169
+ source = matcher.plugin_name or "unknown"
170
+ except LookupError:
171
+ return
172
+
173
+ # 防死循环:跳过自身
174
+ if source in ("nonebot_plugin_aigfm_peer", "nonebot-plugin-aigfm-peer"):
175
+ return
176
+
177
+ # 白名单过滤
178
+ if plugin_config.aigfm_peer_capture_plugins and source not in plugin_config.aigfm_peer_capture_plugins:
179
+ return
180
+
181
+ message = data.get("message", "")
182
+ segments = _parse_segments(message)
183
+ for seg in segments:
184
+ if seg["type"] == "text" and seg["text"]:
185
+ await _push(source, group_id, text=seg["text"])
186
+ elif seg["type"] == "image":
187
+ await _push(source, group_id, image_url=seg.get("url", ""), image_base64=seg.get("base64", ""))
188
+
189
+
190
+ # ========== synthetic event 执行(从 aigf_manager plugin_invoker 抽取) ==========
191
+
192
+ def _apply_command_prefix(command: str) -> str:
193
+ """按本 bot 配置的命令前缀(COMMAND_START)补充到命令前"""
194
+ try:
195
+ command_start = get_driver().config.command_start
196
+ if command_start:
197
+ prefix = next(iter(command_start), "")
198
+ if prefix and not command.startswith(prefix):
199
+ command = prefix + command
200
+ except Exception:
201
+ pass
202
+ return command
203
+
204
+
205
+ def _create_synthetic_event(bot, group_id: int, command: str, user_id: int = 0) -> GroupMessageEvent:
206
+ """创建模拟的群消息事件"""
207
+ # LLM 传无前缀命令,这里按本 bot 配置的命令前缀补充
208
+ command = _apply_command_prefix(command)
209
+
210
+ message = Message(MessageSegment.text(command))
211
+ now = datetime.now()
212
+ return GroupMessageEvent(
213
+ time=int(now.timestamp()),
214
+ self_id=int(bot.self_id) if bot else 0,
215
+ post_type="message",
216
+ sub_type="normal",
217
+ message_type="group",
218
+ user_id=user_id,
219
+ group_id=group_id,
220
+ message_id=0,
221
+ message=message,
222
+ raw_message=command,
223
+ font=0,
224
+ sender={"user_id": user_id, "nickname": "aigf_user", "role": "member"},
225
+ )
226
+
227
+
228
+ async def _execute_command(bot, group_id: int, command: str, user_id: int = 0):
229
+ """执行插件命令,插件响应由 capture_outgoing 钩子自动推送"""
230
+ logger.debug(f"[PeerAgent] 执行远程命令: command={command}, group={group_id}, user={user_id}")
231
+ event = _create_synthetic_event(bot, group_id, command, user_id)
232
+ await handle_event(bot, event)
233
+
234
+
235
+ # ========== HTTP 端点:接收 Bot A 的远程调用 ==========
236
+
237
+ @get_driver().on_startup
238
+ async def _on_startup():
239
+ # 启动时扫描本 bot 已注册命令,随推送告知 Bot A
240
+ _commands_cache.clear()
241
+ _commands_cache.extend(_scan_commands())
242
+ logger.info(f"[PeerAgent] 扫描命令: {len(_commands_cache)} 个")
243
+ if not plugin_config.aigfm_peer_token:
244
+ logger.warning("[PeerAgent] 未配置 aigfm_peer_token,invoke 端点将拒绝所有请求")
245
+ try:
246
+ import nonebot
247
+ from fastapi import Request
248
+ from fastapi.responses import JSONResponse
249
+ app = nonebot.get_app()
250
+
251
+ @app.post("/peer/invoke")
252
+ async def _peer_invoke(request: Request):
253
+ auth = request.headers.get("Authorization", "")
254
+ token = auth[len("Bearer "):].strip() if auth.startswith("Bearer ") else ""
255
+ if not plugin_config.aigfm_peer_token or token != plugin_config.aigfm_peer_token:
256
+ return JSONResponse({"error": "unauthorized"}, status_code=401)
257
+ try:
258
+ data = await request.json()
259
+ except Exception:
260
+ return JSONResponse({"error": "bad json"}, status_code=400)
261
+ command = data.get("command", "")
262
+ group_id = data.get("group_id")
263
+ user_id = data.get("user_id", 0)
264
+ if not command or not group_id:
265
+ return JSONResponse({"error": "missing command or group_id"}, status_code=400)
266
+ logger.info(f"[PeerAgent] 收到调用: command={command}, group={group_id}, user_id={user_id}")
267
+ try:
268
+ bot = get_bot()
269
+ except ValueError:
270
+ return JSONResponse({"error": "no bot"}, status_code=500)
271
+ # 后台执行插件命令,立即返回(响应由 capture_outgoing 钩子异步推回 Bot A)
272
+ try:
273
+ task = asyncio.create_task(_execute_command(bot, int(group_id), command, int(user_id or 0)))
274
+ _invoke_tasks.add(task)
275
+ task.add_done_callback(_invoke_tasks.discard)
276
+ except Exception as e:
277
+ logger.error(f"[PeerAgent] 命令执行失败: {e}")
278
+ return JSONResponse({"error": "execute failed"}, status_code=500)
279
+ return JSONResponse({"ok": True})
280
+
281
+ logger.success(f"[PeerAgent] 启动完成 | 推送: {bool(plugin_config.aigfm_peer_push_port)} | bot 名: {plugin_config.aigfm_peer_bot_name}")
282
+ except Exception as e:
283
+ logger.error(f"[PeerAgent] 注册 invoke 端点失败: {e}")
@@ -0,0 +1,18 @@
1
+ """插件配置定义"""
2
+
3
+ from nonebot import get_plugin_config
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class PluginConfig(BaseModel):
8
+ # 推送目标端口(Bot A 的 HTTP 端口)
9
+ aigfm_peer_push_port: int = Field(0, description="Bot A 的 HTTP 端口,如 14514")
10
+ # 共享 token(Bot A 的 aigfm_peer_bots 里对应项必须一致)
11
+ aigfm_peer_token: str = Field("", description="共享密钥,用于推送和 invoke 认证")
12
+ # 本 bot 名称(推送给 Bot A 时作为来源标识)
13
+ aigfm_peer_bot_name: str = Field("", description="本 bot 的名称,与 Bot A 的 aigfm_peer_bots 配置里的 name 对应")
14
+ # 捕获白名单
15
+ aigfm_peer_capture_plugins: list[str] = Field(default_factory=list, description="要捕获输出的插件名列表,为空则捕获所有")
16
+
17
+
18
+ plugin_config: PluginConfig = get_plugin_config(PluginConfig)
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: nonebot-plugin-aigfm-peer
3
+ Version: 0.1.0
4
+ Summary: 跨bot通信代理插件:捕获本bot插件输出推送给主bot,并支持远程插件调用
5
+ License-Expression: MIT
6
+ Requires-Python: <4.0,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: nonebot2>=2.3.0
10
+ Requires-Dist: nonebot-adapter-onebot>=2.2.0
11
+ Requires-Dist: httpx>=0.24.0
12
+ Dynamic: license-file
13
+
14
+ # nonebot-plugin-aigfm-peer
15
+
16
+ 跨 bot 通信代理插件,安装到其它 bot 上,配合主插件 [nonebot-plugin-aigf-master](https://github.com/Funny1Potato/nonebot-plugin-aigf-master) 使用。
17
+
18
+ ## 📖 介绍
19
+
20
+ - **捕获输出并推送**:拦截本 bot 所有插件的输出(文本/图片),通过 HTTP 推送给主 bot(aigf-master),进入其 LLM 上下文(标注 `[bot名]`)
21
+ - **远程插件调用**:提供 `/peer/invoke` 端点,接收主 bot 的远程命令调用,用 synthetic event 执行本 bot 的本地插件
22
+ - **命令推送**:启动时扫描本 bot 已注册的 `on_command` 命令,随推送告知主 bot,让 LLM 了解可调用命令
23
+
24
+ ## 💿 安装
25
+
26
+ ```bash
27
+ pip install nonebot-plugin-aigfm-peer
28
+ ```
29
+
30
+ 在 `pyproject.toml` 中添加:
31
+
32
+ ```toml
33
+ [tool.nonebot]
34
+ plugins = ["nonebot-plugin-aigfm-peer"]
35
+ ```
36
+
37
+ ## 配置
38
+
39
+ 在 `.env` 中添加:
40
+
41
+ ```env
42
+ # 主插件(Bot A)的 HTTP 端口
43
+ AIGFM_PEER_PUSH_PORT=14514
44
+ # 共享 token(与主插件 AIGFM_PEER_BOTS 中对应项一致)
45
+ AIGFM_PEER_TOKEN="shared_secret"
46
+ # 本 bot 名称(与主插件 AIGFM_PEER_BOTS 的 name 对应)
47
+ AIGFM_PEER_BOT_NAME="botB"
48
+ # 要捕获输出的插件名列表,为空则捕获所有
49
+ AIGFM_PEER_CAPTURE_PLUGINS=[]
50
+ ```
51
+
52
+ ## 与主插件配合
53
+
54
+ 1. 在主插件 `.env` 配置 `AIGFM_PEER_BOTS`(含本 bot 的 name/port/token)
55
+ 2. 两端 token 必须一致
56
+ 3. 主插件与 peer 插件的前缀(`COMMAND_START`)各自独立配置,远程调用时按各 bot 的前缀自动适配
57
+
58
+ ## License
59
+
60
+ MIT
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ nonebot_plugin_aigfm_peer/__init__.py
5
+ nonebot_plugin_aigfm_peer/config.py
6
+ nonebot_plugin_aigfm_peer.egg-info/PKG-INFO
7
+ nonebot_plugin_aigfm_peer.egg-info/SOURCES.txt
8
+ nonebot_plugin_aigfm_peer.egg-info/dependency_links.txt
9
+ nonebot_plugin_aigfm_peer.egg-info/requires.txt
10
+ nonebot_plugin_aigfm_peer.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ nonebot2>=2.3.0
2
+ nonebot-adapter-onebot>=2.2.0
3
+ httpx>=0.24.0
@@ -0,0 +1 @@
1
+ nonebot_plugin_aigfm_peer
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nonebot-plugin-aigfm-peer"
7
+ version = "0.1.0"
8
+ description = "跨bot通信代理插件:捕获本bot插件输出推送给主bot,并支持远程插件调用"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10, <4.0"
12
+ dependencies = [
13
+ "nonebot2>=2.3.0",
14
+ "nonebot-adapter-onebot>=2.2.0",
15
+ "httpx>=0.24.0",
16
+ ]
17
+
18
+ [tool.nonebot]
19
+ plugins = ["nonebot_plugin_aigfm_peer"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+