增强消息处理功能,支持编辑消息

This commit is contained in:
jxxghp
2025-06-15 19:18:18 +08:00
parent f8fbfdbba7
commit c18247f3b1
4 changed files with 161 additions and 42 deletions
+52 -21
View File
@@ -115,11 +115,19 @@ class MessageChain(ChainBase):
if not text: if not text:
logger.debug(f'未识别到消息内容::{body}{form}{args}') logger.debug(f'未识别到消息内容::{body}{form}{args}')
return return
# 获取原消息ID信息
original_message_id = info.message_id
original_chat_id = info.chat_id
# 处理消息 # 处理消息
self.handle_message(channel=channel, source=source, userid=userid, username=username, text=text) self.handle_message(channel=channel, source=source, userid=userid, username=username, text=text,
original_message_id=original_message_id, original_chat_id=original_chat_id)
def handle_message(self, channel: MessageChannel, source: str, def handle_message(self, channel: MessageChannel, source: str,
userid: Union[str, int], username: str, text: str) -> None: userid: Union[str, int], username: str, text: str,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None) -> None:
""" """
识别消息内容,执行操作 识别消息内容,执行操作
""" """
@@ -150,7 +158,7 @@ class MessageChain(ChainBase):
if text.startswith('CALLBACK:'): if text.startswith('CALLBACK:'):
# 处理按钮回调(适配支持回调的渠道) # 处理按钮回调(适配支持回调的渠道)
if ChannelCapabilityManager.supports_callbacks(channel): if ChannelCapabilityManager.supports_callbacks(channel):
self._handle_callback(text, channel, source, userid, username) self._handle_callback(text, channel, source, userid, username, original_message_id, original_chat_id)
else: else:
logger.warning(f"渠道 {channel.value} 不支持回调,但收到了回调消息:{text}") logger.warning(f"渠道 {channel.value} 不支持回调,但收到了回调消息:{text}")
elif text.startswith('/'): elif text.startswith('/'):
@@ -269,7 +277,9 @@ class MessageChain(ChainBase):
title=mediainfo.title, title=mediainfo.title,
items=contexts[:self._page_size], items=contexts[:self._page_size],
userid=userid, userid=userid,
total=len(contexts)) total=len(contexts),
original_message_id=original_message_id,
original_chat_id=original_chat_id)
elif cache_type in ["Subscribe", "ReSubscribe"]: elif cache_type in ["Subscribe", "ReSubscribe"]:
# 订阅或洗版媒体 # 订阅或洗版媒体
@@ -350,7 +360,9 @@ class MessageChain(ChainBase):
title=_current_media.title, title=_current_media.title,
items=cache_list[start:end], items=cache_list[start:end],
userid=userid, userid=userid,
total=len(cache_list)) total=len(cache_list),
original_message_id=original_message_id,
original_chat_id=original_chat_id)
else: else:
# 发送媒体数据 # 发送媒体数据
self.__post_medias_message(channel=channel, self.__post_medias_message(channel=channel,
@@ -358,7 +370,9 @@ class MessageChain(ChainBase):
title=_current_meta.name, title=_current_meta.name,
items=cache_list[start:end], items=cache_list[start:end],
userid=userid, userid=userid,
total=len(cache_list)) total=len(cache_list),
original_message_id=original_message_id,
original_chat_id=original_chat_id)
elif text.lower() == "n": elif text.lower() == "n":
# 下一页 # 下一页
@@ -390,7 +404,9 @@ class MessageChain(ChainBase):
title=_current_media.title, title=_current_media.title,
items=cache_list, items=cache_list,
userid=userid, userid=userid,
total=total) total=total,
original_message_id=original_message_id,
original_chat_id=original_chat_id)
else: else:
# 发送媒体数据 # 发送媒体数据
self.__post_medias_message(channel=channel, self.__post_medias_message(channel=channel,
@@ -398,7 +414,9 @@ class MessageChain(ChainBase):
title=_current_meta.name, title=_current_meta.name,
items=cache_list, items=cache_list,
userid=userid, userid=userid,
total=total) total=total,
original_message_id=original_message_id,
original_chat_id=original_chat_id)
else: else:
# 搜索或订阅 # 搜索或订阅
@@ -458,7 +476,9 @@ class MessageChain(ChainBase):
source=source, source=source,
title=meta.name, title=meta.name,
items=medias[:self._page_size], items=medias[:self._page_size],
userid=userid, total=len(medias)) userid=userid, total=len(medias),
original_message_id=original_message_id,
original_chat_id=original_chat_id)
else: else:
# 广播事件 # 广播事件
self.eventmanager.send_event( self.eventmanager.send_event(
@@ -481,7 +501,9 @@ class MessageChain(ChainBase):
gc.collect() gc.collect()
def _handle_callback(self, text: str, channel: MessageChannel, source: str, def _handle_callback(self, text: str, channel: MessageChannel, source: str,
userid: Union[str, int], username: str) -> None: userid: Union[str, int], username: str,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None) -> None:
""" """
处理按钮回调 处理按钮回调
""" """
@@ -505,14 +527,14 @@ class MessageChain(ChainBase):
# 解析系统回调数据 # 解析系统回调数据
if callback_data.startswith("page_"): if callback_data.startswith("page_"):
# 翻页操作(旧格式,保持兼容) # 翻页操作
self._handle_page_callback(callback_data, channel, source, userid) self._handle_page_callback(callback_data, channel, source, userid, original_message_id, original_chat_id)
elif callback_data.startswith("select_"): elif callback_data.startswith("select_"):
# 选择操作或翻页操作 # 选择操作或翻页操作
if callback_data in ["select_p", "select_n"]: if callback_data in ["select_p", "select_n"]:
# 翻页操作:直接调用原来的文本处理逻辑 # 翻页操作
page_text = callback_data.split("_")[1] # 提取 "p" 或 "n" page_text = callback_data.split("_")[1] # 提取 "p" 或 "n"
self.handle_message(channel, source, userid, username, page_text) self.handle_message(channel, source, userid, username, page_text, original_message_id, original_chat_id)
else: else:
# 选择操作 # 选择操作
self._handle_select_callback(callback_data, channel, source, userid, username) self._handle_select_callback(callback_data, channel, source, userid, username)
@@ -527,7 +549,8 @@ class MessageChain(ChainBase):
logger.info(f"未知的回调数据:{callback_data}") logger.info(f"未知的回调数据:{callback_data}")
def _handle_page_callback(self, callback_data: str, channel: MessageChannel, source: str, def _handle_page_callback(self, callback_data: str, channel: MessageChannel, source: str,
userid: Union[str, int]) -> None: userid: Union[str, int], original_message_id: Optional[int],
original_chat_id: Optional[str]):
""" """
处理翻页回调 处理翻页回调
""" """
@@ -540,10 +563,10 @@ class MessageChain(ChainBase):
# 判断是上一页还是下一页 # 判断是上一页还是下一页
if page < _current_page: if page < _current_page:
# 上一页,调用原来的 "p" 逻辑 # 上一页,调用原来的 "p" 逻辑
self.handle_message(channel, source, userid, "", "p") self.handle_message(channel, source, userid, "", "p", original_message_id, original_chat_id)
elif page > _current_page: elif page > _current_page:
# 下一页,调用原来的 "n" 逻辑 # 下一页,调用原来的 "n" 逻辑
self.handle_message(channel, source, userid, "", "n") self.handle_message(channel, source, userid, "", "n", original_message_id, original_chat_id)
# 如果 page == _current_page,说明是当前页,不需要处理 # 如果 page == _current_page,说明是当前页,不需要处理
except (ValueError, IndexError) as e: except (ValueError, IndexError) as e:
@@ -640,7 +663,9 @@ class MessageChain(ChainBase):
note=note) note=note)
def __post_medias_message(self, channel: MessageChannel, source: str, def __post_medias_message(self, channel: MessageChannel, source: str,
title: str, items: list, userid: str, total: int): title: str, items: list, userid: str, total: int,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None):
""" """
发送媒体列表消息 发送媒体列表消息
""" """
@@ -668,7 +693,9 @@ class MessageChain(ChainBase):
source=source, source=source,
title=title, title=title,
userid=userid, userid=userid,
buttons=buttons buttons=buttons,
original_message_id=original_message_id,
original_chat_id=original_chat_id
) )
self.post_medias_message(notification, medias=items) self.post_medias_message(notification, medias=items)
@@ -725,7 +752,9 @@ class MessageChain(ChainBase):
return buttons return buttons
def __post_torrents_message(self, channel: MessageChannel, source: str, def __post_torrents_message(self, channel: MessageChannel, source: str,
title: str, items: list, userid: str, total: int): title: str, items: list, userid: str, total: int,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None):
""" """
发送种子列表消息 发送种子列表消息
""" """
@@ -754,7 +783,9 @@ class MessageChain(ChainBase):
title=title, title=title,
userid=userid, userid=userid,
link=settings.MP_DOMAIN('#/resource'), link=settings.MP_DOMAIN('#/resource'),
buttons=buttons buttons=buttons,
original_message_id=original_message_id,
original_chat_id=original_chat_id
) )
self.post_torrents_message(notification, torrents=items) self.post_torrents_message(notification, torrents=items)
+9 -3
View File
@@ -246,7 +246,9 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
if client: if client:
client.send_msg(title=message.title, text=message.text, client.send_msg(title=message.title, text=message.text,
image=message.image, userid=userid, link=message.link, image=message.image, userid=userid, link=message.link,
buttons=message.buttons) buttons=message.buttons,
original_message_id=message.original_message_id,
original_chat_id=message.original_chat_id)
def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> None: def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> None:
""" """
@@ -262,7 +264,9 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
if client: if client:
client.send_medias_msg(title=message.title, medias=medias, client.send_medias_msg(title=message.title, medias=medias,
userid=message.userid, link=message.link, userid=message.userid, link=message.link,
buttons=message.buttons) buttons=message.buttons,
original_message_id=message.original_message_id,
original_chat_id=message.original_chat_id)
def post_torrents_message(self, message: Notification, torrents: List[Context]) -> None: def post_torrents_message(self, message: Notification, torrents: List[Context]) -> None:
""" """
@@ -278,7 +282,9 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
if client: if client:
client.send_torrents_msg(title=message.title, torrents=torrents, client.send_torrents_msg(title=message.title, torrents=torrents,
userid=message.userid, link=message.link, userid=message.userid, link=message.link,
buttons=message.buttons) buttons=message.buttons,
original_message_id=message.original_message_id,
original_chat_id=message.original_chat_id)
def register_commands(self, commands: Dict[str, dict]): def register_commands(self, commands: Dict[str, dict]):
""" """
+89 -18
View File
@@ -9,6 +9,7 @@ from urllib.parse import urljoin
import telebot import telebot
from telebot import apihelper from telebot import apihelper
from telebot.types import InputFile, InlineKeyboardMarkup, InlineKeyboardButton from telebot.types import InputFile, InlineKeyboardMarkup, InlineKeyboardButton
from telebot.types import InputMediaPhoto
from app.core.config import settings from app.core.config import settings
from app.core.context import MediaInfo, Context from app.core.context import MediaInfo, Context
@@ -119,7 +120,9 @@ class Telegram:
def send_msg(self, title: str, text: Optional[str] = None, image: Optional[str] = None, def send_msg(self, title: str, text: Optional[str] = None, image: Optional[str] = None,
userid: Optional[str] = None, link: Optional[str] = None, userid: Optional[str] = None, link: Optional[str] = None,
buttons: Optional[List[List[dict]]] = None) -> Optional[bool]: buttons: Optional[List[List[dict]]] = None,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None) -> Optional[bool]:
""" """
发送Telegram消息 发送Telegram消息
:param title: 消息标题 :param title: 消息标题
@@ -128,6 +131,8 @@ class Telegram:
:param userid: 用户ID,如有则只发消息给该用户 :param userid: 用户ID,如有则只发消息给该用户
:param link: 跳转链接 :param link: 跳转链接
:param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]] :param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]]
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
:userid: 发送消息的目标用户ID,为空则发给管理员 :userid: 发送消息的目标用户ID,为空则发给管理员
""" """
if not self._telegram_token or not self._telegram_chat_id: if not self._telegram_token or not self._telegram_chat_id:
@@ -158,7 +163,13 @@ class Telegram:
if buttons: if buttons:
reply_markup = self._create_inline_keyboard(buttons) reply_markup = self._create_inline_keyboard(buttons)
return self.__send_request(userid=chat_id, image=image, caption=caption, reply_markup=reply_markup) # 判断是编辑消息还是发送新消息
if original_message_id and original_chat_id:
# 编辑消息
return self.__edit_message(original_chat_id, original_message_id, caption, buttons, image)
else:
# 发送新消息
return self.__send_request(userid=chat_id, image=image, caption=caption, reply_markup=reply_markup)
except Exception as msg_e: except Exception as msg_e:
logger.error(f"发送消息失败:{msg_e}") logger.error(f"发送消息失败:{msg_e}")
@@ -166,7 +177,9 @@ class Telegram:
def send_medias_msg(self, medias: List[MediaInfo], userid: Optional[str] = None, def send_medias_msg(self, medias: List[MediaInfo], userid: Optional[str] = None,
title: Optional[str] = None, link: Optional[str] = None, title: Optional[str] = None, link: Optional[str] = None,
buttons: Optional[List[List[Dict]]] = None) -> Optional[bool]: buttons: Optional[List[List[Dict]]] = None,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None) -> Optional[bool]:
""" """
发送媒体列表消息 发送媒体列表消息
:param medias: 媒体信息列表 :param medias: 媒体信息列表
@@ -174,6 +187,8 @@ class Telegram:
:param title: 消息标题 :param title: 消息标题
:param link: 跳转链接 :param link: 跳转链接
:param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]] :param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]]
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
""" """
if not self._telegram_token or not self._telegram_chat_id: if not self._telegram_token or not self._telegram_chat_id:
return None return None
@@ -211,7 +226,13 @@ class Telegram:
if buttons: if buttons:
reply_markup = self._create_inline_keyboard(buttons) reply_markup = self._create_inline_keyboard(buttons)
return self.__send_request(userid=chat_id, image=image, caption=caption, reply_markup=reply_markup) # 判断是编辑消息还是发送新消息
if original_message_id and original_chat_id:
# 编辑消息
return self.__edit_message(original_chat_id, original_message_id, caption, buttons, image)
else:
# 发送新消息
return self.__send_request(userid=chat_id, image=image, caption=caption, reply_markup=reply_markup)
except Exception as msg_e: except Exception as msg_e:
logger.error(f"发送消息失败:{msg_e}") logger.error(f"发送消息失败:{msg_e}")
@@ -219,24 +240,25 @@ class Telegram:
def send_torrents_msg(self, torrents: List[Context], def send_torrents_msg(self, torrents: List[Context],
userid: Optional[str] = None, title: Optional[str] = None, userid: Optional[str] = None, title: Optional[str] = None,
link: Optional[str] = None, buttons: Optional[List[List[Dict]]] = None) -> Optional[bool]: link: Optional[str] = None, buttons: Optional[List[List[Dict]]] = None,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None) -> Optional[bool]:
""" """
发送列表消息 发送种子列表消息
:param torrents: Torrent信息列表 :param torrents: 种子信息列表
:param userid: 用户ID,如有则只发消息给该用户 :param userid: 用户ID,如有则只发消息给该用户
:param title: 消息标题 :param title: 消息标题
:param link: 跳转链接 :param link: 跳转链接
:param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]] :param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]]
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
""" """
if not self._telegram_token or not self._telegram_chat_id: if not self._telegram_token or not self._telegram_chat_id:
return None return None
if not torrents:
return False
try: try:
index, caption = 1, "*%s*" % title index, caption = 1, "*%s*" % title
mediainfo = torrents[0].media_info image = torrents[0].media_info.get_message_image()
for context in torrents: for context in torrents:
torrent = context.torrent_info torrent = context.torrent_info
site_name = torrent.site_name site_name = torrent.site_name
@@ -266,8 +288,13 @@ class Telegram:
if buttons: if buttons:
reply_markup = self._create_inline_keyboard(buttons) reply_markup = self._create_inline_keyboard(buttons)
return self.__send_request(userid=chat_id, caption=caption, # 判断是编辑消息还是发送新消息
image=mediainfo.get_message_image(), reply_markup=reply_markup) if original_message_id and original_chat_id:
# 编辑消息(种子消息通常没有图片)
return self.__edit_message(original_chat_id, original_message_id, caption, buttons, image)
else:
# 发送新消息
return self.__send_request(userid=chat_id, image=image, caption=caption, reply_markup=reply_markup)
except Exception as msg_e: except Exception as msg_e:
logger.error(f"发送消息失败:{msg_e}") logger.error(f"发送消息失败:{msg_e}")
@@ -298,18 +325,62 @@ class Telegram:
show_alert: bool = False) -> Optional[bool]: show_alert: bool = False) -> Optional[bool]:
""" """
回应回调查询 回应回调查询
:param callback_query_id: 回调查询ID
:param text: 提示文本
:param show_alert: 是否显示弹窗提示
:return: 回应结果
""" """
if not self._bot:
return None
try: try:
self._bot.answer_callback_query(callback_query_id, text, show_alert) self._bot.answer_callback_query(callback_query_id, text=text, show_alert=show_alert)
return True return True
except Exception as e: except Exception as e:
logger.error(f"回应回调查询失败:{str(e)}") logger.error(f"回应回调查询失败:{str(e)}")
return False return False
def __edit_message(self, chat_id: str, message_id: int, text: str,
buttons: Optional[List[List[dict]]] = None,
image: Optional[str] = None) -> Optional[bool]:
"""
编辑已发送的消息
:param chat_id: 聊天ID
:param message_id: 消息ID
:param text: 新的消息内容
:param buttons: 按钮列表
:param image: 图片URL或路径
:return: 编辑是否成功
"""
if not self._bot:
return None
try:
# 创建按钮键盘
reply_markup = None
if buttons:
reply_markup = self._create_inline_keyboard(buttons)
if image:
# 如果有图片,使用edit_message_media
media = InputMediaPhoto(media=image, caption=text, parse_mode="Markdown")
self._bot.edit_message_media(
chat_id=chat_id,
message_id=message_id,
media=media,
reply_markup=reply_markup
)
else:
# 如果没有图片,使用edit_message_text
self._bot.edit_message_text(
chat_id=chat_id,
message_id=message_id,
text=text,
parse_mode="Markdown",
reply_markup=reply_markup
)
return True
except Exception as e:
logger.error(f"编辑消息失败:{str(e)}")
return False
@retry(Exception, logger=logger) @retry(Exception, logger=logger)
def __send_request(self, userid: Optional[str] = None, image="", caption="", def __send_request(self, userid: Optional[str] = None, image="", caption="",
reply_markup: Optional[InlineKeyboardMarkup] = None) -> bool: reply_markup: Optional[InlineKeyboardMarkup] = None) -> bool:
+11
View File
@@ -79,6 +79,10 @@ class Notification(BaseModel):
targets: Optional[dict] = None targets: Optional[dict] = None
# 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据", "url": "链接"}]] # 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据", "url": "链接"}]]
buttons: Optional[List[List[dict]]] = None buttons: Optional[List[List[dict]]] = None
# 原消息ID,用于编辑消息
original_message_id: Optional[int] = None
# 原消息的聊天ID,用于编辑消息
original_chat_id: Optional[str] = None
def to_dict(self): def to_dict(self):
""" """
@@ -279,6 +283,13 @@ class ChannelCapabilityManager:
""" """
return cls.supports_capability(channel, ChannelCapability.CALLBACK_QUERIES) return cls.supports_capability(channel, ChannelCapability.CALLBACK_QUERIES)
@classmethod
def supports_editing(cls, channel: MessageChannel) -> bool:
"""
检查渠道是否支持消息编辑
"""
return cls.supports_capability(channel, ChannelCapability.MESSAGE_EDITING)
@classmethod @classmethod
def get_max_buttons_per_row(cls, channel: MessageChannel) -> int: def get_max_buttons_per_row(cls, channel: MessageChannel) -> int:
""" """