feat: 集成 astrbot_plugin_telegram 至 astrbot

This commit is contained in:
Soulter
2025-02-24 22:26:23 +08:00
parent a0dbd99928
commit e527f31213
5 changed files with 186 additions and 5 deletions
+14 -1
View File
@@ -149,8 +149,21 @@ CONFIG_METADATA_2 = {
"app_secret": "",
"domain": "https://open.feishu.cn"
},
"telegram": {
"id": "telegram",
"type": "telegram",
"enable": False,
"telegram_token": "your_bot_token",
"start_message": "Hello, I'm AstrBot!",
"telegram_api_base_url": "https://api.telegram.org/bot",
}
},
"items": {
"telegram_token": {
"description": "Bot Token",
"type": "string",
"hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。",
},
"id": {
"description": "ID",
"type": "string",
@@ -250,7 +263,7 @@ CONFIG_METADATA_2 = {
"description": "间隔时间计算方法",
"type": "string",
"options": ["random", "log"],
"hint": "分段回复的间隔时间计算方法。random 为随机时间,log 为根据消息长度计算,$y=log_{log\_base}(x)$x为字数,y的单位为秒。",
"hint": "分段回复的间隔时间计算方法。random 为随机时间,log 为根据消息长度计算,$y=log_<log_base>(x)$x为字数,y的单位为秒。",
},
"interval": {
"description": "随机间隔时间(秒)",
+3 -1
View File
@@ -49,6 +49,8 @@ class PlatformManager():
from .sources.gewechat.gewechat_platform_adapter import GewechatPlatformAdapter # noqa: F401
case "lark":
from .sources.lark.lark_adapter import LarkPlatformAdapter # noqa: F401
case "telegram":
from .sources.telegram.tg_adapter import TelegramPlatformAdapter # noqa: F401
except (ImportError, ModuleNotFoundError) as e:
logger.error(f"加载平台适配器 {platform_config['type']} 失败,原因:{e}。请检查依赖库是否安装。提示:可以在 管理面板->控制台->安装Pip库 中安装依赖库。")
except Exception as e:
@@ -56,7 +58,7 @@ class PlatformManager():
if platform_config['type'] not in platform_cls_map:
logger.error(f"未找到适用于 {platform_config['type']}({platform_config['id']}) 平台适配器,请检查是否已经安装或者名称填写错误")
logger.error(f"未找到适用于 {platform_config['type']}({platform_config['id']}) 平台适配器,请检查是否已经安装或者名称填写错误")
return
cls_type = platform_cls_map[platform_config['type']]
inst = cls_type(platform_config, self.settings, self.event_queue)
@@ -0,0 +1,117 @@
import sys
import uuid
import asyncio
from astrbot.api.platform import Platform, AstrBotMessage, MessageMember, PlatformMetadata, MessageType
from astrbot.api.event import MessageChain
from astrbot.api.message_components import Plain, Image, Record, File as AstrBotFile, Video
from astrbot.core.platform.astr_message_event import MessageSesion
from astrbot.api.platform import register_platform_adapter
from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, filters
from telegram.constants import ChatType
from telegram.ext import MessageHandler as TelegramMessageHandler
from .tg_event import TelegramPlatformEvent
from astrbot.api import logger
if sys.version_info >= (3, 12):
from typing import override
else:
from typing_extensions import override
@register_platform_adapter("telegram", "telegram 适配器")
class TelegramPlatformAdapter(Platform):
def __init__(self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue) -> None:
super().__init__(event_queue)
self.config = platform_config
self.settings = platform_settings
self.client_self_id = uuid.uuid4().hex[:8]
@override
async def send_by_session(self, session: MessageSesion, message_chain: MessageChain):
from_username = session.session_id
await TelegramPlatformEvent.send_with_client(self.client, message_chain, from_username)
await super().send_by_session(session, message_chain)
@override
def meta(self) -> PlatformMetadata:
return PlatformMetadata(
"telegram",
"telegram 适配器",
)
@override
async def run(self):
base_url = self.config.get("telegram_api_base_url", "https://api.telegram.org/bot")
if not base_url:
base_url = "https://api.telegram.org/bot"
self.application = ApplicationBuilder().token(self.config['telegram_token']).base_url(base_url).build()
message_handler = TelegramMessageHandler(
filters=filters.ALL, # receive all messages
callback=self.convert_message
)
self.application.add_handler(message_handler)
await self.application.initialize()
await self.application.start()
queue = self.application.updater.start_polling()
self.client = self.application.bot
logger.info("Telegram Platform Adapter is running.")
await queue
async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(chat_id=update.effective_chat.id, text=self.config["start_message"])
async def convert_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> AstrBotMessage:
message = AstrBotMessage()
# 获得是群聊还是私聊
if update.effective_chat.type == ChatType.PRIVATE:
message.type = MessageType.FRIEND_MESSAGE
else:
message.type = MessageType.GROUP_MESSAGE
message.group_id = update.effective_chat.id
message.message_id = str(update.message.message_id)
message.session_id = str(update.effective_chat.id)
message.sender = MessageMember(str(update.effective_user.id), update.effective_user.username)
message.self_id = str(context.bot.id)
message.raw_message = update
message.message_str = ""
message.message = []
if update.message.text:
plain_text = update.message.text
message.message = [Plain(plain_text),]
message.message_str = plain_text
elif update.message.voice:
file = await update.message.voice.get_file()
message.message = [Record(file=file.file_path, url=file.file_path),]
elif update.message.photo:
for photo in update.message.photo:
file = await photo.get_file()
message.message.append(Image(file=file.file_path, url=file.file_path))
elif update.message.document:
file = await update.message.document.get_file()
message.message = [AstrBotFile(file=file.file_path, name="file"),]
elif update.message.video:
file = await update.message.video.get_file()
message.message = [Video(file=file.file_path, path=file.file_path),]
await self.handle_msg(message)
async def handle_msg(self, message: AstrBotMessage):
message_event = TelegramPlatformEvent(
message_str=message.message_str,
message_obj=message,
platform_meta=self.meta(),
session_id=message.session_id,
client=self.client
)
self.commit_event(message_event)
@@ -0,0 +1,50 @@
from astrbot.api.event import AstrMessageEvent, MessageChain
from astrbot.api.platform import AstrBotMessage, PlatformMetadata, MessageType
from astrbot.api.message_components import Plain, Image, Reply, At
from telegram.ext import ExtBot
class TelegramPlatformEvent(AstrMessageEvent):
def __init__(self, message_str: str, message_obj: AstrBotMessage, platform_meta: PlatformMetadata, session_id: str, client: ExtBot):
super().__init__(message_str, message_obj, platform_meta, session_id)
self.client = client
@staticmethod
async def send_with_client(client: ExtBot, message: MessageChain, user_name: str):
image_path = None
has_reply = False
reply_message_id = None
at_user_id = None
for i in message.chain:
if isinstance(i, Reply):
has_reply = True
reply_message_id = i.id
if isinstance(i, At):
at_user_id = i.name
at_flag = False
for i in message.chain:
payload = {
"chat_id": user_name,
}
if has_reply:
payload["reply_to_message_id"] = reply_message_id
if isinstance(i, Plain):
if at_user_id and not at_flag:
i.text = f"@{at_user_id} " + i.text
at_flag = True
await client.send_message(text=i.text, **payload)
elif isinstance(i, Image):
if i.path:
image_path = i.path
else:
image_path = i.file
await client.send_photo(photo=image_path, **payload)
async def send(self, message: MessageChain):
if self.get_message_type() == MessageType.GROUP_MESSAGE:
await self.send_with_client(self.client, message, self.message_obj.group_id)
else:
await self.send_with_client(self.client, message, self.get_sender_id())
await super().send(message)
+2 -3
View File
@@ -9,7 +9,6 @@ beautifulsoup4
googlesearch-python
readability-lxml
quart
psutil
lxml_html_clean
colorlog
aiocqhttp
@@ -23,5 +22,5 @@ psutil>=5.8.0
lark-oapi
ormsgpack
cryptography
dashscope
dashscope
python-telegram-bot