feat: route telegram eba events to plugins

This commit is contained in:
Junyan Qin
2026-05-07 17:02:49 +08:00
parent e4a471af18
commit 5c182c0f29
9 changed files with 573 additions and 14 deletions

View File

@@ -154,6 +154,9 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
'group.member_banned',
'bot.invited_to_group',
'bot.removed_from_group',
'bot.muted',
'bot.unmuted',
'platform.specific',
]
def get_supported_apis(self) -> list[str]:
@@ -221,27 +224,41 @@ class TelegramAdapter(TelegramAPIMixin, abstract_platform_adapter.AbstractPlatfo
components = await TelegramMessageConverter.yiri2target(message, self.bot)
for component in components:
if component['type'] == 'text':
component_type = component.get('type')
args = {
'chat_id': message_source.source_platform_object.effective_chat.id,
}
if message_source.source_platform_object.message.message_thread_id:
args['message_thread_id'] = message_source.source_platform_object.message.message_thread_id
if quote_origin:
args['reply_to_message_id'] = message_source.source_platform_object.message.id
if component_type == 'text':
if self.config['markdown_card'] is True:
content = telegramify_markdown.markdownify(
content=component['text'],
)
else:
content = component['text']
args = {
'chat_id': message_source.source_platform_object.effective_chat.id,
'text': content,
}
if self.config['markdown_card'] is True:
args['parse_mode'] = 'MarkdownV2'
if message_source.source_platform_object.message.message_thread_id:
args['message_thread_id'] = message_source.source_platform_object.message.message_thread_id
if quote_origin:
args['reply_to_message_id'] = message_source.source_platform_object.message.id
await self.bot.send_message(**args)
args['text'] = content
await self.bot.send_message(**args)
elif component_type == 'photo':
photo = component.get('photo')
if photo is None:
continue
args['photo'] = telegram.InputFile(photo)
await self.bot.send_photo(**args)
elif component_type == 'document':
doc = component.get('document')
if doc is None:
continue
filename = component.get('filename', 'file')
args['document'] = telegram.InputFile(doc, filename=filename)
await self.bot.send_document(**args)
# ---- Streaming Output (preserving original logic) ----

View File

@@ -310,6 +310,16 @@ class TelegramEventConverter(abstract_platform_adapter.AbstractEventConverter):
source_platform_object=update,
)
if old_status == 'restricted' and new_status in ('member', 'administrator') and mcm.new_chat_member:
return platform_events.BotUnmutedEvent(
type='bot.unmuted',
timestamp=mcm.date.timestamp() if mcm.date else time.time(),
adapter_name='telegram',
group=group,
operator=inviter,
source_platform_object=update,
)
return platform_events.PlatformSpecificEvent(
type='platform.specific',
timestamp=mcm.date.timestamp() if mcm.date else time.time(),

View File

@@ -47,6 +47,9 @@ spec:
- group.member_banned
- bot.invited_to_group
- bot.removed_from_group
- bot.muted
- bot.unmuted
- platform.specific
supported_apis:
required:

View File

@@ -18,6 +18,7 @@ from ..entity.errors import platform as platform_errors
from .logger import EventLogger
import langbot_plugin.api.entities.builtin.provider.session as provider_session
import langbot_plugin.api.entities.events as plugin_events
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
@@ -77,6 +78,31 @@ class RuntimeBot:
PIPELINE_DISCARD = '__discard__'
PIPELINE_DISCARD_DISPLAY_NAME = 'Discarded'
@staticmethod
def _eba_event_to_plugin_event(event: platform_events.EBAEvent) -> plugin_events.BaseEventModel | None:
"""Map a platform EBA event to a plugin EventListener event model."""
event_mapping: list[tuple[type[platform_events.EBAEvent], type[plugin_events.BaseEventModel]]] = [
(platform_events.MessageReceivedEvent, plugin_events.MessageReceived),
(platform_events.MessageEditedEvent, plugin_events.MessageEdited),
(platform_events.MessageDeletedEvent, plugin_events.MessageDeleted),
(platform_events.MessageReactionEvent, plugin_events.MessageReactionReceived),
(platform_events.FeedbackReceivedEvent, plugin_events.FeedbackReceived),
(platform_events.MemberJoinedEvent, plugin_events.GroupMemberJoined),
(platform_events.MemberLeftEvent, plugin_events.GroupMemberLeft),
(platform_events.MemberBannedEvent, plugin_events.GroupMemberBanned),
(platform_events.BotInvitedToGroupEvent, plugin_events.BotInvitedToGroup),
(platform_events.BotRemovedFromGroupEvent, plugin_events.BotRemovedFromGroup),
(platform_events.BotMutedEvent, plugin_events.BotMuted),
(platform_events.BotUnmutedEvent, plugin_events.BotUnmuted),
(platform_events.PlatformSpecificEvent, plugin_events.PlatformSpecificEventReceived),
]
for platform_event_type, plugin_event_type in event_mapping:
if isinstance(event, platform_event_type):
return plugin_event_type.from_platform_event(event)
return None
def resolve_pipeline_uuid(
self,
launcher_type: str,
@@ -366,6 +392,21 @@ class RuntimeBot:
self.adapter.register_listener(platform_events.FeedbackEvent, on_feedback)
async def on_eba_event(
event: platform_events.EBAEvent,
adapter: abstract_platform_adapter.AbstractMessagePlatformAdapter,
):
plugin_event = self._eba_event_to_plugin_event(event)
if plugin_event is None:
return
try:
await self.ap.plugin_connector.emit_event(plugin_event)
except Exception:
await self.logger.error(f'Failed to dispatch EBA event to plugins: {traceback.format_exc()}')
self.adapter.register_listener(platform_events.EBAEvent, on_eba_event)
async def run(self):
async def exception_wrapper():
try: