Skip to content

Commit 6dc1fcb

Browse files
committed
Merge branch 'v2' of https://github.com/jxxghp/MoviePilot into v2
2 parents b599ef4 + 526b6a1 commit 6dc1fcb

File tree

4 files changed

+130
-69
lines changed

4 files changed

+130
-69
lines changed

‎app/agent/__init__.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ def _initialize_llm(self):
6969
"""初始化LLM模型"""
7070
provider = settings.LLM_PROVIDER.lower()
7171
api_key = settings.LLM_API_KEY
72-
if not api_key:
73-
raise ValueError("未配置 LLM_API_KEY")
7472

7573
if provider == "google":
7674
if settings.PROXY_HOST:

‎app/chain/message.py‎

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -931,19 +931,11 @@ def _handle_ai_message(self, text: str, channel: MessageChannel, source: str,
931931
))
932932
return
933933

934-
# 检查LLM配置
935-
if not settings.LLM_API_KEY:
936-
self.post_message(Notification(
937-
channel=channel,
938-
source=source,
939-
userid=userid,
940-
username=username,
941-
title="MoviePilot智能助未配置,请在系统设置中配置"
942-
))
943-
return
944-
945934
# 提取用户消息
946-
user_message = text[3:].strip() # 移除 "/ai" 前缀
935+
if text.lower().startswith("/ai"):
936+
user_message = text[3:].strip() # 移除 "/ai" 前缀(大小写不敏感)
937+
else:
938+
user_message = text.strip() # 按原消息处理
947939
if not user_message:
948940
self.post_message(Notification(
949941
channel=channel,

‎app/core/cache.py‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,13 +1024,11 @@ def fresh(fresh: bool = True):
10241024
with fresh():
10251025
result = some_cached_function()
10261026
"""
1027-
token = _fresh.set(fresh)
1028-
logger.debug(f"Setting fresh mode to {fresh}. {id(token):#x}")
1027+
token = _fresh.set(fresh or is_fresh())
10291028
try:
10301029
yield
10311030
finally:
10321031
_fresh.reset(token)
1033-
logger.debug(f"Reset fresh mode. {id(token):#x}")
10341032

10351033
@asynccontextmanager
10361034
async def async_fresh(fresh: bool = True):
@@ -1041,13 +1039,11 @@ async def async_fresh(fresh: bool = True):
10411039
async with async_fresh():
10421040
result = await some_async_cached_function()
10431041
"""
1044-
token = _fresh.set(fresh)
1045-
logger.debug(f"Setting async_fresh mode to {fresh}. {id(token):#x}")
1042+
token = _fresh.set(fresh or is_fresh())
10461043
try:
10471044
yield
10481045
finally:
10491046
_fresh.reset(token)
1050-
logger.debug(f"Reset async_fresh mode. {id(token):#x}")
10511047

10521048
def is_fresh() -> bool:
10531049
"""

‎app/modules/telegram/telegram.py‎

Lines changed: 124 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
import asyncio
12
import re
23
import threading
3-
import uuid
4-
from pathlib import Path
54
from threading import Event
65
from typing import Optional, List, Dict, Callable
76
from urllib.parse import urljoin
87

98
import telebot
9+
from telegramify_markdown import standardize, telegramify
10+
from telegramify_markdown.type import ContentTypes, SentType
1011
from telebot import apihelper
11-
from telebot.types import InputFile, InlineKeyboardMarkup, InlineKeyboardButton
12+
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
1213
from telebot.types import InputMediaPhoto
1314

1415
from app.core.config import settings
1516
from app.core.context import MediaInfo, Context
1617
from app.core.metainfo import MetaInfo
18+
from app.helper.thread import ThreadHelper
1719
from app.log import logger
1820
from app.utils.common import retry
1921
from app.utils.http import RequestUtils
@@ -52,7 +54,7 @@ def __init__(self, TELEGRAM_TOKEN: Optional[str] = None, TELEGRAM_CHAT_ID: Optio
5254
else:
5355
apihelper.proxy = settings.PROXY
5456
# bot
55-
_bot = telebot.TeleBot(self._telegram_token, parse_mode="Markdown")
57+
_bot = telebot.TeleBot(self._telegram_token, parse_mode="MarkdownV2")
5658
# 记录句柄
5759
self._bot = _bot
5860
# 获取并存储bot用户名用于@检测
@@ -236,12 +238,14 @@ def send_msg(self, title: str, text: Optional[str] = None, image: Optional[str]
236238
return False
237239

238240
try:
239-
if text:
240-
# 对text进行Markdown特殊字符转义
241-
text = re.sub(r"([_`])", r"\\\1", text)
242-
caption = f"*{title}*\n{text}"
241+
if title and text:
242+
caption = f"**{title}**\n{text}"
243+
elif title:
244+
caption = f"**{title}**"
245+
elif text:
246+
caption = text
243247
else:
244-
caption = f"*{title}*"
248+
caption = ""
245249

246250
if link:
247251
caption = f"{caption}\n[查看详情]({link})"
@@ -499,7 +503,7 @@ def __edit_message(self, chat_id: str, message_id: int, text: str,
499503

500504
if image:
501505
# 如果有图片,使用edit_message_media
502-
media = InputMediaPhoto(media=image, caption=text, parse_mode="Markdown")
506+
media = InputMediaPhoto(media=image, caption=standardize(text), parse_mode="MarkdownV2")
503507
self._bot.edit_message_media(
504508
chat_id=chat_id,
505509
message_id=message_id,
@@ -511,58 +515,129 @@ def __edit_message(self, chat_id: str, message_id: int, text: str,
511515
self._bot.edit_message_text(
512516
chat_id=chat_id,
513517
message_id=message_id,
514-
text=text,
515-
parse_mode="Markdown",
518+
text=standardize(text),
519+
parse_mode="MarkdownV2",
516520
reply_markup=reply_markup
517521
)
518522
return True
519523
except Exception as e:
520524
logger.error(f"编辑消息失败:{str(e)}")
521525
return False
522526

523-
@retry(RetryException, logger=logger)
524527
def __send_request(self, userid: Optional[str] = None, image="", caption="",
525528
reply_markup: Optional[InlineKeyboardMarkup] = None) -> bool:
526529
"""
527530
向Telegram发送报文
528531
:param reply_markup: 内联键盘
529532
"""
530-
if image:
531-
res = RequestUtils(proxies=settings.PROXY, ua=settings.NORMAL_USER_AGENT).get_res(image)
532-
if res is None:
533-
raise Exception("获取图片失败")
534-
if res.content:
535-
# 使用随机标识构建图片文件的完整路径,并写入图片内容到文件
536-
image_file = Path(settings.TEMP_PATH) / "telegram" / str(uuid.uuid4())
537-
if not image_file.parent.exists():
538-
image_file.parent.mkdir(parents=True, exist_ok=True)
539-
image_file.write_bytes(res.content)
540-
photo = InputFile(image_file)
541-
# 发送图片到Telegram
542-
ret = self._bot.send_photo(chat_id=userid or self._telegram_chat_id,
543-
photo=photo,
544-
caption=caption,
545-
parse_mode="Markdown",
546-
reply_markup=reply_markup)
547-
if ret is None:
548-
raise RetryException("发送图片消息失败")
549-
return True
550-
# 按4096分段循环发送消息
551-
ret = None
552-
if len(caption) > 4095:
553-
for i in range(0, len(caption), 4095):
554-
ret = self._bot.send_message(chat_id=userid or self._telegram_chat_id,
555-
text=caption[i:i + 4095],
556-
parse_mode="Markdown",
557-
reply_markup=reply_markup if i == 0 else None)
558-
else:
559-
ret = self._bot.send_message(chat_id=userid or self._telegram_chat_id,
560-
text=caption,
561-
parse_mode="Markdown",
562-
reply_markup=reply_markup)
563-
if ret is None:
564-
raise RetryException("发送文本消息失败")
565-
return True if ret else False
533+
kwargs = {
534+
'chat_id': userid or self._telegram_chat_id,
535+
'parse_mode': "MarkdownV2",
536+
'reply_markup': reply_markup
537+
}
538+
539+
try:
540+
# 处理图片
541+
image = self.__process_image(image) if image else None
542+
543+
# 图片消息的标题长度限制为1024,文本消息为4096
544+
caption_limit = 1024 if image else 4096
545+
if len(caption) < caption_limit:
546+
ret = self.__send_short_message(image, caption, **kwargs)
547+
else:
548+
sent_idx = set()
549+
ret = self.__send_long_message(image, caption, sent_idx, **kwargs)
550+
551+
return ret is not None
552+
except Exception as e:
553+
logger.error(f"发送Telegram消息���败: {e}")
554+
return False
555+
556+
@retry(RetryException, logger=logger)
557+
def __process_image(self, image_url: str) -> bytes:
558+
"""
559+
处理图片URL,获取图片内容
560+
"""
561+
try:
562+
res = RequestUtils(
563+
proxies=settings.PROXY,
564+
ua=settings.NORMAL_USER_AGENT
565+
).get_res(image_url)
566+
567+
if not res or not res.content:
568+
raise RetryException("获取图片失败")
569+
570+
return res.content
571+
except Exception as e:
572+
raise
573+
574+
@retry(RetryException, logger=logger)
575+
def __send_short_message(self, image: Optional[bytes], caption: str, **kwargs):
576+
"""
577+
发送短消息
578+
"""
579+
try:
580+
if image:
581+
return self._bot.send_photo(
582+
photo=image,
583+
caption=standardize(caption),
584+
**kwargs
585+
)
586+
else:
587+
return self._bot.send_message(
588+
text=standardize(caption),
589+
**kwargs
590+
)
591+
except Exception as e:
592+
raise RetryException(f"发送{'图片' if image else '文本'}消息失败")
593+
594+
@retry(RetryException, logger=logger)
595+
def __send_long_message(self, image: Optional[bytes], caption: str, sent_idx: set, **kwargs):
596+
"""
597+
发送长消息
598+
"""
599+
try:
600+
reply_markup = kwargs.pop("reply_markup", None)
601+
602+
boxs: SentType = ThreadHelper().submit(lambda x: asyncio.run(telegramify(x)), caption).result()
603+
604+
ret = None
605+
for i, item in enumerate(boxs):
606+
if i in sent_idx:
607+
# 跳过已发送消息
608+
continue
609+
610+
current_reply_markup = reply_markup if i == 0 else None
611+
612+
if item.content_type == ContentTypes.TEXT and (i != 0 or not image):
613+
ret = self._bot.send_message(**kwargs,
614+
text=item.content,
615+
reply_markup=current_reply_markup
616+
)
617+
618+
elif item.content_type == ContentTypes.PHOTO or (image and i == 0):
619+
ret = self._bot.send_photo(**kwargs,
620+
photo=(getattr(item, "file_name", ""),
621+
getattr(item, "file_data", image)),
622+
caption=getattr(item, "caption", item.content),
623+
reply_markup=current_reply_markup
624+
)
625+
626+
elif item.content_type == ContentTypes.FILE:
627+
ret = self._bot.send_document(**kwargs,
628+
document=(item.file_name, item.file_data),
629+
caption=item.caption,
630+
reply_markup=current_reply_markup
631+
)
632+
633+
sent_idx.add(i)
634+
635+
return ret
636+
except Exception as e:
637+
try:
638+
raise RetryException(f"消息 [{i + 1}/{len(boxs)}] 发送失败") from e
639+
except NameError:
640+
raise RetryException("发送长消息失败") from e
566641

567642
def register_commands(self, commands: Dict[str, dict]):
568643
"""

0 commit comments

Comments
 (0)