feat: conditionally save message history based on save_history flag

This commit is contained in:
jxxghp
2026-06-17 21:00:40 +08:00
parent 4c20639abb
commit e1ba9a2c97
12 changed files with 324 additions and 171 deletions

View File

@@ -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",

View File

@@ -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]:

View File

@@ -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,
)
)

View File

@@ -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,
))

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -218,6 +218,7 @@ def update_or_post_message(
title=title,
text=text,
buttons=buttons,
save_history=False,
)
)

View File

@@ -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):
"""

View File

@@ -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

View File

@@ -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