diff --git a/README.md b/README.md index a6bbe517..07781f8c 100644 --- a/README.md +++ b/README.md @@ -222,5 +222,5 @@ docker pull jxxghp/moviepilot:latest - [x] 搜索结果过滤 - [ ] 媒体详情页面 - [ ] 洗版支持 -- [ ] 多通知渠道支持 +- [x] 多通知渠道支持 diff --git a/app/api/endpoints/download.py b/app/api/endpoints/download.py index 3d4263de..f90bf343 100644 --- a/app/api/endpoints/download.py +++ b/app/api/endpoints/download.py @@ -55,7 +55,7 @@ def add_downloading( @router.post("/notexists", summary="查询缺失媒体信息", response_model=List[NotExistMediaInfo]) def exists(media_in: schemas.MediaInfo, - _: schemas.TokenPayload = Depends(verify_token)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询缺失媒体信息 """ diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index d3662761..ad0cf3fb 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -1,7 +1,7 @@ import json import json import time -from typing import Any, List, Union +from typing import Union from fastapi import APIRouter, HTTPException, Depends from fastapi.responses import StreamingResponse diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 1b0c56d2..82e5fb70 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -10,8 +10,8 @@ from app.core.event import EventManager from app.core.meta import MetaBase from app.core.module import ModuleManager from app.log import logger -from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage -from app.schemas.types import TorrentStatus, MediaType, MediaImageType, MessageChannel +from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification +from app.schemas.types import TorrentStatus, MediaType, MediaImageType from app.utils.object import ObjectUtils from app.utils.singleton import AbstractSingleton, Singleton @@ -287,42 +287,31 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): """ return self.run_module("refresh_mediaserver", mediainfo=mediainfo, file_path=file_path) - def post_message(self, title: str, text: str = None, - image: str = None, userid: Union[str, int] = None) -> Optional[bool]: + def post_message(self, message: Notification) -> Optional[bool]: """ 发送消息 - :param title: 标题 - :param text: 内容 - :param image: 图片 - :param userid: 用户ID + :param message: 消息体 :return: 成功或失败 """ - return self.run_module("post_message", title=title, text=text, image=image, userid=userid) + return self.run_module("post_message", message=message) - def post_medias_message(self, title: str, items: List[MediaInfo], - userid: Union[str, int] = None) -> Optional[bool]: + def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: """ 发送媒体信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param userid: 用户ID + :param message: 消息体 + :param medias: 媒体列表 :return: 成功或失败 """ - return self.run_module("post_medias_message", title=title, items=items, userid=userid) + return self.run_module("post_medias_message", message=message, medias=medias) - def post_torrents_message(self, title: str, items: List[Context], - mediainfo: MediaInfo, - userid: Union[str, int] = None) -> Optional[bool]: + def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: """ 发送种子信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param mediainfo: 媒体信息 - :param userid: 用户ID + :param message: 消息体 + :param torrents: 种子列表 :return: 成功或失败 """ - return self.run_module("post_torrents_message", title=title, mediainfo=mediainfo, - items=items, userid=userid) + return self.run_module("post_torrents_message", message=message, torrents=torrents) def scrape_metadata(self, path: Path, mediainfo: MediaInfo) -> None: """ diff --git a/app/chain/cookiecloud.py b/app/chain/cookiecloud.py index 9a4b9036..53a682a3 100644 --- a/app/chain/cookiecloud.py +++ b/app/chain/cookiecloud.py @@ -13,6 +13,7 @@ from app.helper.cookiecloud import CookieCloudHelper from app.helper.message import MessageHelper from app.helper.sites import SitesHelper from app.log import logger +from app.schemas import Notification, NotificationType, MessageChannel from app.utils.http import RequestUtils @@ -34,17 +35,20 @@ class CookieCloudChain(ChainBase): password=settings.COOKIECLOUD_PASSWORD ) - def remote_sync(self, userid: Union[int, str]): + def remote_sync(self, channel: MessageChannel, userid: Union[int, str]): """ 远程触发同步站点,发送消息 """ - self.post_message(title="开始同步CookieCloud站点 ...", userid=userid) + self.post_message(Notification(channel=channel, mtype=NotificationType.SiteMessage, + title="开始同步CookieCloud站点 ...", userid=userid)) # 开始同步 success, msg = self.process() if success: - self.post_message(title=f"同步站点成功,{msg}", userid=userid) + self.post_message(Notification(channel=channel, mtype=NotificationType.SiteMessage, + title=f"同步站点成功,{msg}", userid=userid)) else: - self.post_message(title=f"同步站点失败:{msg}", userid=userid) + self.post_message(Notification(channel=channel, mtype=NotificationType.SiteMessage, + title=f"同步站点失败:{msg}", userid=userid)) def process(self, manual=False) -> Tuple[bool, str]: """ diff --git a/app/chain/douban.py b/app/chain/douban.py index b3b7370d..de1a9517 100644 --- a/app/chain/douban.py +++ b/app/chain/douban.py @@ -12,7 +12,7 @@ from app.core.context import MediaInfo from app.core.metainfo import MetaInfo from app.helper.rss import RssHelper from app.log import logger -from app.schemas import MediaType +from app.schemas import MediaType, Notification, MessageChannel, NotificationType class DoubanChain(ChainBase): @@ -91,13 +91,15 @@ class DoubanChain(ChainBase): return self.run_module("douban_discover", mtype=mtype, sort=sort, tags=tags, page=page, count=count) - def remote_sync(self, userid: Union[int, str]): + def remote_sync(self, channel: MessageChannel, userid: Union[int, str]): """ 同步豆瓣想看数据,发送消息 """ - self.post_message(title="开始同步豆瓣想看 ...", userid=userid) + self.post_message(Notification(channel=channel, mtype=NotificationType.Subscribe, + title="开始同步豆瓣想看 ...", userid=userid)) self.sync() - self.post_message(title="同步豆瓣想看数据完成!", userid=userid) + self.post_message(Notification(channel=channel, mtype=NotificationType.Subscribe, + title="同步豆瓣想看数据完成!", userid=userid)) def sync(self): """ diff --git a/app/chain/download.py b/app/chain/download.py index b127527c..645ceae5 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -8,8 +8,8 @@ from app.core.meta import MetaBase from app.db.downloadhistory_oper import DownloadHistoryOper from app.helper.torrent import TorrentHelper from app.log import logger -from app.schemas import ExistMediaInfo, NotExistMediaInfo, DownloadingTorrent -from app.schemas.types import MediaType, TorrentStatus, EventType +from app.schemas import ExistMediaInfo, NotExistMediaInfo, DownloadingTorrent, Notification +from app.schemas.types import MediaType, TorrentStatus, EventType, MessageChannel, NotificationType from app.utils.string import StringUtils @@ -23,7 +23,9 @@ class DownloadChain(ChainBase): self.torrent = TorrentHelper() self.downloadhis = DownloadHistoryOper() - def post_download_message(self, meta: MetaBase, mediainfo: MediaInfo, torrent: TorrentInfo, userid: str = None): + def post_download_message(self, meta: MetaBase, mediainfo: MediaInfo, torrent: TorrentInfo, + channel: MessageChannel = None, + userid: str = None): """ 发送添加下载的消息 """ @@ -51,13 +53,17 @@ class DownloadChain(ChainBase): torrent.description = re.sub(r'<[^>]+>', '', description) msg_text = f"{msg_text}\n描述:{torrent.description}" - self.post_message(title=f"{mediainfo.title_year}" - f"{meta.season_episode} 开始下载", - text=msg_text, - image=mediainfo.get_message_image(), - userid=userid) + self.post_message(Notification( + channel=channel, + mtype=NotificationType.Download, + title=f"{mediainfo.title_year}" + f"{meta.season_episode} 开始下载", + text=msg_text, + image=mediainfo.get_message_image(), + userid=userid)) def download_torrent(self, torrent: TorrentInfo, + channel: MessageChannel = None, userid: Union[str, int] = None) -> Tuple[Optional[Path], str, list]: """ 下载种子文件 @@ -70,14 +76,19 @@ class DownloadChain(ChainBase): proxy=torrent.site_proxy) if not torrent_file: logger.error(f"下载种子文件失败:{torrent.title} - {torrent.enclosure}") - self.post_message(title=f"{torrent.title} 种子下载失败!", - text=f"错误信息:{error_msg}\n种子链接:{torrent.enclosure}", - userid=userid) + self.post_message(Notification( + channel=channel, + mtype=NotificationType.Download, + title=f"{torrent.title} 种子下载失败!", + text=f"错误信息:{error_msg}\n种子链接:{torrent.enclosure}", + userid=userid)) return None, "", [] return torrent_file, download_folder, files def download_single(self, context: Context, torrent_file: Path = None, - episodes: Set[int] = None, userid: Union[str, int] = None) -> Optional[str]: + episodes: Set[int] = None, + channel: MessageChannel = None, + userid: Union[str, int] = None) -> Optional[str]: """ 下载及发送通知 """ @@ -119,7 +130,8 @@ class DownloadChain(ChainBase): torrent_site=_torrent.site_name ) # 发送消息 - self.post_download_message(meta=_meta, mediainfo=_media, torrent=_torrent, userid=userid) + self.post_download_message(meta=_meta, mediainfo=_media, torrent=_torrent, + channel=channel, userid=userid) # 下载成功后处理 self.download_added(context=context, torrent_path=torrent_file) # 广播事件 @@ -132,7 +144,9 @@ class DownloadChain(ChainBase): # 下载失败 logger.error(f"{_media.title_year} 添加下载任务失败:" f"{_torrent.title} - {_torrent.enclosure},{error_msg}") - self.post_message( + self.post_message(Notification( + channel=channel, + mtype=NotificationType.Download, title="添加下载任务失败:%s %s" % (_media.title_year, _meta.season_episode), text=f"站点:{_torrent.site_name}\n" @@ -140,7 +154,7 @@ class DownloadChain(ChainBase): f"种子链接:{_torrent.enclosure}\n" f"错误信息:{error_msg}", image=_media.get_message_image(), - userid=userid) + userid=userid)) return _hash def batch_download(self, @@ -324,12 +338,12 @@ class DownloadChain(ChainBase): if len(torrent_season) != 1 or torrent_season[0] != need_season: continue # 种子集列表 - torrent_episodes = meta.episode_list + torrent_episodes = set(meta.episode_list) # 整季的不处理 if not torrent_episodes: continue # 为需要集的子集则下载 - if set(torrent_episodes).issubset(set(need_episodes)): + if torrent_episodes.issubset(set(need_episodes)): # 下载 download_id = self.download_single(context, userid=userid) if download_id: @@ -517,13 +531,16 @@ class DownloadChain(ChainBase): # 全部存在 return True, no_exists - def remote_downloading(self, userid: Union[str, int] = None): + def remote_downloading(self, channel: MessageChannel, userid: Union[str, int] = None): """ 查询正在下载的任务,并发送消息 """ torrents = self.list_torrents(status=TorrentStatus.DOWNLOADING) if not torrents: - self.post_message(title="没有正在下载的任务!") + self.post_message(Notification( + channel=channel, + mtype=NotificationType.Download, + title="没有正在下载的任务!")) return # 发送消息 title = f"共 {len(torrents)} 个任务正在下载:" @@ -534,7 +551,9 @@ class DownloadChain(ChainBase): f"{StringUtils.str_filesize(torrent.size)} " f"{round(torrent.progress * 100, 1)}%") index += 1 - self.post_message(title=title, text="\n".join(messages), userid=userid) + self.post_message(Notification( + channel=channel, mtype=NotificationType.Download, + title=title, text="\n".join(messages), userid=userid)) def downloading(self) -> List[DownloadingTorrent]: """ diff --git a/app/chain/message.py b/app/chain/message.py index 803a2a4f..88994c3a 100644 --- a/app/chain/message.py +++ b/app/chain/message.py @@ -7,7 +7,8 @@ from app.chain.subscribe import SubscribeChain from app.core.context import MediaInfo from app.core.event import EventManager from app.log import logger -from app.schemas.types import EventType +from app.schemas import Notification +from app.schemas.types import EventType, MessageChannel class MessageChain(ChainBase): @@ -76,7 +77,7 @@ class MessageChain(ChainBase): or not cache_data.get('items') \ or len(cache_data.get('items')) < int(text): # 发送消息 - self.post_message(title="输入有误!", userid=userid) + self.post_message(Notification(channel=channel, title="输入有误!", userid=userid)) return # 缓存类型 cache_type: str = cache_data.get('type') @@ -90,9 +91,11 @@ class MessageChain(ChainBase): exist_flag, no_exists = self.downloadchain.get_no_exists_info(meta=self._current_meta, mediainfo=self._current_media) if exist_flag: - self.post_message(title=f"{self._current_media.title_year}" - f"{self._current_meta.sea} 媒体库中已存在", - userid=userid) + self.post_message( + Notification(channel=channel, + title=f"{self._current_media.title_year}" + f"{self._current_meta.sea} 媒体库中已存在", + userid=userid)) return # 发送缺失的媒体信息 if no_exists: @@ -100,19 +103,23 @@ class MessageChain(ChainBase): messages = [ f"第 {sea} 季缺失 {StringUtils.str_series(no_exist.episodes) if no_exist.episodes else no_exist.total_episodes} 集" for sea, no_exist in no_exists.get(mediainfo.tmdb_id).items()] - self.post_message(title=f"{mediainfo.title_year}:\n" + "\n".join(messages)) + self.post_message(Notification(channel=channel, + title=f"{mediainfo.title_year}:\n" + "\n".join(messages))) # 搜索种子,过滤掉不需要的剧集,以便选择 logger.info(f"{mediainfo.title_year} 媒体库中不存在,开始搜索 ...") self.post_message( - title=f"开始搜索 {mediainfo.type.value} {mediainfo.title_year} ...", userid=userid) + Notification(channel=channel, + title=f"开始搜索 {mediainfo.type.value} {mediainfo.title_year} ...", + userid=userid)) # 开始搜索 contexts = self.searchchain.process(mediainfo=mediainfo, no_exists=no_exists) if not contexts: # 没有数据 - self.post_message(title=f"{mediainfo.title}" - f"{self._current_meta.sea} 未搜索到需要的资源!", - userid=userid) + self.post_message(Notification( + channel=channel, title=f"{mediainfo.title}" + f"{self._current_meta.sea} 未搜索到需要的资源!", + userid=userid)) return # 搜索结果排序 contexts = self.torrenthelper.sort_torrents(contexts) @@ -124,9 +131,9 @@ class MessageChain(ChainBase): self._current_page = 0 # 发送种子数据 logger.info(f"搜索到 {len(contexts)} 条数据,开始发送选择消息 ...") - self.__post_torrents_message(title=mediainfo.title, + self.__post_torrents_message(channel=channel, + title=mediainfo.title, items=contexts[:self._page_size], - mediainfo=mediainfo, userid=userid, total=len(contexts)) @@ -137,15 +144,18 @@ class MessageChain(ChainBase): exist_flag, _ = self.downloadchain.get_no_exists_info(meta=self._current_meta, mediainfo=mediainfo) if exist_flag: - self.post_message(title=f"{mediainfo.title_year}" - f"{self._current_meta.sea} 媒体库中已存在", - userid=userid) + self.post_message(Notification( + channel=channel, + title=f"{mediainfo.title_year}" + f"{self._current_meta.sea} 媒体库中已存在", + userid=userid)) return self.subscribechain.add(title=mediainfo.title, year=mediainfo.year, mtype=mediainfo.type, tmdbid=mediainfo.tmdb_id, season=self._current_meta.begin_season, + channel=channel, userid=userid, username=username) elif cache_type == "Torrent": @@ -155,9 +165,11 @@ class MessageChain(ChainBase): exist_flag, no_exists = self.downloadchain.get_no_exists_info(meta=self._current_meta, mediainfo=self._current_media) if exist_flag: - self.post_message(title=f"{self._current_media.title_year}" - f"{self._current_meta.sea} 媒体库中已存在", - userid=userid) + self.post_message(Notification( + channel=channel, + title=f"{self._current_media.title_year}" + f"{self._current_meta.sea} 媒体库中已存在", + userid=userid)) return # 批量下载 downloads, lefts = self.downloadchain.batch_download(contexts=cache_list, @@ -175,6 +187,7 @@ class MessageChain(ChainBase): mtype=self._current_media.type, tmdbid=self._current_media.tmdb_id, season=self._current_meta.begin_season, + channel=channel, userid=userid, username=username) else: @@ -188,12 +201,14 @@ class MessageChain(ChainBase): cache_data: dict = self._user_cache.get(userid) if not cache_data: # 没有缓存 - self.post_message(title="输入有误!", userid=userid) + self.post_message(Notification( + channel=channel, title="输入有误!", userid=userid)) return if self._current_page == 0: # 第一页 - self.post_message(title="已经是第一页了!", userid=userid) + self.post_message(Notification( + channel=channel, title="已经是第一页了!", userid=userid)) return cache_type: str = cache_data.get('type') cache_list: list = cache_data.get('items') @@ -207,14 +222,15 @@ class MessageChain(ChainBase): end = start + self._page_size if cache_type == "Torrent": # 发送种子数据 - self.__post_torrents_message(title=self._current_media.title, + self.__post_torrents_message(channel=channel, + title=self._current_media.title, items=cache_list[start:end], - mediainfo=self._current_media, userid=userid, total=len(cache_list)) else: # 发送媒体数据 - self.__post_medias_message(title=self._current_media.title, + self.__post_medias_message(channel=channel, + title=self._current_media.title, items=cache_list[start:end], userid=userid, total=len(cache_list)) @@ -224,7 +240,8 @@ class MessageChain(ChainBase): cache_data: dict = self._user_cache.get(userid) if not cache_data: # 没有缓存 - self.post_message(title="输入有误!", userid=userid) + self.post_message(Notification( + channel=channel, title="输入有误!", userid=userid)) return cache_type: str = cache_data.get('type') cache_list: list = cache_data.get('items') @@ -234,17 +251,19 @@ class MessageChain(ChainBase): cache_list = cache_list[self._current_page * self._page_size:(self._current_page + 1) * self._page_size] if not cache_list: # 没有数据 - self.post_message(title="已经是最后一页了!", userid=userid) + self.post_message(Notification( + channel=channel, title="已经是最后一页了!", userid=userid)) return else: if cache_type == "Torrent": # 发送种子数据 - self.__post_torrents_message(title=self._current_media.title, - mediainfo=self._current_media, + self.__post_torrents_message(channel=channel, + title=self._current_media.title, items=cache_list, userid=userid, total=total) else: # 发送媒体数据 - self.__post_medias_message(title=self._current_media.title, + self.__post_medias_message(channel=channel, + title=self._current_media.title, items=cache_list, userid=userid, total=total) else: @@ -261,11 +280,13 @@ class MessageChain(ChainBase): meta, medias = self.medtachain.search(content) # 识别 if not meta.name: - self.post_message(title="无法识别输入内容!", userid=userid) + self.post_message(Notification( + channel=channel, title="无法识别输入内容!", userid=userid)) return # 开始搜索 if not medias: - self.post_message(title=f"{meta.name} 没有找到对应的媒体信息!", userid=userid) + self.post_message(Notification( + channel=channel, title=f"{meta.name} 没有找到对应的媒体信息!", userid=userid)) return logger.info(f"搜索到 {len(medias)} 条相关媒体信息") # 记录当前状态 @@ -277,28 +298,30 @@ class MessageChain(ChainBase): self._current_page = 0 self._current_media = None # 发送媒体列表 - self.__post_medias_message(title=meta.name, + self.__post_medias_message(channel=channel, + title=meta.name, items=medias[:self._page_size], userid=userid, total=len(medias)) - def __post_medias_message(self, title: str, items: list, userid: str, total: int): + def __post_medias_message(self, channel: MessageChannel, + title: str, items: list, userid: str, total: int): """ 发送媒体列表消息 """ - self.post_medias_message( + self.post_medias_message(Notification( + channel=channel, title=f"【{title}】共找到{total}条相关信息,请回复对应数字选择(p: 上一页 n: 下一页)", - items=items, userid=userid - ) + ), medias=items) - def __post_torrents_message(self, title: str, items: list, - mediainfo: MediaInfo, userid: str, total: int): + def __post_torrents_message(self, channel: MessageChannel, title: str, items: list, + userid: str, total: int): """ 发送种子列表消息 """ - self.post_torrents_message( + self.post_torrents_message(Notification( + channel=channel, title=f"【{title}】共找到{total}条相关资源,请回复对应数字下载(0: 自动选择 p: 上一页 n: 下一页)", items=items, - mediainfo=mediainfo, userid=userid - ) + ), torrents=items) diff --git a/app/chain/search.py b/app/chain/search.py index ee83efd9..36aa2bb6 100644 --- a/app/chain/search.py +++ b/app/chain/search.py @@ -16,7 +16,6 @@ from app.helper.torrent import TorrentHelper from app.log import logger from app.schemas import NotExistMediaInfo from app.schemas.types import MediaType, ProgressKey, SystemConfigKey -from app.utils.object import ObjectUtils from app.utils.string import StringUtils diff --git a/app/chain/site.py b/app/chain/site.py index fb23dea5..284ff50f 100644 --- a/app/chain/site.py +++ b/app/chain/site.py @@ -9,6 +9,7 @@ from app.helper.cloudflare import under_challenge from app.helper.cookie import CookieHelper from app.helper.message import MessageHelper from app.log import logger +from app.schemas import MessageChannel, Notification from app.utils.http import RequestUtils from app.utils.site import SiteUtils from app.utils.string import StringUtils @@ -80,13 +81,15 @@ class SiteChain(ChainBase): return False, f"{str(e)}!" return True, "连接成功" - def remote_list(self, userid: Union[str, int] = None): + def remote_list(self, channel: MessageChannel, userid: Union[str, int] = None): """ 查询所有站点,发送消息 """ site_list = self.siteoper.list() if not site_list: - self.post_message(title="没有维护任何站点信息!") + self.post_message(Notification( + channel=channel, + title="没有维护任何站点信息!")) title = f"共有 {len(site_list)} 个站点,回复对应指令操作:" \ f"\n- 禁用站点:/site_disable [id]" \ f"\n- 启用站点:/site_enable [id]" \ @@ -102,9 +105,11 @@ class SiteChain(ChainBase): else: messages.append(f"{site.id}. {site.name}") # 发送列表 - self.post_message(title=title, text="\n".join(messages), userid=userid) + self.post_message(Notification( + channel=channel, + title=title, text="\n".join(messages), userid=userid)) - def remote_disable(self, arg_str, userid: Union[str, int] = None): + def remote_disable(self, arg_str, channel: MessageChannel, userid: Union[str, int] = None): """ 禁用站点 """ @@ -116,16 +121,18 @@ class SiteChain(ChainBase): site_id = int(arg_str) site = self.siteoper.get(site_id) if not site: - self.post_message(title=f"站点编号 {site_id} 不存在!", userid=userid) + self.post_message(Notification( + channel=channel, + title=f"站点编号 {site_id} 不存在!", userid=userid)) return # 禁用站点 self.siteoper.update(site_id, { "is_active": False }) # 重新发送消息 - self.remote_list() + self.remote_list(channel, userid) - def remote_enable(self, arg_str, userid: Union[str, int] = None): + def remote_enable(self, arg_str, channel: MessageChannel, userid: Union[str, int] = None): """ 启用站点 """ @@ -139,14 +146,16 @@ class SiteChain(ChainBase): site_id = int(arg_str) site = self.siteoper.get(site_id) if not site: - self.post_message(title=f"站点编号 {site_id} 不存在!", userid=userid) + self.post_message(Notification( + channel=channel, + title=f"站点编号 {site_id} 不存在!", userid=userid)) return # 禁用站点 self.siteoper.update(site_id, { "is_active": True }) # 重新发送消息 - self.remote_list() + self.remote_list(channel, userid) def update_cookie(self, site_info: Site, username: str, password: str) -> Tuple[bool, str]: @@ -175,32 +184,42 @@ class SiteChain(ChainBase): return True, msg return False, "未知错误" - def remote_cookie(self, arg_str: str, userid: Union[str, int] = None): + def remote_cookie(self, arg_str: str, channel: MessageChannel, userid: Union[str, int] = None): """ 使用用户名密码更新站点Cookie """ err_title = "请输入正确的命令格式:/site_cookie [id] [username] [password]," \ "[id]为站点编号,[uername]为站点用户名,[password]为站点密码" if not arg_str: - self.post_message(title=err_title, userid=userid) + self.post_message(Notification( + channel=channel, + title=err_title, userid=userid)) return arg_str = str(arg_str).strip() args = arg_str.split() if len(args) != 3: - self.post_message(title=err_title, userid=userid) + self.post_message(Notification( + channel=channel, + title=err_title, userid=userid)) return site_id = args[0] if not site_id.isdigit(): - self.post_message(title=err_title, userid=userid) + self.post_message(Notification( + channel=channel, + title=err_title, userid=userid)) return # 站点ID site_id = int(site_id) # 站点信息 site_info = self.siteoper.get(site_id) if not site_info: - self.post_message(title=f"站点编号 {site_id} 不存在!", userid=userid) + self.post_message(Notification( + channel=channel, + title=f"站点编号 {site_id} 不存在!", userid=userid)) return - self.post_message(title=f"开始更新【{site_info.name}】Cookie&UA ...", userid=userid) + self.post_message(Notification( + channel=channel, + title=f"开始更新【{site_info.name}】Cookie&UA ...", userid=userid)) # 用户名 username = args[1] # 密码 @@ -211,9 +230,13 @@ class SiteChain(ChainBase): password=password) if not status: logger.error(msg) - self.post_message(title=f"【{site_info.name}】 Cookie&UA更新失败!", - text=f"错误原因:{msg}", - userid=userid) + self.post_message(Notification( + channel=channel, + title=f"【{site_info.name}】 Cookie&UA更新失败!", + text=f"错误原因:{msg}", + userid=userid)) else: - self.post_message(title=f"【{site_info.name}】 Cookie&UA更新成功", - userid=userid) + self.post_message(Notification( + channel=channel, + title=f"【{site_info.name}】 Cookie&UA更新成功", + userid=userid)) diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index a63d7ad9..8e59e6d1 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -6,18 +6,17 @@ from typing import Dict, List, Optional, Union, Tuple from app.chain import ChainBase from app.chain.download import DownloadChain from app.chain.search import SearchChain -from app.core.metainfo import MetaInfo from app.core.context import TorrentInfo, Context, MediaInfo -from app.core.config import settings +from app.core.metainfo import MetaInfo from app.db.models.subscribe import Subscribe from app.db.subscribe_oper import SubscribeOper from app.db.systemconfig_oper import SystemConfigOper from app.helper.message import MessageHelper from app.helper.sites import SitesHelper from app.log import logger -from app.schemas import NotExistMediaInfo +from app.schemas import NotExistMediaInfo, Notification +from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType from app.utils.string import StringUtils -from app.schemas.types import MediaType, SystemConfigKey class SubscribeChain(ChainBase): @@ -42,6 +41,7 @@ class SubscribeChain(ChainBase): tmdbid: int = None, doubanid: str = None, season: int = None, + channel: MessageChannel = None, userid: str = None, username: str = None, message: bool = True, @@ -106,11 +106,13 @@ class SubscribeChain(ChainBase): logger.error(f'{mediainfo.title_year} {err_msg}') if not exist_ok and message: # 发回原用户 - self.post_message(title=f"{mediainfo.title_year}{metainfo.season} " - f"添加订阅失败!", - text=f"{err_msg}", - image=mediainfo.get_message_image(), - userid=userid) + self.post_message(Notification(channel=channel, + mtype=NotificationType.Subscribe, + title=f"{mediainfo.title_year}{metainfo.season} " + f"添加订阅失败!", + text=f"{err_msg}", + image=mediainfo.get_message_image(), + userid=userid)) elif message: logger.info(f'{mediainfo.title_year}{metainfo.season} 添加订阅成功') if username or userid: @@ -118,42 +120,52 @@ class SubscribeChain(ChainBase): else: text = f"评分:{mediainfo.vote_average}" # 广而告之 - self.post_message(title=f"{mediainfo.title_year}{metainfo.season} 已添加订阅", - text=text, - image=mediainfo.get_message_image()) + self.post_message(Notification(channel=channel, + mtype=NotificationType.Subscribe, + title=f"{mediainfo.title_year}{metainfo.season} 已添加订阅", + text=text, + image=mediainfo.get_message_image())) # 返回结果 return sid, "" - def remote_refresh(self, userid: Union[str, int] = None): + def remote_refresh(self, channel: MessageChannel, userid: Union[str, int] = None): """ 远程刷新订阅,发送消息 """ - self.post_message(title=f"开始刷新订阅 ...", userid=userid) + self.post_message(Notification(channel=channel, + title=f"开始刷新订阅 ...", userid=userid)) self.refresh() - self.post_message(title=f"订阅刷新完成!", userid=userid) + self.post_message(Notification(channel=channel, + title=f"订阅刷新完成!", userid=userid)) - def remote_search(self, arg_str: str, userid: Union[str, int] = None): + def remote_search(self, arg_str: str, channel: MessageChannel, userid: Union[str, int] = None): """ 远程搜索订阅,发送消息 """ if arg_str and not str(arg_str).isdigit(): - self.post_message(title="请输入正确的命令格式:/subscribe_search [id]," - "[id]为订阅编号,不输入订阅编号时搜索所有订阅", userid=userid) + self.post_message(Notification(channel=channel, + title="请输入正确的命令格式:/subscribe_search [id]," + "[id]为订阅编号,不输入订阅编号时搜索所有订阅", userid=userid)) return if arg_str: sid = int(arg_str) subscribe = self.subscribehelper.get(sid) if not subscribe: - self.post_message(title=f"订阅编号 {sid} 不存在!", userid=userid) + self.post_message(Notification(channel=channel, + title=f"订阅编号 {sid} 不存在!", userid=userid)) return - self.post_message(title=f"开始搜索 {subscribe.name} ...", userid=userid) + self.post_message(Notification(channel=channel, + title=f"开始搜索 {subscribe.name} ...", userid=userid)) # 搜索订阅 self.search(sid=int(arg_str)) - self.post_message(title=f"{subscribe.name} 搜索完成!", userid=userid) + self.post_message(Notification(channel=channel, + title=f"{subscribe.name} 搜索完成!", userid=userid)) else: - self.post_message(title=f"开始搜索所有订阅 ...", userid=userid) + self.post_message(Notification(channel=channel, + title=f"开始搜索所有订阅 ...", userid=userid)) self.search(state='R') - self.post_message(title=f"订阅搜索完成!", userid=userid) + self.post_message(Notification(channel=channel, + title=f"订阅搜索完成!", userid=userid)) def search(self, sid: int = None, state: str = 'N', manual: bool = False): """ @@ -189,8 +201,9 @@ class SubscribeChain(ChainBase): logger.info(f'{mediainfo.title_year} 媒体库中已存在,完成订阅') self.subscribehelper.delete(subscribe.id) # 发送通知 - self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', - image=mediainfo.get_message_image()) + self.post_message(Notification(mtype=NotificationType.Subscribe, + title=f'{mediainfo.title_year}{meta.season} 已完成订阅', + image=mediainfo.get_message_image())) continue # 使用订阅的总集数和开始集数替换no_exists no_exists = self.__get_subscribe_no_exits( @@ -255,8 +268,9 @@ class SubscribeChain(ChainBase): logger.info(f'{mediainfo.title_year} 下载完成,完成订阅') self.subscribehelper.delete(subscribe.id) # 发送通知 - self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', - image=mediainfo.get_message_image()) + self.post_message(Notification(mtype=NotificationType.Subscribe, + title=f'{mediainfo.title_year}{meta.season} 已完成订阅', + image=mediainfo.get_message_image())) else: # 未完成下载 logger.info(f'{mediainfo.title_year} 未下载未完整,继续订阅 ...') @@ -343,8 +357,9 @@ class SubscribeChain(ChainBase): logger.info(f'{mediainfo.title_year} 媒体库中已存在,完成订阅') self.subscribehelper.delete(subscribe.id) # 发送通知 - self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', - image=mediainfo.get_message_image()) + self.post_message(Notification(mtype=NotificationType.Subscribe, + title=f'{mediainfo.title_year}{meta.season} 已完成订阅', + image=mediainfo.get_message_image())) continue # 使用订阅的总集数和开始集数替换no_exists no_exists = self.__get_subscribe_no_exits( @@ -404,8 +419,9 @@ class SubscribeChain(ChainBase): logger.info(f'{mediainfo.title_year} 下载完成,完成订阅') self.subscribehelper.delete(subscribe.id) # 发送通知 - self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', - image=mediainfo.get_message_image()) + self.post_message(Notification(mtype=NotificationType.Subscribe, + title=f'{mediainfo.title_year}{meta.season} 已完成订阅', + image=mediainfo.get_message_image())) else: update_date = True if downloads else False # 未完成下载,计算剩余集数 @@ -481,13 +497,14 @@ class SubscribeChain(ChainBase): "lack_episode": len(left_episodes) }) - def remote_list(self, userid: Union[str, int] = None): + def remote_list(self, channel: MessageChannel, userid: Union[str, int] = None): """ 查询订阅并发送消息 """ subscribes = self.subscribehelper.list() if not subscribes: - self.post_message(title='没有任何订阅!', userid=userid) + self.post_message(Notification(channel=channel, + title='没有任何订阅!', userid=userid)) return title = f"共有 {len(subscribes)} 个订阅,回复对应指令操作: " \ f"\n- 删除订阅:/subscribe_delete [id]" \ @@ -505,15 +522,17 @@ class SubscribeChain(ChainBase): f"_{subscribe.total_episode - (subscribe.lack_episode or subscribe.total_episode)}" f"/{subscribe.total_episode}_") # 发送列表 - self.post_message(title=title, text='\n'.join(messages), userid=userid) + self.post_message(Notification(channel=channel, + title=title, text='\n'.join(messages), userid=userid)) - def remote_delete(self, arg_str: str, userid: Union[str, int] = None): + def remote_delete(self, arg_str: str, channel: MessageChannel, userid: Union[str, int] = None): """ 删除订阅 """ if not arg_str: - self.post_message(title="请输入正确的命令格式:/subscribe_delete [id]," - "[id]为订阅编号", userid=userid) + self.post_message(Notification(channel=channel, + title="请输入正确的命令格式:/subscribe_delete [id]," + "[id]为订阅编号", userid=userid)) return arg_strs = str(arg_str).split() for arg_str in arg_strs: @@ -523,12 +542,13 @@ class SubscribeChain(ChainBase): subscribe_id = int(arg_str) subscribe = self.subscribehelper.get(subscribe_id) if not subscribe: - self.post_message(title=f"订阅编号 {subscribe_id} 不存在!", userid=userid) + self.post_message(Notification(channel=channel, + title=f"订阅编号 {subscribe_id} 不存在!", userid=userid)) return # 删除订阅 self.subscribehelper.delete(subscribe_id) # 重新发送消息 - self.remote_list() + self.remote_list(channel, userid) @staticmethod def __get_subscribe_no_exits(no_exists: Dict[int, Dict[int, NotExistMediaInfo]], diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 7db903f3..748b3988 100644 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -13,8 +13,8 @@ from app.db.models.downloadhistory import DownloadHistory from app.db.transferhistory_oper import TransferHistoryOper from app.helper.progress import ProgressHelper from app.log import logger -from app.schemas import TransferInfo, TransferTorrent -from app.schemas.types import TorrentStatus, EventType, MediaType, ProgressKey +from app.schemas import TransferInfo, TransferTorrent, Notification +from app.schemas.types import TorrentStatus, EventType, MediaType, ProgressKey, MessageChannel, NotificationType from app.utils.string import StringUtils from app.utils.system import SystemUtils @@ -30,10 +30,11 @@ class TransferChain(ChainBase): self.transferhis = TransferHistoryOper() self.progress = ProgressHelper() - def process(self, arg_str: str = None, userid: Union[str, int] = None) -> bool: + def process(self, arg_str: str = None, channel: MessageChannel = None, userid: Union[str, int] = None) -> bool: """ 获取下载器中的种子列表,并执行转移 :param arg_str: 传入的参数 (种子hash和TMDB ID) + :param channel: 消息通道 :param userid: 用户ID """ @@ -110,9 +111,12 @@ class TransferChain(ChainBase): mediainfo: MediaInfo = self.recognize_media(meta=meta) if not mediainfo: logger.warn(f'未识别到媒体信息,标题:{torrent.title}') - self.post_message(title=f"{torrent.title} 未识别到媒体信息,无法入库!\n" - f"回复:```\n/transfer {torrent.hash} [tmdbid]\n``` 手动识别转移。", - userid=userid) + self.post_message(Notification( + channel=channel, + mtype=NotificationType.Organize, + title=f"{torrent.title} 未识别到媒体信息,无法入库!\n" + f"回复:```\n/transfer {torrent.hash} [tmdbid]\n``` 手动识别转移。", + userid=userid)) # 新增转移失败历史记录 self.transferhis.add( src=str(torrent.path), @@ -134,12 +138,13 @@ class TransferChain(ChainBase): if not transferinfo or not transferinfo.target_path: # 转移失败 logger.warn(f"{torrent.title} 入库失败") - self.post_message( + self.post_message(Notification( + channel=channel, title=f"{mediainfo.title_year}{meta.season_episode} 入库失败!", text=f"原因:{transferinfo.message if transferinfo else '未知'}", image=mediainfo.get_message_image(), userid=userid - ) + )) # 新增转移失败历史记录 self.transferhis.add( src=str(torrent.path), @@ -223,7 +228,9 @@ class TransferChain(ChainBase): if transferinfo.message: msg_str = f"{msg_str},以下文件处理失败:\n{transferinfo.message}" # 发送 - self.post_message(title=msg_title, text=msg_str, image=mediainfo.get_message_image()) + self.post_message(Notification( + mtype=NotificationType.Organize, + title=msg_title, text=msg_str, image=mediainfo.get_message_image())) @staticmethod def delete_files(path: Path): diff --git a/app/chain/webhook.py b/app/chain/webhook.py index 6a23d7d1..a19a57ed 100644 --- a/app/chain/webhook.py +++ b/app/chain/webhook.py @@ -2,8 +2,9 @@ import time from typing import Any from app.chain import ChainBase +from app.schemas import Notification from app.utils.http import WebUtils -from app.schemas.types import EventType, MediaImageType, MediaType +from app.schemas.types import EventType, MediaImageType, MediaType, NotificationType class WebhookChain(ChainBase): @@ -92,4 +93,5 @@ class WebhookChain(ChainBase): image_url = _webhook_images.get(event_info.get("channel")) # 发送消息 - self.post_message(title=message_title, text=message_content, image=image_url) + self.post_message(Notification(mtype=NotificationType.MediaServer, + title=message_title, text=message_content, image=image_url)) diff --git a/app/command.py b/app/command.py index 8bdf13f6..b5863ffe 100644 --- a/app/command.py +++ b/app/command.py @@ -15,7 +15,7 @@ from app.core.event import Event as ManagerEvent from app.log import logger from app.utils.object import ObjectUtils from app.utils.singleton import Singleton -from app.schemas.types import EventType +from app.schemas.types import EventType, MessageChannel class CommandChian(ChainBase): @@ -173,7 +173,8 @@ class Command(metaclass=Singleton): """ return self._commands.get(cmd, {}) - def execute(self, cmd: str, data_str: str = "", userid: Union[str, int] = None) -> None: + def execute(self, cmd: str, data_str: str = "", + channel: MessageChannel = None, userid: Union[str, int] = None) -> None: """ 执行命令 """ @@ -187,12 +188,12 @@ class Command(metaclass=Singleton): if cmd_data: # 有内置参数直接使用内置参数 command['func'](**cmd_data) - elif args_num == 1: - # 没有用户输入参数 - command['func'](userid) + elif args_num == 2: + # 没有输入参数,只输入渠道和用户ID + command['func'](channel, userid) else: # 多个输入参数:用户输入、用户ID - command['func'](data_str, userid) + command['func'](data_str, channel, userid) else: # 没有参数 command['func']() diff --git a/app/modules/__init__.py b/app/modules/__init__.py index bca352c9..71be4ef8 100644 --- a/app/modules/__init__.py +++ b/app/modules/__init__.py @@ -1,6 +1,10 @@ from abc import abstractmethod, ABCMeta from typing import Tuple, Union +from app.db.systemconfig_oper import SystemConfigOper +from app.schemas import Notification +from app.schemas.types import SystemConfigKey, MessageChannel + class _ModuleBase(metaclass=ABCMeta): """ @@ -30,3 +34,32 @@ class _ModuleBase(metaclass=ABCMeta): :return: None,该方法可被多个模块同时处理 """ pass + + +def checkMessage(channel_type: MessageChannel): + """ + 检查消息渠道及消息类型,如不符合则不处理 + """ + + def decorator(func): + def wrapper(self, message: Notification, *args, **kwargs): + # 检查消息渠道 + if message.channel and message.channel != channel_type: + return None + else: + # 检查消息类型开关 + if message.mtype: + switchs = SystemConfigOper().get(SystemConfigKey.NotificationChannels) + for switch in switchs: + if switch.get("mtype") == message.mtype.value: + if channel_type == MessageChannel.Wechat and not switch.get("wechat"): + return None + if channel_type == MessageChannel.Telegram and not switch.get("telegram"): + return None + if channel_type == MessageChannel.Slack and not switch.get("slack"): + return None + return func(self, message, *args, **kwargs) + + return wrapper + + return decorator diff --git a/app/modules/slack/__init__.py b/app/modules/slack/__init__.py index 5a7bf42f..d65e70d9 100644 --- a/app/modules/slack/__init__.py +++ b/app/modules/slack/__init__.py @@ -5,9 +5,9 @@ from typing import Optional, Union, List, Tuple, Any from app.core.context import MediaInfo, Context from app.core.config import settings from app.log import logger -from app.modules import _ModuleBase +from app.modules import _ModuleBase, checkMessage from app.modules.slack.slack import Slack -from app.schemas import MessageChannel, CommingMessage +from app.schemas import MessageChannel, CommingMessage, Notification class SlackModule(_ModuleBase): @@ -181,40 +181,33 @@ class SlackModule(_ModuleBase): userid=userid, username=username, text=text) return None - def post_message(self, title: str, - text: str = None, image: str = None, - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Slack) + def post_message(self, message: Notification) -> Optional[bool]: """ 发送消息 - :param title: 标题 - :param text: 内容 - :param image: 图片 - :param userid: 用户ID + :param message: 消息 :return: 成功或失败 """ - return self.slack.send_msg(title=title, text=text, image=image, userid=userid) + return self.slack.send_msg(title=message.title, text=message.text, + image=message.image, userid=message.userid) - def post_medias_message(self, title: str, items: List[MediaInfo], - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Slack) + def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: """ 发送媒体信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param userid: 用户ID + :param message: 消息体 + :param medias: 媒体信息 :return: 成功或失败 """ - return self.slack.send_meidas_msg(title=title, medias=items, userid=userid) + return self.slack.send_meidas_msg(title=message.title, medias=medias, userid=message.userid) - def post_torrents_message(self, title: str, items: List[Context], - mediainfo: MediaInfo = None, - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Slack) + def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: """ 发送种子信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param mediainfo: 媒体信息 - :param userid: 用户ID + :param message: 消息体 + :param torrents: 种子信息 :return: 成功或失败 """ - return self.slack.send_torrents_msg(title=title, torrents=items, - userid=userid) + return self.slack.send_torrents_msg(title=message.title, torrents=torrents, + userid=message.userid) diff --git a/app/modules/telegram/__init__.py b/app/modules/telegram/__init__.py index 56aace31..c94c8c8d 100644 --- a/app/modules/telegram/__init__.py +++ b/app/modules/telegram/__init__.py @@ -4,9 +4,9 @@ from typing import Optional, Union, List, Tuple, Any from app.core.context import MediaInfo, Context from app.core.config import settings from app.log import logger -from app.modules import _ModuleBase +from app.modules import _ModuleBase, checkMessage from app.modules.telegram.telegram import Telegram -from app.schemas import MessageChannel, CommingMessage +from app.schemas import MessageChannel, CommingMessage, Notification class TelegramModule(_ModuleBase): @@ -91,42 +91,36 @@ class TelegramModule(_ModuleBase): userid=user_id, username=user_id, text=text) return None - def post_message(self, title: str, - text: str = None, image: str = None, userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Telegram) + def post_message(self, message: Notification) -> Optional[bool]: """ 发送消息 - :param title: 标题 - :param text: 内容 - :param image: 图片 - :param userid: 用户ID + :param message: 消息体 :return: 成功或失败 """ - return self.telegram.send_msg(title=title, text=text, image=image, userid=userid) + return self.telegram.send_msg(title=message.title, text=message.text, + image=message.image, userid=message.userid) - def post_medias_message(self, title: str, items: List[MediaInfo], - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Telegram) + def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: """ 发送媒体信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param userid: 用户ID + :param message: 消息体 + :param medias: 媒体列表 :return: 成功或失败 """ - return self.telegram.send_meidas_msg(title=title, medias=items, userid=userid) + return self.telegram.send_meidas_msg(title=message.title, medias=medias, + userid=message.userid) - def post_torrents_message(self, title: str, items: List[Context], - mediainfo: MediaInfo = None, - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Telegram) + def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: """ 发送种子信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param mediainfo: 媒体信息 - :param userid: 用户ID + :param message: 消息体 + :param torrents: 种子列表 :return: 成功或失败 """ - return self.telegram.send_torrents_msg(title=title, torrents=items, - mediainfo=mediainfo, userid=userid) + return self.telegram.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) def register_commands(self, commands: dict): """ diff --git a/app/modules/telegram/telegram.py b/app/modules/telegram/telegram.py index 79747f2c..85d3b468 100644 --- a/app/modules/telegram/telegram.py +++ b/app/modules/telegram/telegram.py @@ -133,7 +133,6 @@ class Telegram(metaclass=Singleton): return False def send_torrents_msg(self, torrents: List[Context], - mediainfo: MediaInfo = None, userid: str = "", title: str = "") -> Optional[bool]: """ 发送列表消息 @@ -141,8 +140,12 @@ class Telegram(metaclass=Singleton): if not self._telegram_token or not self._telegram_chat_id: return None + if not torrents: + return False + try: index, caption = 1, "*%s*" % title + mediainfo = torrents[0].media_info for context in torrents: torrent = context.torrent_info site_name = torrent.site_name diff --git a/app/modules/wechat/__init__.py b/app/modules/wechat/__init__.py index e5b1ded8..dffff9ae 100644 --- a/app/modules/wechat/__init__.py +++ b/app/modules/wechat/__init__.py @@ -1,13 +1,13 @@ import xml.dom.minidom from typing import Optional, Union, List, Tuple, Any -from app.core.context import MediaInfo, Context from app.core.config import settings +from app.core.context import Context, MediaInfo from app.log import logger -from app.modules import _ModuleBase +from app.modules import _ModuleBase, checkMessage from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt from app.modules.wechat.wechat import WeChat -from app.schemas import MessageChannel, CommingMessage +from app.schemas import MessageChannel, CommingMessage, Notification from app.utils.dom import DomUtils @@ -114,41 +114,35 @@ class WechatModule(_ModuleBase): logger.error(f"微信消息处理发生错误:{err}") return None - def post_message(self, title: str, - text: str = None, image: str = None, userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Wechat) + def post_message(self, message: Notification) -> Optional[bool]: """ 发送消息 - :param title: 标题 - :param text: 内容 - :param image: 图片 - :param userid: 用户ID + :param message: 消息内容 :return: 成功或失败 """ - return self.wechat.send_msg(title=title, text=text, image=image, userid=userid) + return self.wechat.send_msg(title=message.title, text=message.text, + image=message.image, userid=message.userid) - def post_medias_message(self, title: str, items: List[MediaInfo], - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Wechat) + def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: """ 发送媒体信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param userid: 用户ID + :param message: 消息内容 + :param medias: 媒体列表 :return: 成功或失败 """ # 先发送标题 - self.wechat.send_msg(title=title) + self.wechat.send_msg(title=message.title) # 再发送内容 - return self.wechat.send_medias_msg(medias=items, userid=userid) + return self.wechat.send_medias_msg(medias=medias, userid=message.userid) - def post_torrents_message(self, title: str, items: List[Context], - mediainfo: MediaInfo, - userid: Union[str, int] = None) -> Optional[bool]: + @checkMessage(MessageChannel.Wechat) + def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: """ 发送种子信息选择列表 - :param title: 标题 - :param items: 消息列表 - :param mediainfo: 媒体信息 - :param userid: 用户ID + :param message: 消息内容 + :param torrents: 种子列表 :return: 成功或失败 """ - return self.wechat.send_torrents_msg(title=title, torrents=items, mediainfo=mediainfo, userid=userid) + return self.wechat.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) diff --git a/app/modules/wechat/wechat.py b/app/modules/wechat/wechat.py index fa38d617..d892f1cc 100644 --- a/app/modules/wechat/wechat.py +++ b/app/modules/wechat/wechat.py @@ -191,7 +191,7 @@ class WeChat(metaclass=Singleton): } return self.__post_request(message_url, req_json) - def send_torrents_msg(self, torrents: List[Context], mediainfo: MediaInfo, + def send_torrents_msg(self, torrents: List[Context], userid: str = "", title: str = "") -> Optional[bool]: """ 发送列表消息 @@ -213,6 +213,7 @@ class WeChat(metaclass=Singleton): for context in torrents: torrent = context.torrent_info meta = MetaInfo(title=torrent.title, subtitle=torrent.description) + mediainfo = context.media_info torrent_title = f"{index}.【{torrent.site_name}】" \ f"{meta.season_episode} " \ f"{meta.resource_term} " \ diff --git a/app/schemas/context.py b/app/schemas/context.py index f7ee1242..80339249 100644 --- a/app/schemas/context.py +++ b/app/schemas/context.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, Dict, List +from typing import Optional, Dict, List, Union from pydantic import BaseModel @@ -321,14 +321,12 @@ class Notification(BaseModel): title: Optional[str] = None # 文本内容 text: Optional[str] = None - # 列表内容 - items: Optional[list] = [] # 图片 image: Optional[str] = None # 链接 link: Optional[str] = None # 用户ID - user_id: Optional[str] = None + userid: Optional[Union[str, int]] = None class CommingMessage(BaseModel): @@ -336,7 +334,7 @@ class CommingMessage(BaseModel): 外来消息 """ # 用户ID - userid: Optional[str] = None + userid: Optional[Union[str, int]] = None # 用户名称 username: Optional[str] = None # 消息渠道 diff --git a/app/utils/timer.py b/app/utils/timer.py index f7018b4a..6dda7aac 100644 --- a/app/utils/timer.py +++ b/app/utils/timer.py @@ -1,6 +1,7 @@ import datetime import random from typing import List +from datetime import datetime class TimerUtils: @@ -38,8 +39,6 @@ class TimerUtils: return trigger - from datetime import datetime, timedelta - @staticmethod def time_difference(input_datetime: datetime) -> str: """