mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
feat(telegram): 优化Telegram文件下载与base64转换逻辑,重构消息发送相关代码
- 新增 TelegramModule.download_file_to_base64 方法,统一文件下载与base64编码 - Telegram 客户端新增 download_file 方法,简化文件下载流程 - 消息图片下载逻辑调整为通过模块方法调用,移除冗余静态方法 - 修复部分参数格式与空格风格,提升代码一致性 - 优化长消息发送异常处理与代码结构
This commit is contained in:
@@ -251,7 +251,7 @@ class MoviePilotAgent:
|
|||||||
if start_idx > 0:
|
if start_idx > 0:
|
||||||
on_token(buffer[:start_idx])
|
on_token(buffer[:start_idx])
|
||||||
in_think_tag = True
|
in_think_tag = True
|
||||||
buffer = buffer[start_idx + 7 :]
|
buffer = buffer[start_idx + 7:]
|
||||||
else:
|
else:
|
||||||
# 检查是否以 <think> 的前缀结尾
|
# 检查是否以 <think> 的前缀结尾
|
||||||
partial_match = False
|
partial_match = False
|
||||||
@@ -269,7 +269,7 @@ class MoviePilotAgent:
|
|||||||
end_idx = buffer.find("</think>")
|
end_idx = buffer.find("</think>")
|
||||||
if end_idx != -1:
|
if end_idx != -1:
|
||||||
in_think_tag = False
|
in_think_tag = False
|
||||||
buffer = buffer[end_idx + 8 :]
|
buffer = buffer[end_idx + 8:]
|
||||||
else:
|
else:
|
||||||
# 检查是否以 </think> 的前缀结尾
|
# 检查是否以 </think> 的前缀结尾
|
||||||
partial_match = False
|
partial_match = False
|
||||||
|
|||||||
+4
-43
@@ -1204,9 +1204,8 @@ class MessageChain(ChainBase):
|
|||||||
f"AI智能体处理失败: {str(e)}", role="system", title="MoviePilot助手"
|
f"AI智能体处理失败: {str(e)}", role="system", title="MoviePilot助手"
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _download_images_to_base64(
|
def _download_images_to_base64(
|
||||||
images: List[str], channel: MessageChannel, source: str
|
self, images: List[str], channel: MessageChannel, source: str
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
"""
|
"""
|
||||||
下载图片并转为base64
|
下载图片并转为base64
|
||||||
@@ -1218,7 +1217,9 @@ class MessageChain(ChainBase):
|
|||||||
try:
|
try:
|
||||||
if img.startswith("tg://file_id/"):
|
if img.startswith("tg://file_id/"):
|
||||||
file_id = img.replace("tg://file_id/", "")
|
file_id = img.replace("tg://file_id/", "")
|
||||||
base64_data = MessageChain._download_telegram_file(file_id, source)
|
base64_data = self.run_module(
|
||||||
|
"download_file_to_base64", file_id=file_id, source=source
|
||||||
|
)
|
||||||
if base64_data:
|
if base64_data:
|
||||||
base64_images.append(f"data:image/jpeg;base64,{base64_data}")
|
base64_images.append(f"data:image/jpeg;base64,{base64_data}")
|
||||||
elif img.startswith("http"):
|
elif img.startswith("http"):
|
||||||
@@ -1232,43 +1233,3 @@ class MessageChain(ChainBase):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"下载图片失败: {img}, error: {e}")
|
logger.error(f"下载图片失败: {img}, error: {e}")
|
||||||
return base64_images if base64_images else None
|
return base64_images if base64_images else None
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _download_telegram_file(file_id: str, source: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
下载Telegram文件并转为base64
|
|
||||||
"""
|
|
||||||
from app.modules.telegram import TelegramModule
|
|
||||||
|
|
||||||
module = TelegramModule()
|
|
||||||
config = module.get_config(source)
|
|
||||||
if not config:
|
|
||||||
return None
|
|
||||||
client = module.get_instance(config.name)
|
|
||||||
if not client or not client._bot:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
file_info = client._bot.get_file(file_id)
|
|
||||||
file_url = f"https://api.telegram.org/file/bot{client._telegram_token}/{file_info.file_path}"
|
|
||||||
resp = RequestUtils(timeout=30).get_res(file_url)
|
|
||||||
if resp and resp.content:
|
|
||||||
import base64
|
|
||||||
|
|
||||||
return base64.b64encode(resp.content).decode()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"下载Telegram文件失败: {e}")
|
|
||||||
return None
|
|
||||||
client = module.get_instance(config.name)
|
|
||||||
if not client:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
file_info = client.bot.get_file(file_id)
|
|
||||||
file_url = f"https://api.telegram.org/file/bot{client._telegram_token}/{file_info.file_path}"
|
|
||||||
resp = RequestUtils(timeout=30).get_res(file_url)
|
|
||||||
if resp and resp.content:
|
|
||||||
import base64
|
|
||||||
|
|
||||||
return base64.b64encode(resp.content).decode()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"下载Telegram文件失败: {e}")
|
|
||||||
return None
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
|||||||
|
|
||||||
# Remove mention at the beginning with optional following space
|
# Remove mention at the beginning with optional following space
|
||||||
if cleaned.startswith(mention_pattern):
|
if cleaned.startswith(mention_pattern):
|
||||||
cleaned = cleaned[len(mention_pattern) :].lstrip()
|
cleaned = cleaned[len(mention_pattern):].lstrip()
|
||||||
|
|
||||||
# Remove mention at any other position
|
# Remove mention at any other position
|
||||||
cleaned = cleaned.replace(mention_pattern, "").strip()
|
cleaned = cleaned.replace(mention_pattern, "").strip()
|
||||||
@@ -525,3 +525,23 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
|||||||
f"Command set has changed, Updating new commands: {filtered_scoped_commands}"
|
f"Command set has changed, Updating new commands: {filtered_scoped_commands}"
|
||||||
)
|
)
|
||||||
client.register_commands(filtered_scoped_commands)
|
client.register_commands(filtered_scoped_commands)
|
||||||
|
|
||||||
|
def download_file_to_base64(self, file_id: str, source: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
下载Telegram文件并转为base64
|
||||||
|
:param file_id: Telegram文件ID
|
||||||
|
:param source: 来源名称
|
||||||
|
:return: base64编码的图片数据
|
||||||
|
"""
|
||||||
|
config = self.get_config(source)
|
||||||
|
if not config:
|
||||||
|
return None
|
||||||
|
client = self.get_instance(config.name)
|
||||||
|
if not client:
|
||||||
|
return None
|
||||||
|
file_content = client.download_file(file_id)
|
||||||
|
if file_content:
|
||||||
|
import base64
|
||||||
|
|
||||||
|
return base64.b64encode(file_content).decode()
|
||||||
|
return None
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ from telebot.types import (
|
|||||||
InlineKeyboardButton,
|
InlineKeyboardButton,
|
||||||
InputMediaPhoto,
|
InputMediaPhoto,
|
||||||
)
|
)
|
||||||
from telegramify_markdown import standardize, telegramify
|
from telegramify_markdown import standardize, telegramify # noqa
|
||||||
from telegramify_markdown.type import ContentTypes, SentType
|
from telegramify_markdown.type import ContentTypes, SentType
|
||||||
|
|
||||||
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
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
from app.helper.thread import ThreadHelper
|
|
||||||
from app.helper.image import ImageHelper
|
from app.helper.image import ImageHelper
|
||||||
|
from app.helper.thread import ThreadHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.common import retry
|
from app.utils.common import retry
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
@@ -171,6 +171,14 @@ class Telegram:
|
|||||||
self._polling_thread.start()
|
self._polling_thread.start()
|
||||||
logger.info("Telegram消息接收服务启动")
|
logger.info("Telegram消息接收服务启动")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bot(self):
|
||||||
|
"""
|
||||||
|
获取Telegram Bot实例
|
||||||
|
:return: TeleBot实例或None
|
||||||
|
"""
|
||||||
|
return self._bot
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bot_username(self) -> Optional[str]:
|
def bot_username(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@@ -179,6 +187,24 @@ class Telegram:
|
|||||||
"""
|
"""
|
||||||
return self._bot_username
|
return self._bot_username
|
||||||
|
|
||||||
|
def download_file(self, file_id: str) -> Optional[bytes]:
|
||||||
|
"""
|
||||||
|
下载Telegram文件
|
||||||
|
:param file_id: 文件ID
|
||||||
|
:return: 文件字节数据
|
||||||
|
"""
|
||||||
|
if not self._bot:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
file_info = self._bot.get_file(file_id)
|
||||||
|
file_url = f"https://api.telegram.org/file/bot{self._telegram_token}/{file_info.file_path}"
|
||||||
|
resp = RequestUtils(timeout=30).get_res(file_url)
|
||||||
|
if resp and resp.content:
|
||||||
|
return resp.content
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"下载Telegram文件失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def _update_user_chat_mapping(self, userid: int, chat_id: int) -> None:
|
def _update_user_chat_mapping(self, userid: int, chat_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
更新用户与聊天的映射关系
|
更新用户与聊天的映射关系
|
||||||
@@ -229,7 +255,7 @@ class Telegram:
|
|||||||
for entity in message.entities:
|
for entity in message.entities:
|
||||||
if entity.type == "mention":
|
if entity.type == "mention":
|
||||||
mention_text = message.text[
|
mention_text = message.text[
|
||||||
entity.offset : entity.offset + entity.length
|
entity.offset: entity.offset + entity.length
|
||||||
]
|
]
|
||||||
if mention_text == f"@{self._bot_username}":
|
if mention_text == f"@{self._bot_username}":
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -324,6 +350,9 @@ class Telegram:
|
|||||||
logger.warn("标题和内容不能同时为空")
|
logger.warn("标题和内容不能同时为空")
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
|
|
||||||
|
# Determine target chat_id with improved logic using user mapping
|
||||||
|
chat_id = self._determine_target_chat_id(userid, original_chat_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 标准化标题后再加粗,避免**符号被显示为文本
|
# 标准化标题后再加粗,避免**符号被显示为文本
|
||||||
bold_title = (
|
bold_title = (
|
||||||
@@ -341,9 +370,6 @@ class Telegram:
|
|||||||
if link:
|
if link:
|
||||||
caption = f"{caption}\n[查看详情]({link})"
|
caption = f"{caption}\n[查看详情]({link})"
|
||||||
|
|
||||||
# Determine target chat_id with improved logic using user mapping
|
|
||||||
chat_id = self._determine_target_chat_id(userid, original_chat_id)
|
|
||||||
|
|
||||||
# 创建按钮键盘
|
# 创建按钮键盘
|
||||||
reply_markup = None
|
reply_markup = None
|
||||||
if buttons:
|
if buttons:
|
||||||
@@ -800,7 +826,7 @@ class Telegram:
|
|||||||
"""
|
"""
|
||||||
发送长消息
|
发送长消息
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
reply_markup = kwargs.pop("reply_markup", None)
|
reply_markup = kwargs.pop("reply_markup", None)
|
||||||
|
|
||||||
boxs: SentType = (
|
boxs: SentType = (
|
||||||
@@ -815,6 +841,7 @@ class Telegram:
|
|||||||
# 跳过已发送消息
|
# 跳过已发送消息
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
current_reply_markup = reply_markup if i == 0 else None
|
current_reply_markup = reply_markup if i == 0 else None
|
||||||
|
|
||||||
if item.content_type == ContentTypes.TEXT and (i != 0 or not image):
|
if item.content_type == ContentTypes.TEXT and (i != 0 or not image):
|
||||||
@@ -843,13 +870,14 @@ class Telegram:
|
|||||||
|
|
||||||
sent_idx.add(i)
|
sent_idx.add(i)
|
||||||
|
|
||||||
return ret
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
raise RetryException(f"消息 [{i + 1}/{len(boxs)}] 发送失败") from e
|
raise RetryException(f"消息 [{i + 1}/{len(boxs)}] 发送失败") from e
|
||||||
except NameError:
|
except NameError:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
def register_commands(self, commands: Dict[str, dict]):
|
def register_commands(self, commands: Dict[str, dict]):
|
||||||
"""
|
"""
|
||||||
注册菜单命令
|
注册菜单命令
|
||||||
|
|||||||
Reference in New Issue
Block a user