nonebot-plugin-onebot2tg 1.0.2__py3-none-any.whl
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
|
+
from nonebot.plugin import PluginMetadata
|
|
2
|
+
from nonebot import get_plugin_config
|
|
3
|
+
|
|
4
|
+
from .config import Config
|
|
5
|
+
from . import forwarder
|
|
6
|
+
|
|
7
|
+
config = get_plugin_config(Config)
|
|
8
|
+
forwarder.config = config
|
|
9
|
+
|
|
10
|
+
__plugin_meta__ = PluginMetadata(
|
|
11
|
+
name="nonebot-plugin-onebot2tg",
|
|
12
|
+
description="OneBot V11 与 Telegram 双向消息转发插件",
|
|
13
|
+
usage="配置 ONEBOT2TG_TARGET_CHAT_ID 和 ONEBOT2TG_OB_TARGET_GROUP_ID 即可自动转发",
|
|
14
|
+
type="application",
|
|
15
|
+
homepage="",
|
|
16
|
+
config=Config,
|
|
17
|
+
supported_adapters={
|
|
18
|
+
"~onebot.v11",
|
|
19
|
+
"~telegram",
|
|
20
|
+
},
|
|
21
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Config(BaseModel):
|
|
5
|
+
"""插件配置"""
|
|
6
|
+
|
|
7
|
+
# 模式开关
|
|
8
|
+
onebot2tg_enable_bridge: bool = True
|
|
9
|
+
"""开启互通模式(双向转发,需配置互通群号,默认开启)"""
|
|
10
|
+
|
|
11
|
+
onebot2tg_enable_forward: bool = False
|
|
12
|
+
"""开启转发模式(QQ所有消息单向转发到TG私聊)"""
|
|
13
|
+
|
|
14
|
+
# 互通模式配置(一对一)
|
|
15
|
+
onebot2tg_bridge_group_id: str | int = ""
|
|
16
|
+
"""互通模式下,要互通的QQ群号"""
|
|
17
|
+
|
|
18
|
+
onebot2tg_bridge_tg_chat_id: str | int = ""
|
|
19
|
+
"""互通模式下,TG对应的chat_id(群或频道)"""
|
|
20
|
+
|
|
21
|
+
# 转发模式配置
|
|
22
|
+
onebot2tg_forward_target_chat_id: str | int = ""
|
|
23
|
+
"""转发模式下,QQ消息转发到TG的目标chat_id(私聊或群)"""
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from nonebot import get_adapter, logger
|
|
4
|
+
from nonebot.adapters.onebot.v11 import (
|
|
5
|
+
Bot as OB11Bot,
|
|
6
|
+
Message as OB11Message,
|
|
7
|
+
MessageEvent as OB11MessageEvent,
|
|
8
|
+
GroupMessageEvent as OB11GroupMessageEvent,
|
|
9
|
+
PrivateMessageEvent as OB11PrivateMessageEvent,
|
|
10
|
+
MessageSegment as OB11Segment,
|
|
11
|
+
)
|
|
12
|
+
from nonebot.adapters.telegram import Bot as TGBot
|
|
13
|
+
from nonebot.adapters.telegram.event import MessageEvent as TGMessageEvent
|
|
14
|
+
from nonebot.adapters.telegram.message import (
|
|
15
|
+
Message as TGMessage,
|
|
16
|
+
File,
|
|
17
|
+
Entity,
|
|
18
|
+
)
|
|
19
|
+
from nonebot.drivers import Request
|
|
20
|
+
from nonebot.plugin.on import on_message
|
|
21
|
+
from nonebot.rule import is_type
|
|
22
|
+
|
|
23
|
+
from .config import Config
|
|
24
|
+
|
|
25
|
+
config: Config
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_tg_bot() -> TGBot | None:
|
|
29
|
+
try:
|
|
30
|
+
adapter = get_adapter("Telegram")
|
|
31
|
+
for bot in adapter.bots.values():
|
|
32
|
+
if isinstance(bot, TGBot):
|
|
33
|
+
return bot
|
|
34
|
+
except Exception as e:
|
|
35
|
+
logger.warning(f"获取 Telegram Bot 失败: {e}")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def get_ob11_bot() -> OB11Bot | None:
|
|
40
|
+
try:
|
|
41
|
+
adapter = get_adapter("OneBot V11")
|
|
42
|
+
for bot in adapter.bots.values():
|
|
43
|
+
if isinstance(bot, OB11Bot):
|
|
44
|
+
return bot
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.warning(f"获取 OneBot V11 Bot 失败: {e}")
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _ob11_display_name(event: OB11MessageEvent) -> str:
|
|
51
|
+
sender = event.sender
|
|
52
|
+
return sender.card or sender.nickname or event.get_user_id()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _tg_display_name(event: TGMessageEvent) -> str:
|
|
56
|
+
user = getattr(event, "from_", None)
|
|
57
|
+
if user:
|
|
58
|
+
return user.username or user.first_name or event.get_user_id()
|
|
59
|
+
return event.get_user_id()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ============================================================
|
|
63
|
+
# 下载 TG 文件(走代理)
|
|
64
|
+
# ============================================================
|
|
65
|
+
async def _download_tg_file(tg_bot: TGBot, file_id: str) -> bytes | None:
|
|
66
|
+
"""通过代理下载 TG 文件"""
|
|
67
|
+
try:
|
|
68
|
+
file_info = await tg_bot.get_file(file_id=file_id)
|
|
69
|
+
if not file_info.file_path:
|
|
70
|
+
return None
|
|
71
|
+
api_server = tg_bot.bot_config.api_server.rstrip("/")
|
|
72
|
+
url = f"{api_server}/file/bot{tg_bot.bot_config.token}/{file_info.file_path}"
|
|
73
|
+
adapter = tg_bot.adapter
|
|
74
|
+
proxy = getattr(adapter, "adapter_config", None)
|
|
75
|
+
proxy_url = proxy.proxy if proxy else None
|
|
76
|
+
request = Request("GET", url, proxy=proxy_url)
|
|
77
|
+
response = await adapter.request(request)
|
|
78
|
+
if response.status_code == 200 and response.content:
|
|
79
|
+
return response.content
|
|
80
|
+
logger.warning(f"下载 TG 文件失败,状态码: {response.status_code}")
|
|
81
|
+
return None
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.warning(f"下载 TG 文件失败: {e}")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ============================================================
|
|
88
|
+
# TG Message → OneBot V11 Message
|
|
89
|
+
# ============================================================
|
|
90
|
+
async def _tg_message_to_ob11(
|
|
91
|
+
tg_bot: TGBot, event: TGMessageEvent
|
|
92
|
+
) -> OB11Message:
|
|
93
|
+
segments: list[OB11Segment] = []
|
|
94
|
+
|
|
95
|
+
for seg in event.get_message():
|
|
96
|
+
if seg.type == "text":
|
|
97
|
+
segments.append(OB11Segment.text(seg.data.get("text", "")))
|
|
98
|
+
elif seg.type == "photo":
|
|
99
|
+
file_id = seg.data.get("file", "")
|
|
100
|
+
if file_id:
|
|
101
|
+
data = await _download_tg_file(tg_bot, file_id)
|
|
102
|
+
if data:
|
|
103
|
+
segments.append(OB11Segment.image(data))
|
|
104
|
+
else:
|
|
105
|
+
segments.append(OB11Segment.text("[图片]"))
|
|
106
|
+
else:
|
|
107
|
+
segments.append(OB11Segment.text("[图片]"))
|
|
108
|
+
elif seg.type == "sticker":
|
|
109
|
+
file_id = seg.data.get("file", "")
|
|
110
|
+
if file_id:
|
|
111
|
+
data = await _download_tg_file(tg_bot, file_id)
|
|
112
|
+
if data:
|
|
113
|
+
segments.append(OB11Segment.image(data))
|
|
114
|
+
else:
|
|
115
|
+
segments.append(OB11Segment.text("[贴纸]"))
|
|
116
|
+
else:
|
|
117
|
+
segments.append(OB11Segment.text("[贴纸]"))
|
|
118
|
+
elif seg.type == "animation":
|
|
119
|
+
segments.append(OB11Segment.text("[动图]"))
|
|
120
|
+
elif seg.type == "document":
|
|
121
|
+
segments.append(OB11Segment.text("[文件]"))
|
|
122
|
+
elif seg.type == "video":
|
|
123
|
+
segments.append(OB11Segment.text("[视频]"))
|
|
124
|
+
elif seg.type == "voice":
|
|
125
|
+
segments.append(OB11Segment.text("[语音]"))
|
|
126
|
+
else:
|
|
127
|
+
segments.append(OB11Segment.text(f"[{seg.type}]"))
|
|
128
|
+
|
|
129
|
+
return OB11Message(segments)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ============================================================
|
|
133
|
+
# OneBot V11 Message → TG Message
|
|
134
|
+
# ============================================================
|
|
135
|
+
async def _ob11_message_to_tg(
|
|
136
|
+
bot: OB11Bot, event: OB11MessageEvent
|
|
137
|
+
) -> tuple[str, list]:
|
|
138
|
+
caption_parts: list[str] = []
|
|
139
|
+
file_segments: list = []
|
|
140
|
+
|
|
141
|
+
for seg in event.get_message():
|
|
142
|
+
if seg.type == "text":
|
|
143
|
+
caption_parts.append(seg.data.get("text", ""))
|
|
144
|
+
elif seg.type == "image":
|
|
145
|
+
try:
|
|
146
|
+
file_info = await bot.get_image(file=seg.data.get("file", ""))
|
|
147
|
+
url = file_info.get("url", "")
|
|
148
|
+
if url:
|
|
149
|
+
file_segments.append(File.photo(url))
|
|
150
|
+
else:
|
|
151
|
+
caption_parts.append("[图片]")
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.warning(f"获取图片失败: {e}")
|
|
154
|
+
caption_parts.append("[图片]")
|
|
155
|
+
elif seg.type == "face":
|
|
156
|
+
caption_parts.append(f"[表情:{seg.data.get('id', '')}]")
|
|
157
|
+
elif seg.type == "at":
|
|
158
|
+
caption_parts.append(f"@{seg.data.get('qq', '')}")
|
|
159
|
+
elif seg.type == "reply":
|
|
160
|
+
pass
|
|
161
|
+
else:
|
|
162
|
+
caption_parts.append(f"[{seg.type}]")
|
|
163
|
+
|
|
164
|
+
return "".join(caption_parts), file_segments
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def _send_to_tg(
|
|
168
|
+
tg_bot: TGBot,
|
|
169
|
+
chat_id: str | int,
|
|
170
|
+
display_name: str,
|
|
171
|
+
caption: str,
|
|
172
|
+
file_segments: list,
|
|
173
|
+
mode: str,
|
|
174
|
+
):
|
|
175
|
+
if file_segments:
|
|
176
|
+
first_seg = file_segments[0]
|
|
177
|
+
full_caption = f"{display_name}:\n{caption}" if caption else f"{display_name}:"
|
|
178
|
+
|
|
179
|
+
if len(file_segments) == 1:
|
|
180
|
+
try:
|
|
181
|
+
await tg_bot.send_to(
|
|
182
|
+
chat_id=chat_id,
|
|
183
|
+
message=TGMessage([first_seg]) + TGMessage([Entity.text(full_caption)]),
|
|
184
|
+
)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.error(f"[{mode}] 发送图片到 TG 失败: {e}")
|
|
187
|
+
else:
|
|
188
|
+
try:
|
|
189
|
+
await tg_bot.send_to(
|
|
190
|
+
chat_id=chat_id,
|
|
191
|
+
message=TGMessage([first_seg]) + TGMessage([Entity.text(full_caption)]),
|
|
192
|
+
)
|
|
193
|
+
for seg in file_segments[1:]:
|
|
194
|
+
await tg_bot.send_to(chat_id=chat_id, message=seg)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.error(f"[{mode}] 发送多张图片到 TG 失败: {e}")
|
|
197
|
+
else:
|
|
198
|
+
text = f"{display_name}:\n{caption}"
|
|
199
|
+
try:
|
|
200
|
+
await tg_bot.send_message(chat_id=chat_id, text=text)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.error(f"[{mode}] 发送文本到 TG 失败: {e}")
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ============================================================
|
|
206
|
+
# TG 消息处理器(仅互通模式)
|
|
207
|
+
# ============================================================
|
|
208
|
+
tg_msg = on_message(rule=is_type(TGMessageEvent))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@tg_msg.handle()
|
|
212
|
+
async def handle_tg_message(event: TGMessageEvent):
|
|
213
|
+
if not config.onebot2tg_enable_bridge:
|
|
214
|
+
return
|
|
215
|
+
if not config.onebot2tg_bridge_group_id:
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
chat_id = str(event.chat.id) if hasattr(event, "chat") else ""
|
|
219
|
+
if chat_id != str(config.onebot2tg_bridge_tg_chat_id):
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
tg_bot = await get_tg_bot()
|
|
223
|
+
if tg_bot is None:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
ob11_bot = await get_ob11_bot()
|
|
227
|
+
if ob11_bot is None:
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
display_name = _tg_display_name(event)
|
|
231
|
+
ob11_msg = await _tg_message_to_ob11(tg_bot, event)
|
|
232
|
+
prefix = OB11Segment.text(f"{display_name}:\n")
|
|
233
|
+
final_msg = OB11Message([prefix]) + ob11_msg
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
await ob11_bot.send_group_msg(
|
|
237
|
+
group_id=int(config.onebot2tg_bridge_group_id),
|
|
238
|
+
message=final_msg,
|
|
239
|
+
)
|
|
240
|
+
logger.debug("[互通] 已转发 TG 消息到 QQ群")
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.error(f"[互通] 转发 TG 消息到 QQ群失败: {e}")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# ============================================================
|
|
246
|
+
# OneBot 消息处理器(互通 + 转发模式)
|
|
247
|
+
# ============================================================
|
|
248
|
+
ob11_msg = on_message(rule=is_type(OB11MessageEvent))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@ob11_msg.handle()
|
|
252
|
+
async def handle_ob11_message(event: OB11MessageEvent):
|
|
253
|
+
tg_bot = await get_tg_bot()
|
|
254
|
+
if tg_bot is None:
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
ob11_bot = await get_ob11_bot()
|
|
258
|
+
if ob11_bot is None:
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
is_group = isinstance(event, OB11GroupMessageEvent)
|
|
262
|
+
group_id = str(event.group_id) if is_group else ""
|
|
263
|
+
display_name = _ob11_display_name(event)
|
|
264
|
+
|
|
265
|
+
# ---------- 互通模式 ----------
|
|
266
|
+
if config.onebot2tg_enable_bridge and config.onebot2tg_bridge_tg_chat_id:
|
|
267
|
+
if is_group and group_id == str(config.onebot2tg_bridge_group_id):
|
|
268
|
+
caption, file_segments = await _ob11_message_to_tg(ob11_bot, event)
|
|
269
|
+
await _send_to_tg(
|
|
270
|
+
tg_bot,
|
|
271
|
+
config.onebot2tg_bridge_tg_chat_id,
|
|
272
|
+
display_name,
|
|
273
|
+
caption,
|
|
274
|
+
file_segments,
|
|
275
|
+
"互通",
|
|
276
|
+
)
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
# ---------- 转发模式(QQ → TG 单向) ----------
|
|
280
|
+
if config.onebot2tg_enable_forward and config.onebot2tg_forward_target_chat_id:
|
|
281
|
+
if is_group:
|
|
282
|
+
source = f"[群:{event.group_id}] {display_name}"
|
|
283
|
+
elif isinstance(event, OB11PrivateMessageEvent):
|
|
284
|
+
source = f"[私聊] {display_name}"
|
|
285
|
+
else:
|
|
286
|
+
source = f"[QQ] {display_name}"
|
|
287
|
+
|
|
288
|
+
caption, file_segments = await _ob11_message_to_tg(ob11_bot, event)
|
|
289
|
+
await _send_to_tg(
|
|
290
|
+
tg_bot,
|
|
291
|
+
config.onebot2tg_forward_target_chat_id,
|
|
292
|
+
source,
|
|
293
|
+
caption,
|
|
294
|
+
file_segments,
|
|
295
|
+
"转发",
|
|
296
|
+
)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nonebot-plugin-onebot2tg
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: OneBot V11 与 Telegram 消息转发插件
|
|
5
|
+
Author: SoulGateKey
|
|
6
|
+
Author-email: SoulGateKey <SoulGateKey@gmail.com>
|
|
7
|
+
Requires-Dist: httpx>=0.27.0,<1.0.0
|
|
8
|
+
Requires-Dist: nonebot-plugin-alconna>=0.60.0,<1.0.0
|
|
9
|
+
Requires-Dist: nonebot-plugin-apscheduler>=0.5.0,<1.0.0
|
|
10
|
+
Requires-Dist: nonebot-plugin-localstore>=0.7.4,<1.0.0
|
|
11
|
+
Requires-Dist: nonebot-plugin-uninfo>=0.10.0,<1.0.0
|
|
12
|
+
Requires-Dist: nonebot2>=2.5.0,<3.0.0
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Project-URL: Homepage, https://github.com/SoulGateKey/nonebot-plugin-onebot2tg
|
|
15
|
+
Project-URL: Issues, https://github.com/SoulGateKey/nonebot-plugin-onebot2tg/issues
|
|
16
|
+
Project-URL: Repository, https://github.com/SoulGateKey/nonebot-plugin-onebot2tg.git
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
<a href="https://v2.nonebot.dev/store">
|
|
21
|
+
<img src="https://raw.githubusercontent.com/fllesser/nonebot-plugin-template/refs/heads/resource/.docs/NoneBotPlugin.svg" width="310" alt="logo"></a>
|
|
22
|
+
|
|
23
|
+
## ✨ nonebot-plugin-onebot2tg ✨
|
|
24
|
+
[](./LICENSE)
|
|
25
|
+
[](https://pypi.python.org/pypi/nonebot-plugin-onebot2tg)
|
|
26
|
+
[](https://www.python.org)
|
|
27
|
+
[](https://github.com/astral-sh/uv)
|
|
28
|
+
<br/>
|
|
29
|
+
[](https://github.com/astral-sh/ruff)
|
|
30
|
+
[](https://results.pre-commit.ci/latest/github/SoulGateKey/nonebot-plugin-onebot2tg/master)
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
OneBot V11 与 Telegram 消息转发插件
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## 📖 功能
|
|
37
|
+
|
|
38
|
+
- [x] **互通模式**:指定一个 QQ 群与一个 Telegram 群/频道双向互通,消息实时同步
|
|
39
|
+
- [x] **转发模式**:将 QQ 所有接收到的消息单向转发到 Telegram 私聊/频道/群聊
|
|
40
|
+
- [x] 互通模式和转发模式可同时开启不会重复发送消息
|
|
41
|
+
- [x] Telegram 图片/贴纸下载会自动走适配器配置的代理
|
|
42
|
+
- [x] **消息类型支持**:文本、图片、贴纸
|
|
43
|
+
- [ ] 贴纸图片过大时自动压缩?
|
|
44
|
+
- [ ] GIF转发
|
|
45
|
+
- [ ] 显示具体表情而不是表情ID
|
|
46
|
+
- [ ] 显示@人的昵称而不是QQ号
|
|
47
|
+
- [ ] 双向转发Reply消息 这个可能需要做数据库
|
|
48
|
+
- [ ] 在实现Reply消息后 实现转发模式双向转发
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
## 💿 安装
|
|
52
|
+
<details open>
|
|
53
|
+
<summary>使用 nb-cli 安装</summary>
|
|
54
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
55
|
+
|
|
56
|
+
nb plugin install nonebot-plugin-onebot2tg --upgrade
|
|
57
|
+
使用 **pypi** 源安装
|
|
58
|
+
|
|
59
|
+
nb plugin install nonebot-plugin-onebot2tg --upgrade -i "https://pypi.org/simple"
|
|
60
|
+
使用**清华源**安装
|
|
61
|
+
|
|
62
|
+
nb plugin install nonebot-plugin-onebot2tg --upgrade -i "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
</details>
|
|
66
|
+
|
|
67
|
+
<details>
|
|
68
|
+
<summary>使用包管理器安装</summary>
|
|
69
|
+
在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令
|
|
70
|
+
|
|
71
|
+
<details open>
|
|
72
|
+
<summary>uv</summary>
|
|
73
|
+
|
|
74
|
+
uv add nonebot-plugin-onebot2tg
|
|
75
|
+
安装仓库 master 分支
|
|
76
|
+
|
|
77
|
+
uv add git+https://github.com/SoulGateKey/nonebot-plugin-onebot2tg@master
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
<details>
|
|
81
|
+
<summary>pdm</summary>
|
|
82
|
+
|
|
83
|
+
pdm add nonebot-plugin-onebot2tg
|
|
84
|
+
安装仓库 master 分支
|
|
85
|
+
|
|
86
|
+
pdm add git+https://github.com/SoulGateKey/nonebot-plugin-onebot2tg@master
|
|
87
|
+
</details>
|
|
88
|
+
<details>
|
|
89
|
+
<summary>poetry</summary>
|
|
90
|
+
|
|
91
|
+
poetry add nonebot-plugin-onebot2tg
|
|
92
|
+
安装仓库 master 分支
|
|
93
|
+
|
|
94
|
+
poetry add git+https://github.com/SoulGateKey/nonebot-plugin-onebot2tg@master
|
|
95
|
+
</details>
|
|
96
|
+
|
|
97
|
+
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入
|
|
98
|
+
|
|
99
|
+
plugins = ["nonebot_plugin_onebot2tg"]
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
<summary>使用 nbr 安装(使用 uv 管理依赖可用)</summary>
|
|
105
|
+
|
|
106
|
+
[nbr](https://github.com/fllesser/nbr) 是一个基于 uv 的 nb-cli,可以方便地管理 nonebot2
|
|
107
|
+
|
|
108
|
+
nbr plugin install nonebot-plugin-onebot2tg
|
|
109
|
+
使用 **pypi** 源安装
|
|
110
|
+
|
|
111
|
+
nbr plugin install nonebot-plugin-onebot2tg -i "https://pypi.org/simple"
|
|
112
|
+
使用**清华源**安装
|
|
113
|
+
|
|
114
|
+
nbr plugin install nonebot-plugin-onebot2tg -i "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
115
|
+
|
|
116
|
+
</details>
|
|
117
|
+
|
|
118
|
+
## 依赖适配器
|
|
119
|
+
|
|
120
|
+
- [nonebot-adapter-onebot](https://github.com/nonebot/adapter-onebot) (OneBot V11)
|
|
121
|
+
- [nonebot-adapter-telegram](https://github.com/nonebot/adapter-telegram) (Telegram)
|
|
122
|
+
|
|
123
|
+
## ⚙️ 配置
|
|
124
|
+
|
|
125
|
+
在 `.env` 文件中添加以下配置项:
|
|
126
|
+
|
|
127
|
+
### 模式开关
|
|
128
|
+
|
|
129
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
130
|
+
|--------|------|--------|------|
|
|
131
|
+
| `ONEBOT2TG_ENABLE_BRIDGE` | `bool` | `True` | 开启互通模式(双向转发,需配置互通群号,默认开启) |
|
|
132
|
+
| `ONEBOT2TG_ENABLE_FORWARD` | `bool` | `False` | 开启转发模式(QQ 所有消息单向转发到 TG) |
|
|
133
|
+
|
|
134
|
+
### 互通模式配置
|
|
135
|
+
|
|
136
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
137
|
+
|--------|------|--------|------|
|
|
138
|
+
| `ONEBOT2TG_BRIDGE_GROUP_ID` | `str \| int` | `""` | 互通模式下,要互通的 QQ 群号 |
|
|
139
|
+
| `ONEBOT2TG_BRIDGE_TG_CHAT_ID` | `str \| int` | `""` | 互通模式下,TG 对应的 chat_id(群或频道) |
|
|
140
|
+
|
|
141
|
+
### 转发模式配置
|
|
142
|
+
|
|
143
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
144
|
+
|--------|------|--------|------|
|
|
145
|
+
| `ONEBOT2TG_FORWARD_TARGET_CHAT_ID` | `str \| int` | `""` | 转发模式下,QQ 消息转发到 TG 的目标 chat_id 或 用户ID |
|
|
146
|
+
|
|
147
|
+
## 🎉 使用示例
|
|
148
|
+
|
|
149
|
+
### 场景 1:QQ 群与 Telegram 群互通
|
|
150
|
+
|
|
151
|
+
```env
|
|
152
|
+
ONEBOT2TG_ENABLE_BRIDGE=true
|
|
153
|
+
ONEBOT2TG_ENABLE_FORWARD=false
|
|
154
|
+
ONEBOT2TG_BRIDGE_GROUP_ID=123456789
|
|
155
|
+
ONEBOT2TG_BRIDGE_TG_CHAT_ID=-1001234567890
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
配置后,该 QQ 群与 Telegram 群的消息会实时双向同步。
|
|
159
|
+
|
|
160
|
+
### 场景 2:QQ 消息单向转发到 Telegram
|
|
161
|
+
|
|
162
|
+
```env
|
|
163
|
+
ONEBOT2TG_ENABLE_BRIDGE=false
|
|
164
|
+
ONEBOT2TG_ENABLE_FORWARD=true
|
|
165
|
+
ONEBOT2TG_FORWARD_TARGET_CHAT_ID=-1001234567890
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
配置后,QQ 群/私聊接收到的所有消息会单向转发到指定的 Telegram 聊天。
|
|
169
|
+
|
|
170
|
+
### 场景 3:互通和转发模式同时开启
|
|
171
|
+
|
|
172
|
+
## 注意事项
|
|
173
|
+
- 目前仅测试过QQ与TG互转,其他onebot实现欢迎提交测试结果及PR
|
|
174
|
+
- 互通模式为一对一配置,暂不支持多群互通
|
|
175
|
+
- 转发模式为单向(QQ → TG),TG 消息不会转发回 QQ
|
|
176
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
nonebot_plugin_onebot2tg/__init__.py,sha256=MpN-NZw3UXF5dA9C74Qn4P2AFpNA2BGsRe-fAmzINrM,569
|
|
2
|
+
nonebot_plugin_onebot2tg/config.py,sha256=k3TXY2WIZtFkNVBnFkLkoclTu4cqGvg2gtZx4QJdFpk,757
|
|
3
|
+
nonebot_plugin_onebot2tg/forwarder.py,sha256=uMlUGZ_YTnqXjBNaR1Xlwe2_TWq4BWLh3ElQvmJ0Bp4,10208
|
|
4
|
+
nonebot_plugin_onebot2tg-1.0.2.dist-info/WHEEL,sha256=fWriCkzqm-pffF5af4gJC9iI5FMFaJTuN9UxxxzOmdY,81
|
|
5
|
+
nonebot_plugin_onebot2tg-1.0.2.dist-info/METADATA,sha256=fZ5N6Aa9rxLsoqaFO8kcpjRq6hBBzNQ6iZlK9QkhtDQ,6407
|
|
6
|
+
nonebot_plugin_onebot2tg-1.0.2.dist-info/RECORD,,
|