feat 多通知渠道支持
This commit is contained in:
parent
c1e8b6d0ff
commit
6d2f4697b0
@ -222,5 +222,5 @@ docker pull jxxghp/moviepilot:latest
|
||||
- [x] 搜索结果过滤
|
||||
- [ ] 媒体详情页面
|
||||
- [ ] 洗版支持
|
||||
- [ ] 多通知渠道支持
|
||||
- [x] 多通知渠道支持
|
||||
|
||||
|
@ -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:
|
||||
"""
|
||||
查询缺失媒体信息
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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]:
|
||||
"""
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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]:
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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]],
|
||||
|
@ -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):
|
||||
|
@ -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))
|
||||
|
@ -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']()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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} " \
|
||||
|
@ -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
|
||||
# 消息渠道
|
||||
|
@ -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:
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user