fix: handle telegram eba non-message updates

This commit is contained in:
Junyan Qin
2026-05-07 16:09:23 +08:00
parent eb475245ab
commit dfcf9d10e4
5 changed files with 268 additions and 103 deletions

View File

@@ -13,15 +13,21 @@ import traceback
import telegram import telegram
import telegram.ext import telegram.ext
from telegram import Update from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, MessageHandler, filters from telegram.ext import (
ApplicationBuilder,
CallbackQueryHandler,
ChatMemberHandler,
ContextTypes,
MessageHandler,
MessageReactionHandler,
filters,
)
import telegramify_markdown import telegramify_markdown
import pydantic import pydantic
from langbot.pkg.utils import httpclient
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import langbot_plugin.api.definition.abstract.platform.event_logger as abstract_platform_logger import langbot_plugin.api.definition.abstract.platform.event_logger as abstract_platform_logger
from langbot.pkg.platform.adapters.telegram.message_converter import TelegramMessageConverter from langbot.pkg.platform.adapters.telegram.message_converter import TelegramMessageConverter
@@ -58,8 +64,14 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
def __init__(self, config: dict, logger: abstract_platform_logger.AbstractEventLogger): def __init__(self, config: dict, logger: abstract_platform_logger.AbstractEventLogger):
async def telegram_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): async def telegram_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not update.message and not update.edited_message and not update.chat_member \ if (
and not update.my_chat_member and not update.callback_query and not update.message_reaction: not update.message
and not update.edited_message
and not update.chat_member
and not update.my_chat_member
and not update.callback_query
and not update.message_reaction
):
return return
# Skip messages from the bot itself # Skip messages from the bot itself
@@ -68,23 +80,16 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
try: try:
# Legacy event type callbacks (compat with existing botmgr FriendMessage / GroupMessage listeners) # Legacy event type callbacks (compat with existing botmgr FriendMessage / GroupMessage listeners)
if update.message and (platform_events.FriendMessage in self.listeners if update.message and (
or platform_events.GroupMessage in self.listeners): platform_events.FriendMessage in self.listeners or platform_events.GroupMessage in self.listeners
):
legacy_event = await self.legacy_event_converter.target2yiri(update, self.bot, self.bot_account_id) legacy_event = await self.legacy_event_converter.target2yiri(update, self.bot, self.bot_account_id)
if legacy_event and type(legacy_event) in self.listeners: if legacy_event and type(legacy_event) in self.listeners:
await self.listeners[type(legacy_event)](legacy_event, self) await self.listeners[type(legacy_event)](legacy_event, self)
# EBA wildcard event callback (Event base class registered as wildcard) eba_event = await self.event_converter.target2yiri(update, self.bot, self.bot_account_id)
if platform_events.Event in self.listeners: if eba_event:
eba_event = await self.event_converter.target2yiri(update, self.bot, self.bot_account_id) await self._dispatch_eba_event(eba_event)
if eba_event:
await self.listeners[platform_events.Event](eba_event, self)
# EBA specific event type callback
if platform_events.EBAEvent in self.listeners:
eba_event = await self.event_converter.target2yiri(update, self.bot, self.bot_account_id)
if eba_event:
await self.listeners[platform_events.EBAEvent](eba_event, self)
except Exception: except Exception:
await self.logger.error(f'Error in telegram callback: {traceback.format_exc()}') await self.logger.error(f'Error in telegram callback: {traceback.format_exc()}')
@@ -106,6 +111,25 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
telegram_callback, telegram_callback,
) )
) )
application.add_handler(
ChatMemberHandler(
telegram_callback,
ChatMemberHandler.CHAT_MEMBER,
)
)
application.add_handler(
ChatMemberHandler(
telegram_callback,
ChatMemberHandler.MY_CHAT_MEMBER,
)
)
application.add_handler(CallbackQueryHandler(telegram_callback))
application.add_handler(
MessageReactionHandler(
telegram_callback,
MessageReactionHandler.MESSAGE_REACTION,
)
)
super().__init__( super().__init__(
config=config, config=config,
@@ -122,35 +146,33 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
def get_supported_events(self) -> list[str]: def get_supported_events(self) -> list[str]:
return [ return [
"message.received", 'message.received',
"message.edited", 'message.edited',
"message.deleted", 'message.reaction',
"message.reaction", 'group.member_joined',
"group.member_joined", 'group.member_left',
"group.member_left", 'group.member_banned',
"group.member_banned", 'bot.invited_to_group',
"group.info_updated", 'bot.removed_from_group',
"bot.invited_to_group",
"bot.removed_from_group",
] ]
def get_supported_apis(self) -> list[str]: def get_supported_apis(self) -> list[str]:
return [ return [
"send_message", 'send_message',
"reply_message", 'reply_message',
"edit_message", 'edit_message',
"delete_message", 'delete_message',
"forward_message", 'forward_message',
"get_group_info", 'get_group_info',
"get_group_member_list", 'get_group_member_list',
"get_group_member_info", 'get_group_member_info',
"get_user_info", 'get_user_info',
"get_file_url", 'get_file_url',
"mute_member", 'mute_member',
"unmute_member", 'unmute_member',
"kick_member", 'kick_member',
"leave_group", 'leave_group',
"call_platform_api", 'call_platform_api',
] ]
# ---- Message Send / Reply (preserving original logic) ---- # ---- Message Send / Reply (preserving original logic) ----
@@ -337,6 +359,14 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
# ---- Event Listeners ---- # ---- Event Listeners ----
async def _dispatch_eba_event(self, event: platform_events.EBAEvent):
"""Dispatch once, preferring the most specific registered listener."""
for event_type in (type(event), platform_events.EBAEvent, platform_events.Event):
callback = self.listeners.get(event_type)
if callback:
await callback(event, self)
return
def register_listener( def register_listener(
self, self,
event_type: typing.Type[platform_events.Event], event_type: typing.Type[platform_events.Event],
@@ -366,7 +396,8 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
handler = PLATFORM_API_MAP.get(action) handler = PLATFORM_API_MAP.get(action)
if handler is None: if handler is None:
from langbot_plugin.api.entities.builtin.platform.errors import NotSupportedError from langbot_plugin.api.entities.builtin.platform.errors import NotSupportedError
raise NotSupportedError(f"call_platform_api:{action}")
raise NotSupportedError(f'call_platform_api:{action}')
return await handler(self.bot, params) return await handler(self.bot, params)
# ---- Lifecycle ---- # ---- Lifecycle ----

View File

@@ -12,7 +12,6 @@ from telegram import Update
import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
from langbot.pkg.platform.adapters.telegram.message_converter import TelegramMessageConverter from langbot.pkg.platform.adapters.telegram.message_converter import TelegramMessageConverter
@@ -22,7 +21,7 @@ def _make_user(tg_user: telegram.User) -> platform_entities.User:
"""Convert a Telegram User to a unified User entity.""" """Convert a Telegram User to a unified User entity."""
return platform_entities.User( return platform_entities.User(
id=tg_user.id, id=tg_user.id,
nickname=tg_user.first_name or "", nickname=tg_user.first_name or '',
username=tg_user.username, username=tg_user.username,
is_bot=tg_user.is_bot, is_bot=tg_user.is_bot,
) )
@@ -32,7 +31,7 @@ def _make_user_group(tg_chat: telegram.Chat) -> platform_entities.UserGroup:
"""Convert a Telegram Chat to a unified UserGroup entity.""" """Convert a Telegram Chat to a unified UserGroup entity."""
return platform_entities.UserGroup( return platform_entities.UserGroup(
id=tg_chat.id, id=tg_chat.id,
name=tg_chat.title or tg_chat.first_name or "", name=tg_chat.title or tg_chat.first_name or '',
description=tg_chat.description if hasattr(tg_chat, 'description') else None, description=tg_chat.description if hasattr(tg_chat, 'description') else None,
) )
@@ -69,8 +68,10 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
import time import time
# ---- Message event ---- # ---- Message event ----
if update.message and update.message.text is not None or ( if (
update.message and (update.message.photo or update.message.voice or update.message.document) update.message
and update.message.text is not None
or (update.message and (update.message.photo or update.message.voice or update.message.document))
): ):
return await TelegramEventConverter._convert_message(update, bot, bot_account_id) return await TelegramEventConverter._convert_message(update, bot, bot_account_id)
@@ -89,15 +90,15 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
# ---- Callback query (button clicks, etc.) ---- # ---- Callback query (button clicks, etc.) ----
if update.callback_query: if update.callback_query:
return platform_events.PlatformSpecificEvent( return platform_events.PlatformSpecificEvent(
type="platform.specific", type='platform.specific',
timestamp=time.time(), timestamp=time.time(),
adapter_name="telegram", adapter_name='telegram',
action="callback_query", action='callback_query',
data={ data={
"callback_query_id": update.callback_query.id, 'callback_query_id': update.callback_query.id,
"data": update.callback_query.data, 'data': update.callback_query.data,
"from_user_id": update.callback_query.from_user.id if update.callback_query.from_user else None, 'from_user_id': update.callback_query.from_user.id if update.callback_query.from_user else None,
"message_id": update.callback_query.message.message_id if update.callback_query.message else None, 'message_id': update.callback_query.message.message_id if update.callback_query.message else None,
}, },
source_platform_object=update, source_platform_object=update,
) )
@@ -108,23 +109,25 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
# ---- Fallback: wrap as PlatformSpecificEvent ---- # ---- Fallback: wrap as PlatformSpecificEvent ----
return platform_events.PlatformSpecificEvent( return platform_events.PlatformSpecificEvent(
type="platform.specific", type='platform.specific',
timestamp=time.time(), timestamp=time.time(),
adapter_name="telegram", adapter_name='telegram',
action="unknown_update", action='unknown_update',
data={"update_id": update.update_id}, data={'update_id': update.update_id},
source_platform_object=update, source_platform_object=update,
) )
@staticmethod @staticmethod
async def _convert_message( async def _convert_message(
update: Update, bot: telegram.Bot, bot_account_id: str, update: Update,
bot: telegram.Bot,
bot_account_id: str,
) -> platform_events.MessageReceivedEvent: ) -> platform_events.MessageReceivedEvent:
"""Convert a Telegram message to MessageReceivedEvent.""" """Convert a Telegram message to MessageReceivedEvent."""
message = update.message message = update.message
lb_message = await TelegramMessageConverter.target2yiri(message, bot, bot_account_id) lb_message = await TelegramMessageConverter.target2yiri(message, bot, bot_account_id)
sender = _make_user(message.from_user) if message.from_user else platform_entities.User(id="") sender = _make_user(message.from_user) if message.from_user else platform_entities.User(id='')
chat = message.chat chat = message.chat
ct = _chat_type(chat) ct = _chat_type(chat)
@@ -133,9 +136,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
group = _make_user_group(chat) group = _make_user_group(chat)
return platform_events.MessageReceivedEvent( return platform_events.MessageReceivedEvent(
type="message.received", type='message.received',
timestamp=message.date.timestamp() if message.date else 0.0, timestamp=message.date.timestamp() if message.date else 0.0,
adapter_name="telegram", adapter_name='telegram',
message_id=message.message_id, message_id=message.message_id,
message_chain=lb_message, message_chain=lb_message,
sender=sender, sender=sender,
@@ -147,13 +150,15 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
@staticmethod @staticmethod
async def _convert_edited_message( async def _convert_edited_message(
update: Update, bot: telegram.Bot, bot_account_id: str, update: Update,
bot: telegram.Bot,
bot_account_id: str,
) -> platform_events.MessageEditedEvent: ) -> platform_events.MessageEditedEvent:
"""Convert a Telegram edited message to MessageEditedEvent.""" """Convert a Telegram edited message to MessageEditedEvent."""
message = update.edited_message message = update.edited_message
lb_message = await TelegramMessageConverter.target2yiri(message, bot, bot_account_id) lb_message = await TelegramMessageConverter.target2yiri(message, bot, bot_account_id)
editor = _make_user(message.from_user) if message.from_user else platform_entities.User(id="") editor = _make_user(message.from_user) if message.from_user else platform_entities.User(id='')
chat = message.chat chat = message.chat
ct = _chat_type(chat) ct = _chat_type(chat)
@@ -162,9 +167,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
group = _make_user_group(chat) group = _make_user_group(chat)
return platform_events.MessageEditedEvent( return platform_events.MessageEditedEvent(
type="message.edited", type='message.edited',
timestamp=message.edit_date.timestamp() if message.edit_date else 0.0, timestamp=message.edit_date.timestamp() if message.edit_date else 0.0,
adapter_name="telegram", adapter_name='telegram',
message_id=message.message_id, message_id=message.message_id,
new_content=lb_message, new_content=lb_message,
editor=editor, editor=editor,
@@ -182,22 +187,27 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
cm = update.chat_member cm = update.chat_member
chat = cm.chat chat = cm.chat
group = _make_user_group(chat) group = _make_user_group(chat)
member = _make_user(cm.new_chat_member.user) if cm.new_chat_member else platform_entities.User(id="") member = _make_user(cm.new_chat_member.user) if cm.new_chat_member else platform_entities.User(id='')
inviter = _make_user(cm.from_user) if cm.from_user else None inviter = _make_user(cm.from_user) if cm.from_user else None
old_status = cm.old_chat_member.status if cm.old_chat_member else None old_status = cm.old_chat_member.status if cm.old_chat_member else None
new_status = cm.new_chat_member.status if cm.new_chat_member else None new_status = cm.new_chat_member.status if cm.new_chat_member else None
# Member joined # Member joined
if old_status in (None, 'left', 'kicked') and new_status in ('member', 'administrator', 'creator', 'restricted'): if old_status in (None, 'left', 'kicked') and new_status in (
'member',
'administrator',
'creator',
'restricted',
):
return platform_events.MemberJoinedEvent( return platform_events.MemberJoinedEvent(
type="group.member_joined", type='group.member_joined',
timestamp=cm.date.timestamp() if cm.date else time.time(), timestamp=cm.date.timestamp() if cm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
member=member, member=member,
inviter=inviter, inviter=inviter,
join_type="invite" if inviter and inviter.id != member.id else "direct", join_type='invite' if inviter and inviter.id != member.id else 'direct',
source_platform_object=update, source_platform_object=update,
) )
@@ -205,9 +215,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
if old_status in ('member', 'administrator', 'creator', 'restricted') and new_status in ('left', 'kicked'): if old_status in ('member', 'administrator', 'creator', 'restricted') and new_status in ('left', 'kicked'):
is_kicked = new_status == 'kicked' is_kicked = new_status == 'kicked'
return platform_events.MemberLeftEvent( return platform_events.MemberLeftEvent(
type="group.member_left", type='group.member_left',
timestamp=cm.date.timestamp() if cm.date else time.time(), timestamp=cm.date.timestamp() if cm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
member=member, member=member,
is_kicked=is_kicked, is_kicked=is_kicked,
@@ -223,9 +233,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
if hasattr(restricted, 'until_date') and restricted.until_date: if hasattr(restricted, 'until_date') and restricted.until_date:
duration = int(restricted.until_date.timestamp() - time.time()) duration = int(restricted.until_date.timestamp() - time.time())
return platform_events.MemberBannedEvent( return platform_events.MemberBannedEvent(
type="group.member_banned", type='group.member_banned',
timestamp=cm.date.timestamp() if cm.date else time.time(), timestamp=cm.date.timestamp() if cm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
member=member, member=member,
operator=inviter, operator=inviter,
@@ -235,15 +245,15 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
# Other chat_member changes -> PlatformSpecificEvent # Other chat_member changes -> PlatformSpecificEvent
return platform_events.PlatformSpecificEvent( return platform_events.PlatformSpecificEvent(
type="platform.specific", type='platform.specific',
timestamp=cm.date.timestamp() if cm.date else time.time(), timestamp=cm.date.timestamp() if cm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
action="chat_member_updated", action='chat_member_updated',
data={ data={
"old_status": old_status, 'old_status': old_status,
"new_status": new_status, 'new_status': new_status,
"chat_id": chat.id, 'chat_id': chat.id,
"user_id": member.id, 'user_id': member.id,
}, },
source_platform_object=update, source_platform_object=update,
) )
@@ -264,9 +274,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
# Bot invited to group # Bot invited to group
if old_status in (None, 'left', 'kicked') and new_status in ('member', 'administrator'): if old_status in (None, 'left', 'kicked') and new_status in ('member', 'administrator'):
return platform_events.BotInvitedToGroupEvent( return platform_events.BotInvitedToGroupEvent(
type="bot.invited_to_group", type='bot.invited_to_group',
timestamp=mcm.date.timestamp() if mcm.date else time.time(), timestamp=mcm.date.timestamp() if mcm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
inviter=inviter, inviter=inviter,
source_platform_object=update, source_platform_object=update,
@@ -275,9 +285,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
# Bot removed from group # Bot removed from group
if old_status in ('member', 'administrator', 'creator') and new_status in ('left', 'kicked'): if old_status in ('member', 'administrator', 'creator') and new_status in ('left', 'kicked'):
return platform_events.BotRemovedFromGroupEvent( return platform_events.BotRemovedFromGroupEvent(
type="bot.removed_from_group", type='bot.removed_from_group',
timestamp=mcm.date.timestamp() if mcm.date else time.time(), timestamp=mcm.date.timestamp() if mcm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
operator=inviter, operator=inviter,
source_platform_object=update, source_platform_object=update,
@@ -291,9 +301,9 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
if hasattr(restricted, 'until_date') and restricted.until_date: if hasattr(restricted, 'until_date') and restricted.until_date:
duration = int(restricted.until_date.timestamp() - time.time()) duration = int(restricted.until_date.timestamp() - time.time())
return platform_events.BotMutedEvent( return platform_events.BotMutedEvent(
type="bot.muted", type='bot.muted',
timestamp=mcm.date.timestamp() if mcm.date else time.time(), timestamp=mcm.date.timestamp() if mcm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
group=group, group=group,
operator=inviter, operator=inviter,
duration=duration, duration=duration,
@@ -301,14 +311,14 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
) )
return platform_events.PlatformSpecificEvent( return platform_events.PlatformSpecificEvent(
type="platform.specific", type='platform.specific',
timestamp=mcm.date.timestamp() if mcm.date else time.time(), timestamp=mcm.date.timestamp() if mcm.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
action="my_chat_member_updated", action='my_chat_member_updated',
data={ data={
"old_status": old_status, 'old_status': old_status,
"new_status": new_status, 'new_status': new_status,
"chat_id": chat.id, 'chat_id': chat.id,
}, },
source_platform_object=update, source_platform_object=update,
) )
@@ -330,7 +340,7 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
elif hasattr(r, 'custom_emoji_id'): elif hasattr(r, 'custom_emoji_id'):
new_emojis.append(str(r.custom_emoji_id)) new_emojis.append(str(r.custom_emoji_id))
user = platform_entities.User(id="") user = platform_entities.User(id='')
if reaction.user: if reaction.user:
user = _make_user(reaction.user) user = _make_user(reaction.user)
@@ -338,12 +348,12 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
group = _make_user_group(chat) if ct == platform_entities.ChatType.GROUP else None group = _make_user_group(chat) if ct == platform_entities.ChatType.GROUP else None
return platform_events.MessageReactionEvent( return platform_events.MessageReactionEvent(
type="message.reaction", type='message.reaction',
timestamp=reaction.date.timestamp() if reaction.date else time.time(), timestamp=reaction.date.timestamp() if reaction.date else time.time(),
adapter_name="telegram", adapter_name='telegram',
message_id=reaction.message_id, message_id=reaction.message_id,
user=user, user=user,
reaction=new_emojis[0] if new_emojis else "", reaction=new_emojis[0] if new_emojis else '',
is_add=len(new_emojis) > 0, is_add=len(new_emojis) > 0,
chat_type=ct, chat_type=ct,
chat_id=chat.id, chat_id=chat.id,

View File

@@ -41,12 +41,10 @@ spec:
supported_events: supported_events:
- message.received - message.received
- message.edited - message.edited
- message.deleted
- message.reaction - message.reaction
- group.member_joined - group.member_joined
- group.member_left - group.member_left
- group.member_banned - group.member_banned
- group.info_updated
- bot.invited_to_group - bot.invited_to_group
- bot.removed_from_group - bot.removed_from_group

View File

@@ -6,7 +6,6 @@ Migrated from the original sources/telegram.py TelegramMessageConverter. Logic u
from __future__ import annotations from __future__ import annotations
import base64 import base64
import typing
import telegram import telegram

View File

@@ -0,0 +1,127 @@
from __future__ import annotations
import pathlib
import pytest
import yaml
from telegram.ext import CallbackQueryHandler, ChatMemberHandler, MessageHandler, MessageReactionHandler
from langbot.pkg.platform.adapters.telegram.adapter import TelegramAdapter
from langbot_plugin.api.definition.abstract.platform.event_logger import AbstractEventLogger
from langbot_plugin.api.entities.builtin.platform import entities as platform_entities
from langbot_plugin.api.entities.builtin.platform import events as platform_events
from langbot_plugin.api.entities.builtin.platform import message as platform_message
class DummyLogger(AbstractEventLogger):
async def info(self, text, images=None, message_session_id=None, no_throw=True):
pass
async def debug(self, text, images=None, message_session_id=None, no_throw=True):
pass
async def warning(self, text, images=None, message_session_id=None, no_throw=True):
pass
async def error(self, text, images=None, message_session_id=None, no_throw=True):
pass
def make_adapter() -> TelegramAdapter:
return TelegramAdapter(
{
'token': '123456:ABCDEF_fake_token_for_object_parsing',
'markdown_card': False,
'enable-stream-reply': False,
},
DummyLogger(),
)
def test_telegram_adapter_registers_all_declared_update_handlers():
adapter = make_adapter()
handlers = adapter.application.handlers[0]
assert sum(isinstance(handler, MessageHandler) for handler in handlers) == 2
assert sum(isinstance(handler, ChatMemberHandler) for handler in handlers) == 2
assert any(isinstance(handler, CallbackQueryHandler) for handler in handlers)
assert any(isinstance(handler, MessageReactionHandler) for handler in handlers)
@pytest.mark.asyncio
async def test_telegram_adapter_dispatches_only_most_specific_eba_listener():
adapter = make_adapter()
calls: list[str] = []
async def wildcard_listener(event, adapter):
calls.append('event')
async def eba_listener(event, adapter):
calls.append('eba')
async def message_listener(event, adapter):
calls.append('message.received')
adapter.register_listener(platform_events.Event, wildcard_listener)
adapter.register_listener(platform_events.EBAEvent, eba_listener)
adapter.register_listener(platform_events.MessageReceivedEvent, message_listener)
event = platform_events.MessageReceivedEvent(
message_id=1,
message_chain=platform_message.MessageChain([platform_message.Plain(text='hello')]),
sender=platform_entities.User(id=1),
chat_id=1,
)
await adapter._dispatch_eba_event(event)
assert calls == ['message.received']
@pytest.mark.asyncio
async def test_telegram_adapter_dispatch_falls_back_to_eba_then_event_listener():
adapter = make_adapter()
calls: list[str] = []
async def wildcard_listener(event, adapter):
calls.append('event')
async def eba_listener(event, adapter):
calls.append('eba')
adapter.register_listener(platform_events.Event, wildcard_listener)
adapter.register_listener(platform_events.EBAEvent, eba_listener)
event = platform_events.MessageEditedEvent(
message_id=1,
new_content=platform_message.MessageChain([platform_message.Plain(text='edited')]),
editor=platform_entities.User(id=1),
chat_id=1,
)
await adapter._dispatch_eba_event(event)
assert calls == ['eba']
adapter.unregister_listener(platform_events.EBAEvent, eba_listener)
await adapter._dispatch_eba_event(event)
assert calls == ['eba', 'event']
def test_telegram_supported_events_match_manifest():
adapter_events = make_adapter().get_supported_events()
manifest_path = (
pathlib.Path(__file__).parents[3]
/ 'src'
/ 'langbot'
/ 'pkg'
/ 'platform'
/ 'adapters'
/ 'telegram'
/ 'manifest.yaml'
)
manifest_events = yaml.safe_load(manifest_path.read_text())['spec']['supported_events']
assert adapter_events == manifest_events
assert 'message.deleted' not in adapter_events
assert 'group.info_updated' not in adapter_events