feat: telegram adapter

This commit is contained in:
Junyan Qin
2025-02-14 12:55:48 +08:00
parent b2d1c82196
commit 2b6be04c5d

View File

@@ -16,11 +16,9 @@ import json
import datetime
import hashlib
import base64
import aiohttp
from Crypto.Cipher import AES
import aiohttp
import lark_oapi.ws.exception
import quart
from flask import jsonify
from lark_oapi.api.im.v1 import *
from lark_oapi.api.verification.v1 import GetVerificationRequest
@@ -36,30 +34,127 @@ from ...utils import image
class TelegramMessageConverter(adapter.MessageConverter):
@staticmethod
async def yiri2target(message_chain: platform_message.MessageChain, bot: telegram.Bot):
pass
async def yiri2target(message_chain: platform_message.MessageChain, bot: telegram.Bot) -> list[dict]:
components = []
for component in message_chain:
if isinstance(component, platform_message.Plain):
components.append({
"type": "text",
"text": component.text
})
elif isinstance(component, platform_message.Image):
photo_bytes = None
if component.base64:
photo_bytes = base64.b64decode(component.base64)
elif component.url:
async with aiohttp.ClientSession() as session:
async with session.get(component.url) as response:
photo_bytes = await response.read()
elif component.path:
with open(component.path, "rb") as f:
photo_bytes = f.read()
components.append({
"type": "photo",
"photo": photo_bytes
})
elif isinstance(component, platform_message.Forward):
for node in component.node_list:
components.extend(await TelegramMessageConverter.yiri2target(node.message_chain, bot))
return components
@staticmethod
async def target2yiri(message: telegram.Message, bot: telegram.Bot):
pass
async def target2yiri(message: telegram.Message, bot: telegram.Bot, bot_account_id: str):
message_components = []
def parse_message_text(text: str) -> list[platform_message.MessageComponent]:
msg_components = []
if f'@{bot_account_id}' in text:
msg_components.append(platform_message.At(target=bot_account_id))
text = text.replace(f'@{bot_account_id}', '')
msg_components.append(platform_message.Plain(text=text))
return msg_components
if message.text:
message_text = message.text
message_components.extend(parse_message_text(message_text))
if message.photo:
message_components.extend(parse_message_text(message.caption))
file = await message.photo[-1].get_file()
file_bytes = None
file_format = ''
async with aiohttp.ClientSession(trust_env=True) as session:
async with session.get(file.file_path) as response:
file_bytes = await response.read()
file_format = 'image/jpeg'
message_components.append(platform_message.Image(base64=f"data:{file_format};base64,{base64.b64encode(file_bytes).decode('utf-8')}"))
return platform_message.MessageChain(message_components)
class TelegramEventConverter(adapter.EventConverter):
@staticmethod
async def yiri2target(event: platform_events.Event, bot: telegram.Bot):
pass
async def yiri2target(event: platform_events.MessageEvent, bot: telegram.Bot):
return event.source_platform_object
@staticmethod
async def target2yiri(event: platform_events.Event, bot: telegram.Bot):
pass
async def target2yiri(event: Update, bot: telegram.Bot, bot_account_id: str):
lb_message = await TelegramMessageConverter.target2yiri(event.message, bot, bot_account_id)
if event.effective_chat.type == 'private':
return platform_events.FriendMessage(
sender=platform_entities.Friend(
id=event.effective_chat.id,
nickname=event.effective_chat.first_name,
remark=event.effective_chat.id,
),
message_chain=lb_message,
time=event.message.date.timestamp(),
source_platform_object=event
)
elif event.effective_chat.type == 'group':
return platform_events.GroupMessage(
sender=platform_entities.GroupMember(
id=event.effective_chat.id,
member_name=event.effective_chat.title,
permission=platform_entities.Permission.Member,
group=platform_entities.Group(
id=event.effective_chat.id,
name=event.effective_chat.title,
permission=platform_entities.Permission.Member,
),
special_title="",
join_timestamp=0,
last_speak_timestamp=0,
mute_time_remaining=0,
),
message_chain=lb_message,
time=event.message.date.timestamp(),
source_platform_object=event
)
@adapter.adapter_class("telegram")
class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
bot: telegram.Bot
application: telegram.ext.Application
bot_account_id: str
message_converter: TelegramMessageConverter = TelegramMessageConverter()
event_converter: TelegramEventConverter = TelegramEventConverter()
@@ -76,15 +171,19 @@ class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
self.ap = ap
async def telegram_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.from_user.is_bot:
return
lb_event = await self.event_converter.target2yiri(update, self.bot)
await self.listeners[type(lb_event)](lb_event, self)
try:
lb_event = await self.event_converter.target2yiri(update, self.bot, self.bot_account_id)
await self.listeners[type(lb_event)](lb_event, self)
except Exception as e:
print(traceback.format_exc())
self.application = ApplicationBuilder().token(self.config['token']).build()
self.bot = self.application.bot
self.application.add_handler(MessageHandler(filters.TEXT | (filters.COMMAND) | filters.PHOTO, telegram_callback))
self.application.add_handler(MessageHandler(filters.TEXT | (filters.COMMAND) | filters.PHOTO , telegram_callback))
async def send_message(
self, target_type: str, target_id: str, message: platform_message.MessageChain
@@ -97,7 +196,21 @@ class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
message: platform_message.MessageChain,
quote_origin: bool = False,
):
pass
assert isinstance(message_source.source_platform_object, Update)
components = await TelegramMessageConverter.yiri2target(message, self.bot)
for component in components:
if component['type'] == 'text':
args = {
"chat_id": message_source.source_platform_object.effective_chat.id,
"text": component['text'],
}
if quote_origin:
args['reply_to_message_id'] = message_source.source_platform_object.message.id
await self.bot.send_message(**args)
async def is_muted(self, group_id: int) -> bool:
return False
@@ -118,7 +231,10 @@ class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
async def run_async(self):
await self.application.initialize()
await self.application.updater.start_polling()
self.bot_account_id = (await self.bot.get_me()).username
await self.application.updater.start_polling(
allowed_updates=Update.ALL_TYPES
)
await self.application.start()
async def kill(self) -> bool: