diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 8d488909..f2241e34 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -1478,7 +1478,8 @@ class ChainBase(metaclass=ABCMeta): if not message: logger.warning("消息为空,跳过发送") return - self.messageoper.add(**message.model_dump()) + if message.save_history: + self.messageoper.add(**message.model_dump()) dispatch_message = self._normalize_notification_for_dispatch(message) # 发送消息按设置隔离 if not dispatch_message.userid and dispatch_message.mtype: @@ -1593,7 +1594,8 @@ class ChainBase(metaclass=ABCMeta): if not message: logger.warning("消息为空,跳过发送") return - await self.messageoper.async_add(**message.model_dump()) + if message.save_history: + await self.messageoper.async_add(**message.model_dump()) dispatch_message = self._normalize_notification_for_dispatch(message) # 发送消息按设置隔离 if not dispatch_message.userid and dispatch_message.mtype: @@ -1684,7 +1686,8 @@ class ChainBase(metaclass=ABCMeta): :return: 成功或失败 """ note_list = [media.to_dict() for media in medias] - self.messageoper.add(**message.model_dump(), note=note_list) + if message.save_history: + self.messageoper.add(**message.model_dump(), note=note_list) dispatch_message = self._normalize_notification_for_dispatch(message) return self.messagequeue.send_message( "post_medias_message", @@ -1703,7 +1706,8 @@ class ChainBase(metaclass=ABCMeta): :return: 成功或失败 """ note_list = [torrent.torrent_info.to_dict() for torrent in torrents] - self.messageoper.add(**message.model_dump(), note=note_list) + if message.save_history: + self.messageoper.add(**message.model_dump(), note=note_list) dispatch_message = self._normalize_notification_for_dispatch(message) return self.messagequeue.send_message( "post_torrents_message", diff --git a/app/chain/download.py b/app/chain/download.py index 07b18371..c0c6a651 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -1337,7 +1337,8 @@ class DownloadChain(ChainBase): mtype=NotificationType.Download, title="没有正在下载的任务!", userid=userid, - link=settings.MP_DOMAIN('#/downloading') + link=settings.MP_DOMAIN('#/downloading'), + save_history=False, )) return # 发送消息 @@ -1356,7 +1357,8 @@ class DownloadChain(ChainBase): title=title, text="\n".join(messages), userid=userid, - link=settings.MP_DOMAIN('#/downloading') + link=settings.MP_DOMAIN('#/downloading'), + save_history=False, )) def downloading(self, name: Optional[str] = None) -> List[DownloaderTorrent]: diff --git a/app/chain/message.py b/app/chain/message.py index fb05955b..13ed616c 100644 --- a/app/chain/message.py +++ b/app/chain/message.py @@ -194,6 +194,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="语音识别失败,请稍后重试", + save_history=False, ) ) return @@ -298,6 +299,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="请输入要使用传统交互处理的内容", + save_history=False, ) ) return False @@ -623,6 +625,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="回调数据格式错误,请检查!", + save_history=False, ) ) return False @@ -751,6 +754,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="该选择已失效,请重新发起选择", + save_history=False, ) ) return False @@ -823,6 +827,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title=f"开始重新整理记录 #{history_id} ...", + save_history=False, ) ) @@ -836,6 +841,7 @@ class MessageChain(ChainBase): username=username, title=f"整理记录 #{history_id} 已重新整理", link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) return @@ -849,6 +855,7 @@ class MessageChain(ChainBase): title="重新整理失败", text=errmsg, link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) @@ -902,6 +909,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="MoviePilot智能助手未启用,请在系统设置中启用", + save_history=False, ) ) return @@ -917,6 +925,7 @@ class MessageChain(ChainBase): title="重新整理失败", text=f"整理记录 #{history_id} 不存在", link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) return @@ -932,6 +941,7 @@ class MessageChain(ChainBase): title=f"已将整理记录 #{history_id} 交给智能助手处理", text="处理完成后会在这里回复结果。", link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) @@ -960,6 +970,7 @@ class MessageChain(ChainBase): text=final_output.strip() or f"整理记录 #{history_id} 已由智能助手处理完成。", link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) except Exception as e: @@ -972,6 +983,7 @@ class MessageChain(ChainBase): title="智能助手整理失败", text=str(e), link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) @@ -1093,6 +1105,7 @@ class MessageChain(ChainBase): source=source, title="智能体会话已清除,下次将创建新的会话", userid=userid, + save_history=False, ) ) else: @@ -1102,6 +1115,7 @@ class MessageChain(ChainBase): source=source, title="您当前没有活跃的智能体会话", userid=userid, + save_history=False, ) ) @@ -1137,6 +1151,7 @@ class MessageChain(ChainBase): source=source, title="智能体推理已应急停止,会话记忆已保留,您可以继续对话", userid=userid, + save_history=False, ) ) else: @@ -1146,6 +1161,7 @@ class MessageChain(ChainBase): source=source, title="当前没有正在执行的智能体任务", userid=userid, + save_history=False, ) ) else: @@ -1155,6 +1171,7 @@ class MessageChain(ChainBase): source=source, title="您当前没有活跃的智能体会话", userid=userid, + save_history=False, ) ) @@ -1210,6 +1227,7 @@ class MessageChain(ChainBase): source=source, title="您当前没有活跃的智能体会话", userid=userid, + save_history=False, ) ) return @@ -1223,6 +1241,7 @@ class MessageChain(ChainBase): title="当前智能体会话状态", text=self._format_session_status_text(status), userid=userid, + save_history=False, ) ) @@ -1253,6 +1272,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="MoviePilot智能助手未启用,请在系统设置中启用", + save_history=False, ) ) return False @@ -1274,6 +1294,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="请输入您的问题或需求", + save_history=False, ) ) return False @@ -1297,6 +1318,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="附件读取失败,请稍后重试", + save_history=False, ) ) return False @@ -1315,6 +1337,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="附件读取失败,请稍后重试", + save_history=False, ) ) return False @@ -1335,6 +1358,7 @@ class MessageChain(ChainBase): userid=userid, username=username, title="文件读取失败,请稍后重试", + save_history=False, ) ) return False @@ -2002,6 +2026,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title="交互已失效,请重新搜索或订阅", + save_history=False, ) ) return True @@ -2115,6 +2140,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title="媒体交互已结束", + save_history=False, ) ) return True @@ -2282,6 +2308,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"{meta.name} 没有找到对应的媒体信息!", + save_history=False, ) ) return @@ -2386,6 +2413,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"【{mediainfo.title_year}{request.meta.sea} 媒体库中已存在,如需重新下载请发送:搜索 名称 或 下载 名称】", + save_history=False, ) ) return @@ -2405,6 +2433,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"{mediainfo.title_year}:\n" + "\n".join(messages), + save_history=False, ) ) @@ -2416,6 +2445,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"开始搜索 {mediainfo.type.value} {mediainfo.title_year} ...", + save_history=False, ) ) @@ -2428,6 +2458,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"{mediainfo.title}{request.meta.sea} 未搜索到需要的资源!", + save_history=False, ) ) return @@ -2501,6 +2532,7 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=f"【{mediainfo.title_year}{request.meta.sea} 媒体库中已存在,如需洗版请发送:洗版 XXX】", + save_history=False, ) ) return @@ -2904,6 +2936,7 @@ class MediaInteractionChain(ChainBase): buttons=buttons, original_message_id=original_message_id, original_chat_id=original_chat_id, + save_history=False, ), medias=page_items, ) @@ -2953,6 +2986,7 @@ class MediaInteractionChain(ChainBase): buttons=buttons, original_message_id=original_message_id, original_chat_id=original_chat_id, + save_history=False, ), torrents=page_items, ) @@ -3006,6 +3040,7 @@ class MediaInteractionChain(ChainBase): buttons=buttons, original_message_id=original_message_id, original_chat_id=original_chat_id, + save_history=False, ) ) @@ -3324,5 +3359,6 @@ class MediaInteractionChain(ChainBase): userid=userid, username=username, title=title, + save_history=False, ) ) diff --git a/app/chain/site.py b/app/chain/site.py index e11e2a39..aa5786dc 100644 --- a/app/chain/site.py +++ b/app/chain/site.py @@ -4,18 +4,10 @@ from datetime import datetime from typing import List, Optional, Tuple, Union, Dict from urllib.parse import urljoin +from app.helper.sites import SitesHelper # noqa from lxml import etree from app.chain import ChainBase -from app.helper.interaction import ( - SlashInteractionManager, - build_navigation_buttons, - format_markdown_table, - page_items, - supports_interaction_buttons, - supports_markdown, - update_or_post_message, -) from app.core.config import global_vars, settings from app.core.event import Event, eventmanager from app.db.models.site import Site @@ -25,8 +17,16 @@ from app.helper.browser import PlaywrightHelper from app.helper.cloudflare import under_challenge from app.helper.cookie import CookieHelper from app.helper.cookiecloud import CookieCloudHelper +from app.helper.interaction import ( + SlashInteractionManager, + build_navigation_buttons, + format_markdown_table, + page_items, + supports_interaction_buttons, + supports_markdown, + update_or_post_message, +) from app.helper.rss import RssHelper -from app.helper.sites import SitesHelper # noqa from app.log import logger from app.schemas import MessageChannel, Notification, SiteUserData from app.schemas.types import EventType, NotificationType @@ -34,7 +34,6 @@ from app.utils.http import RequestUtils from app.utils.site import SiteUtils from app.utils.string import StringUtils - site_interaction_manager = SlashInteractionManager() @@ -359,7 +358,7 @@ class SiteChain(ChainBase): if global_vars.is_system_stopped: logger.info("系统正在停止,中断CookieCloud同步") return False, "系统正在停止,同步被中断" - + # 索引器信息 indexer = siteshelper.get_indexer(domain) # 数据库的站点信息 @@ -642,11 +641,11 @@ class SiteChain(ChainBase): return True, "连接成功" def remote_list( - self, - arg_str: str = "", - channel: MessageChannel = None, - userid: Union[str, int] = None, - source: Optional[str] = None, + self, + arg_str: str = "", + channel: MessageChannel = None, + userid: Union[str, int] = None, + source: Optional[str] = None, ): """ /sites 统一入口。 @@ -660,11 +659,11 @@ class SiteChain(ChainBase): ) normalized_arg = (arg_str or "").strip() if normalized_arg and self.handle_text_interaction( - channel=channel, - source=source, - userid=userid, - username="", - text=normalized_arg, + channel=channel, + source=source, + userid=userid, + username="", + text=normalized_arg, ): return self._render_site_interaction( @@ -688,14 +687,14 @@ class SiteChain(ChainBase): return parts[1], parts[2] def handle_callback_interaction( - self, - callback_data: str, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, - original_message_id: Optional[Union[str, int]] = None, - original_chat_id: Optional[str] = None, + self, + callback_data: str, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, + original_message_id: Optional[Union[str, int]] = None, + original_chat_id: Optional[str] = None, ) -> bool: """ 处理 /sites 按钮交互。 @@ -760,12 +759,12 @@ class SiteChain(ChainBase): return True def handle_text_interaction( - self, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, - text: str, + self, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, + text: str, ) -> bool: """ 处理 /sites 文本补充输入。 @@ -987,14 +986,14 @@ class SiteChain(ChainBase): return True def _render_site_interaction( - self, - request, - channel: MessageChannel, - source: Optional[str], - userid: Union[str, int], - username: Optional[str], - original_message_id: Optional[Union[str, int]] = None, - original_chat_id: Optional[str] = None, + self, + request, + channel: MessageChannel, + source: Optional[str], + userid: Union[str, int], + username: Optional[str], + original_message_id: Optional[Union[str, int]] = None, + original_chat_id: Optional[str] = None, ) -> None: """ 渲染 /sites 当前页面。 @@ -1202,7 +1201,8 @@ class SiteChain(ChainBase): self.post_message(Notification( channel=channel, title=f"站点编号 {site_id} 不存在!", - userid=userid)) + userid=userid, + save_history=False)) return # 禁用站点 siteoper.update(site_id, { @@ -1229,7 +1229,9 @@ class SiteChain(ChainBase): if not site: self.post_message(Notification( channel=channel, - title=f"站点编号 {site_id} 不存在!", userid=userid)) + title=f"站点编号 {site_id} 不存在!", + userid=userid, + save_history=False)) return # 禁用站点 siteoper.update(site_id, { @@ -1280,7 +1282,9 @@ class SiteChain(ChainBase): self.post_message(Notification( channel=channel, source=source, - title=err_title, userid=userid)) + title=err_title, + userid=userid, + save_history=False)) return arg_str = str(arg_str).strip() args = arg_str.split() @@ -1292,14 +1296,18 @@ class SiteChain(ChainBase): self.post_message(Notification( channel=channel, source=source, - title=err_title, userid=userid)) + title=err_title, + userid=userid, + save_history=False)) return site_id = args[0] if not site_id.isdigit(): self.post_message(Notification( channel=channel, source=source, - title=err_title, userid=userid)) + title=err_title, + userid=userid, + save_history=False)) return # 站点ID site_id = int(site_id) @@ -1309,12 +1317,16 @@ class SiteChain(ChainBase): self.post_message(Notification( channel=channel, source=source, - title=f"站点编号 {site_id} 不存在!", userid=userid)) + title=f"站点编号 {site_id} 不存在!", + userid=userid, + save_history=False)) return self.post_message(Notification( channel=channel, source=source, - title=f"开始更新【{site_info.name}】Cookie&UA ...", userid=userid)) + title=f"开始更新【{site_info.name}】Cookie&UA ...", + userid=userid, + save_history=False)) # 用户名 username = args[1] # 密码 @@ -1331,13 +1343,15 @@ class SiteChain(ChainBase): source=source, title=f"【{site_info.name}】 Cookie&UA更新失败!", text=f"错误原因:{msg}", - userid=userid)) + userid=userid, + save_history=False)) else: self.post_message(Notification( channel=channel, source=source, title=f"【{site_info.name}】 Cookie&UA更新成功", - userid=userid)) + userid=userid, + save_history=False)) def remote_refresh_userdatas(self, channel: MessageChannel, userid: Union[str, int] = None, source: Optional[str] = None): @@ -1349,7 +1363,8 @@ class SiteChain(ChainBase): channel=channel, source=source, title="开始刷新站点数据 ...", - userid=userid + userid=userid, + save_history=False, )) # 刷新站点数据 site_datas = self.refresh_userdatas() @@ -1392,12 +1407,14 @@ class SiteChain(ChainBase): source=source, title="【站点数据统计】", text="\n".join(sorted_messages), - userid=userid + userid=userid, + save_history=False )) else: self.post_message(Notification( channel=channel, source=source, title="没有刷新到任何站点数据!", - userid=userid + userid=userid, + save_history=False, )) diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 2a13680c..ceea2ec1 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -12,15 +12,6 @@ from app.chain import ChainBase from app.chain.download import DownloadChain from app.chain.media import MediaChain from app.chain.search import SearchChain -from app.helper.interaction import ( - SlashInteractionManager, - build_navigation_buttons, - format_markdown_table, - page_items, - supports_interaction_buttons, - supports_markdown, - update_or_post_message, -) from app.chain.tmdb import TmdbChain from app.chain.torrents import TorrentsChain from app.core.config import settings, global_vars @@ -34,6 +25,15 @@ from app.db.models.subscribe import Subscribe from app.db.site_oper import SiteOper from app.db.subscribe_oper import SubscribeOper from app.db.systemconfig_oper import SystemConfigOper +from app.helper.interaction import ( + SlashInteractionManager, + build_navigation_buttons, + format_markdown_table, + page_items, + supports_interaction_buttons, + supports_markdown, + update_or_post_message, +) from app.helper.server import MoviePilotServerHelper from app.helper.torrent import TorrentHelper from app.log import logger @@ -42,7 +42,6 @@ from app.schemas import (MediaRecognizeConvertEventData, SubscribeEpisodesRefres from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType, EventType, ChainEventType, \ ContentType - subscribe_interaction_manager = SlashInteractionManager() @@ -365,9 +364,9 @@ class SubscribeChain(ChainBase): 判断当前订阅是否启用了电视剧全集洗版。 """ return ( - bool(subscribe.best_version_full) - and bool(subscribe.best_version) - and subscribe.type == MediaType.TV.value + bool(subscribe.best_version_full) + and bool(subscribe.best_version) + and subscribe.type == MediaType.TV.value ) @classmethod @@ -435,9 +434,9 @@ class SubscribeChain(ChainBase): 构造分集洗版优先全集时使用的整季缺失范围。 """ if ( - not subscribe.best_version - or cls.__is_full_best_version_enabled(subscribe) - or subscribe.type != MediaType.TV.value + not subscribe.best_version + or cls.__is_full_best_version_enabled(subscribe) + or subscribe.type != MediaType.TV.value ): return None @@ -474,7 +473,7 @@ class SubscribeChain(ChainBase): full_season_contexts = [ context for context in contexts if context.media_info.type == MediaType.TV - and self.__is_full_season_resource(meta=context.meta_info, subscribe=subscribe) + and self.__is_full_season_resource(meta=context.meta_info, subscribe=subscribe) ] if full_pack_no_exists else [] full_pack_contexts = [ context for context in full_season_contexts @@ -1081,10 +1080,10 @@ class SubscribeChain(ChainBase): # 洗版 if subscribe.best_version: if ( - torrent_mediainfo.type == MediaType.TV - and not self.__is_full_season_best_version_resource( - meta=torrent_meta, subscribe=subscribe - ) + torrent_mediainfo.type == MediaType.TV + and not self.__is_full_season_best_version_resource( + meta=torrent_meta, subscribe=subscribe + ) ): logger.info( f"{subscribe.name} 正在全集洗版,{torrent_info.title} 不是全集资源" @@ -1092,10 +1091,10 @@ class SubscribeChain(ChainBase): continue # 洗版时,不符合订阅集数的不要 if ( - torrent_mediainfo.type == MediaType.TV - and not self._is_episode_range_covered( - meta=torrent_meta, subscribe=subscribe - ) + torrent_mediainfo.type == MediaType.TV + and not self._is_episode_range_covered( + meta=torrent_meta, subscribe=subscribe + ) ): logger.info( f"{subscribe.name} 正在洗版,{torrent_info.title} 不符合订阅集数范围" @@ -1116,9 +1115,9 @@ class SubscribeChain(ChainBase): # 防止标题元数据与实际种子文件错位导致同优先级集被重复下载。 context.allowed_episodes = set(interested_episodes) if ( - torrent_mediainfo.type != MediaType.TV - and subscribe.current_priority - and torrent_info.pri_order <= subscribe.current_priority + torrent_mediainfo.type != MediaType.TV + and subscribe.current_priority + and torrent_info.pri_order <= subscribe.current_priority ): logger.info( f'{subscribe.name} 正在洗版,{torrent_info.title} 优先级低于或等于已下载优先级') @@ -1585,8 +1584,8 @@ class SubscribeChain(ChainBase): continue else: if not self.__is_full_season_best_version_resource( - meta=torrent_meta, - subscribe=subscribe, + meta=torrent_meta, + subscribe=subscribe, ): logger.debug( f"{subscribe.name} 正在全集洗版,{torrent_info.title} 不是全集资源" @@ -1594,11 +1593,11 @@ class SubscribeChain(ChainBase): continue # 洗版时,不符合订阅集数的不要 if ( - meta.type == MediaType.TV - and not self._is_episode_range_covered( - meta=torrent_meta, - subscribe=subscribe, - ) + meta.type == MediaType.TV + and not self._is_episode_range_covered( + meta=torrent_meta, + subscribe=subscribe, + ) ): logger.debug( f"{subscribe.name} 正在洗版,{torrent_info.title} 不符合订阅集数范围" @@ -1642,9 +1641,9 @@ class SubscribeChain(ChainBase): # 避免 RSS / 订阅刷新场景下标题元数据与种子文件错位导致同优先级集重复下载。 _context.allowed_episodes = set(interested_episodes) if ( - meta.type != MediaType.TV - and subscribe.current_priority - and torrent_info.pri_order <= subscribe.current_priority + meta.type != MediaType.TV + and subscribe.current_priority + and torrent_info.pri_order <= subscribe.current_priority ): logger.info( f'{subscribe.name} 正在洗版,{torrent_info.title} 优先级低于或等于已下载优先级') @@ -2087,11 +2086,11 @@ class SubscribeChain(ChainBase): }) def remote_list( - self, - arg_str: str = "", - channel: MessageChannel = None, - userid: Union[str, int] = None, - source: Optional[str] = None, + self, + arg_str: str = "", + channel: MessageChannel = None, + userid: Union[str, int] = None, + source: Optional[str] = None, ): """ /subscribes 统一入口。 @@ -2105,11 +2104,11 @@ class SubscribeChain(ChainBase): ) normalized_arg = (arg_str or "").strip() if normalized_arg and self.handle_text_interaction( - channel=channel, - source=source, - userid=userid, - username="", - text=normalized_arg, + channel=channel, + source=source, + userid=userid, + username="", + text=normalized_arg, ): return self._render_subscribe_interaction( @@ -2133,14 +2132,14 @@ class SubscribeChain(ChainBase): return parts[1], parts[2] def handle_callback_interaction( - self, - callback_data: str, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, - original_message_id: Optional[Union[str, int]] = None, - original_chat_id: Optional[str] = None, + self, + callback_data: str, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, + original_message_id: Optional[Union[str, int]] = None, + original_chat_id: Optional[str] = None, ) -> bool: """ 处理 /subscribes 按钮交互。 @@ -2211,12 +2210,12 @@ class SubscribeChain(ChainBase): return True def handle_text_interaction( - self, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, - text: str, + self, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, + text: str, ) -> bool: """ 处理 /subscribes 文本补充输入。 @@ -2416,14 +2415,14 @@ class SubscribeChain(ChainBase): return True def _render_subscribe_interaction( - self, - request, - channel: MessageChannel, - source: Optional[str], - userid: Union[str, int], - username: Optional[str], - original_message_id: Optional[Union[str, int]] = None, - original_chat_id: Optional[str] = None, + self, + request, + channel: MessageChannel, + source: Optional[str], + userid: Union[str, int], + username: Optional[str], + original_message_id: Optional[Union[str, int]] = None, + original_chat_id: Optional[str] = None, ) -> None: """ 渲染 /subscribes 当前页面。 @@ -2502,7 +2501,7 @@ class SubscribeChain(ChainBase): ) def _format_subscribe_list( - self, subscribes: List[Subscribe], channel: Optional[MessageChannel] + self, subscribes: List[Subscribe], channel: Optional[MessageChannel] ) -> str: """ 根据渠道能力格式化订阅列表。 @@ -2590,11 +2589,11 @@ class SubscribeChain(ChainBase): ) def _run_refresh_action( - self, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, + self, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, ) -> None: """ 执行订阅刷新。 @@ -2620,11 +2619,11 @@ class SubscribeChain(ChainBase): ) def _run_metadata_refresh_action( - self, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, + self, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, ) -> None: """ 执行订阅元数据刷新。 @@ -2657,12 +2656,12 @@ class SubscribeChain(ChainBase): return [int(item) for item in re.findall(r"\d+", arg_str or "")] def _run_search_action( - self, - arg_str: str, - channel: MessageChannel, - source: str, - userid: Union[str, int], - username: str, + self, + arg_str: str, + channel: MessageChannel, + source: str, + userid: Union[str, int], + username: str, ) -> Tuple[bool, str]: """ 手动执行订阅搜索。 @@ -2756,9 +2755,13 @@ class SubscribeChain(ChainBase): 删除订阅 """ if not arg_str: - self.post_message(schemas.Notification(channel=channel, source=source, - title="请输入正确的命令格式:/subscribe_delete [id]," - "[id]为订阅编号", userid=userid)) + self.post_message(schemas.Notification( + channel=channel, + source=source, + title="请输入正确的命令格式:/subscribe_delete [id]," + "[id]为订阅编号", + userid=userid, + save_history=False)) return arg_strs = str(arg_str).split() subscribeoper = SubscribeOper() @@ -2769,8 +2772,11 @@ class SubscribeChain(ChainBase): subscribe_id = int(arg_str) subscribe = subscribeoper.get(subscribe_id) if not subscribe: - self.post_message(schemas.Notification(channel=channel, source=source, - title=f"订阅编号 {subscribe_id} 不存在!", userid=userid)) + self.post_message(schemas.Notification( + channel=channel, source=source, + title=f"订阅编号 {subscribe_id} 不存在!", + userid=userid, + save_history=False)) return # 删除订阅 subscribeoper.delete(subscribe_id) diff --git a/app/chain/system.py b/app/chain/system.py index b73f4cdc..29f84c4d 100644 --- a/app/chain/system.py +++ b/app/chain/system.py @@ -27,8 +27,12 @@ class SystemChain(ChainBase): 清理系统缓存 """ self.clear_cache() - self.post_message(Notification(channel=channel, source=source, - title=f"缓存清理完成!", userid=userid)) + self.post_message(Notification( + channel=channel, + source=source, + title=f"缓存清理完成!", + userid=userid, + save_history=False)) def restart(self, channel: MessageChannel, userid: Union[int, str], source: Optional[str] = None): """ @@ -37,8 +41,12 @@ class SystemChain(ChainBase): from app.core.config import global_vars if channel and userid: - self.post_message(Notification(channel=channel, source=source, - title="系统正在重启,请耐心等候!", userid=userid)) + self.post_message(Notification( + channel=channel, + source=source, + title="系统正在重启,请耐心等候!", + userid=userid, + save_history=False)) # 保存重启信息 self.save_cache({ "channel": channel.value, @@ -180,9 +188,12 @@ class SystemChain(ChainBase): """ 查看当前版本、远程版本 """ - self.post_message(Notification(channel=channel, source=source, - title=self.__get_version_message(), - userid=userid)) + self.post_message(Notification( + channel=channel, + source=source, + title=self.__get_version_message(), + userid=userid, + save_history=False)) def restart_finish(self): """ @@ -202,9 +213,11 @@ class SystemChain(ChainBase): # 版本号 title = self.__get_version_message() - self.post_message(Notification(channel=channel, - title=f"系统已重启完成!\n{title}", - userid=userid)) + self.post_message(Notification( + channel=channel, + title=f"系统已重启完成!\n{title}", + userid=userid, + save_history=False)) self.remove_cache(self._restart_file) @staticmethod diff --git a/app/chain/torrents.py b/app/chain/torrents.py index 756ab1f2..b5755d27 100644 --- a/app/chain/torrents.py +++ b/app/chain/torrents.py @@ -2,6 +2,8 @@ import re import traceback from typing import Dict, List, Union, Optional +from app.helper.sites import SitesHelper # noqa + from app.chain import ChainBase from app.chain.media import MediaChain from app.core.config import settings, global_vars @@ -10,7 +12,6 @@ from app.core.metainfo import MetaInfo from app.db.site_oper import SiteOper from app.db.systemconfig_oper import SystemConfigOper from app.helper.rss import RssHelper -from app.helper.sites import SitesHelper # noqa from app.helper.torrent import TorrentHelper from app.log import logger from app.schemas import Notification @@ -39,11 +40,17 @@ class TorrentsChain(ChainBase): """ 远程刷新订阅,发送消息 """ - self.post_message(Notification(channel=channel, - title=f"开始刷新种子 ...", userid=userid)) + self.post_message(Notification( + channel=channel, + title=f"开始刷新种子 ...", + userid=userid, + save_history=False)) self.refresh() - self.post_message(Notification(channel=channel, - title=f"种子刷新完成!", userid=userid)) + self.post_message(Notification( + channel=channel, + title=f"种子刷新完成!", + userid=userid, + save_history=False)) def get_torrents(self, stype: Optional[str] = None) -> Dict[str, List[Context]]: """ @@ -150,7 +157,8 @@ class TorrentsChain(ChainBase): return [] # 解析RSS rss_items = RssHelper().parse(site.get("rss"), True if site.get("proxy") else False, - timeout=int(site.get("timeout") or 30), ua=site.get("ua") if site.get("ua") else None) + timeout=int(site.get("timeout") or 30), + ua=site.get("ua") if site.get("ua") else None) if rss_items is None: # rss过期,尝试保留原配置生成新的rss self.__renew_rss_url(domain=domain, site=site) diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 0a619520..d33ad0c6 100755 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -2945,6 +2945,7 @@ class TransferChain(ChainBase, ConfigReloadMixin, metaclass=Singleton): title="请输入正确的命令格式:/redo [id] 或 /redo [id] [tmdbid/豆瓣id]|[类型]," "[id] 为整理记录编号", userid=userid, + save_history=False, ) ) @@ -2971,6 +2972,7 @@ class TransferChain(ChainBase, ConfigReloadMixin, metaclass=Singleton): text=errmsg, userid=userid, link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) return @@ -2997,6 +2999,7 @@ class TransferChain(ChainBase, ConfigReloadMixin, metaclass=Singleton): text=errmsg, userid=userid, link=settings.MP_DOMAIN("#/history"), + save_history=False, ) ) return diff --git a/app/helper/interaction.py b/app/helper/interaction.py index d78e2fdf..5c75a873 100644 --- a/app/helper/interaction.py +++ b/app/helper/interaction.py @@ -218,6 +218,7 @@ def update_or_post_message( title=title, text=text, buttons=buttons, + save_history=False, ) ) diff --git a/app/schemas/message.py b/app/schemas/message.py index cfe1d348..400a6b51 100644 --- a/app/schemas/message.py +++ b/app/schemas/message.py @@ -223,6 +223,8 @@ class Notification(BaseModel): original_chat_id: Optional[str] = None # 是否禁用链接预览(仅Telegram支持) disable_web_page_preview: Optional[bool] = None + # 是否写入消息历史 + save_history: bool = True def to_dict(self): """ diff --git a/tests/test_media_interaction.py b/tests/test_media_interaction.py index 4771ded1..88c983de 100644 --- a/tests/test_media_interaction.py +++ b/tests/test_media_interaction.py @@ -224,6 +224,7 @@ def test_media_interaction_starts_search_and_posts_media_list(): assert handled post_medias_message.assert_called_once() notification = post_medias_message.call_args.args[0] + assert notification.save_history is False assert notification.buttons assert notification.buttons[0][0]["callback_data"].startswith("media:") @@ -306,6 +307,7 @@ def test_torrent_selection_prompts_download_dir_buttons_before_download(): assert request.phase == "download-dir" post_message.assert_called_once() notification = post_message.call_args.args[0] + assert notification.save_history is False assert "请选择下载目录" in notification.title assert "电影下载 (/downloads/movies)" in notification.text assert notification.buttons[0][0]["callback_data"] == f"media:{request.request_id}:download-dir:1" @@ -342,6 +344,7 @@ def test_torrent_selection_prompts_text_download_dir_for_plain_channel(): assert handled notification = post_message.call_args.args[0] + assert notification.save_history is False assert "请回复对应数字" in notification.title assert notification.buttons is None assert "2. 动画下载 (rclone:/media/anime)" in notification.text diff --git a/tests/test_message_notifications.py b/tests/test_message_notifications.py index 01d596ea..f7c915c8 100644 --- a/tests/test_message_notifications.py +++ b/tests/test_message_notifications.py @@ -5,9 +5,11 @@ from app.db import SessionFactory from app.db.message_oper import MessageOper from app.db.models.message import Message from app.chain import ChainBase +from app.core.context import Context, MediaInfo, TorrentInfo +from app.core.meta import MetaBase from app.helper.message import MessageHelper from app.schemas import Notification -from app.schemas.types import NotificationType +from app.schemas.types import MediaType, NotificationType def _clear_messages() -> None: @@ -167,3 +169,59 @@ def test_agent_notification_post_message_is_persisted_without_sse_queue() -> Non assert messages[0].mtype == NotificationType.Agent.value assert helper.get() is None chain.messagequeue.send_message.assert_called_once() + + +def test_transient_notification_post_message_skips_history_but_dispatches() -> None: + """ + 标记为不保存历史的过程消息应跳过数据库登记,但仍正常派发。 + """ + _clear_messages() + chain = ChainBase() + + chain.messagequeue.send_message = Mock() + chain.eventmanager.send_event = Mock() + + chain.post_message( + Notification( + title="请选择下载目录", + text="1. 默认目录", + save_history=False, + ) + ) + + assert MessageOper().list_by_page(page=1, count=10) == [] + assert "save_history" not in chain.eventmanager.send_event.call_args.kwargs["data"] + chain.eventmanager.send_event.assert_called_once() + chain.messagequeue.send_message.assert_called_once() + + +def test_transient_media_and_torrent_lists_skip_history_but_dispatch() -> None: + """ + 传统交互候选列表标记为不保存历史时,只发送到渠道,不写入消息表。 + """ + _clear_messages() + chain = ChainBase() + media = MediaInfo(type=MediaType.MOVIE, title="星际穿越", year="2014") + torrent = Context( + meta_info=MetaBase("星际穿越"), + media_info=media, + torrent_info=TorrentInfo( + title="星际穿越.2014.1080p", + site_name="TestSite", + enclosure="https://example.com/demo.torrent", + ), + ) + + chain.messagequeue.send_message = Mock() + + chain.post_medias_message( + Notification(title="请选择媒体", save_history=False), + medias=[media], + ) + chain.post_torrents_message( + Notification(title="请选择资源", save_history=False), + torrents=[torrent], + ) + + assert MessageOper().list_by_page(page=1, count=10) == [] + assert chain.messagequeue.send_message.call_count == 2