diff --git a/README.md b/README.md index 2d9b2f0a..42216c5c 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ MoviePilot需要配套下载器和媒体服务器配合使用。 - **❗SUPERUSER:** 超级管理员用户名,默认`admin`,安装后使用该用户登录后台管理界面,**注意:启动一次后再次修改该值不会生效,除非删除数据库文件!** - **❗API_TOKEN:** API密钥,默认`moviepilot`,在媒体服务器Webhook、微信回调等地址配置中需要加上`?token=`该值,建议修改为复杂字符串 +- **APP_DOMAIN:** MoviePilot WEB使用的域名,用于生成跳转链接等 - **BIG_MEMORY_MODE:** 大内存模式,默认为`false`,开启后会增加缓存数量,占用更多的内存,但响应速度会更快 - **DOH_ENABLE:** DNS over HTTPS开关,`true`/`false`,默认`true`,开启后会使用DOH对api.themoviedb.org等域名进行解析,以减少被DNS污染的情况,提升网络连通性 - **META_CACHE_EXPIRE:** 元数据识别缓存过期时间(小时),数字型,不配置或者配置为0时使用系统默认(大内存模式为7天,否则为3天),调大该值可减少themoviedb的访问次数 diff --git a/app/chain/download.py b/app/chain/download.py index 76878a31..38644ecc 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -89,7 +89,8 @@ class DownloadChain(ChainBase): title=f"{mediainfo.title_year} " f"{'%s %s' % (meta.season, download_episodes) if download_episodes else meta.season_episode} 开始下载", text=msg_text, - image=mediainfo.get_message_image())) + image=mediainfo.get_message_image(), + link=settings.MP_DOMAIN('/#/downloading'))) def download_torrent(self, torrent: TorrentInfo, channel: MessageChannel = None, @@ -841,7 +842,9 @@ class DownloadChain(ChainBase): channel=channel, mtype=NotificationType.Download, title="没有正在下载的任务!", - userid=userid)) + userid=userid, + link=settings.MP_DOMAIN('#/downloading') + )) return # 发送消息 title = f"共 {len(torrents)} 个任务正在下载:" @@ -853,8 +856,13 @@ class DownloadChain(ChainBase): f"{round(torrent.progress, 1)}%") index += 1 self.post_message(Notification( - channel=channel, mtype=NotificationType.Download, - title=title, text="\n".join(messages), userid=userid)) + channel=channel, + mtype=NotificationType.Download, + title=title, + text="\n".join(messages), + userid=userid, + link=settings.MP_DOMAIN('#/downloading') + )) def downloading(self) -> List[DownloadingTorrent]: """ diff --git a/app/chain/message.py b/app/chain/message.py index 24a2857f..4c2644dc 100644 --- a/app/chain/message.py +++ b/app/chain/message.py @@ -520,5 +520,6 @@ class MessageChain(ChainBase): self.post_torrents_message(Notification( channel=channel, title=title, - userid=userid + userid=userid, + link=settings.MP_DOMAIN('#/resource') ), torrents=items) diff --git a/app/chain/site.py b/app/chain/site.py index ef579c51..4da81c26 100644 --- a/app/chain/site.py +++ b/app/chain/site.py @@ -109,11 +109,11 @@ class SiteChain(ChainBase): user_agent = site.ua or settings.USER_AGENT url = f"{site.url}api/member/profile" headers = { - "Content-Type": "application/json", - "User-Agent": user_agent, - "Accept": "application/json, text/plain, */*", - "Authorization": site.token - } + "Content-Type": "application/json", + "User-Agent": user_agent, + "Accept": "application/json, text/plain, */*", + "Authorization": site.token + } res = RequestUtils( headers=headers, proxies=settings.PROXY if site.proxy else None, @@ -457,7 +457,8 @@ class SiteChain(ChainBase): self.post_message(Notification( channel=channel, title="没有维护任何站点信息!", - userid=userid)) + userid=userid, + link=settings.MP_DOMAIN('#/site'))) title = f"共有 {len(site_list)} 个站点,回复对应指令操作:" \ f"\n- 禁用站点:/site_disable [id]" \ f"\n- 启用站点:/site_enable [id]" \ @@ -475,7 +476,8 @@ class SiteChain(ChainBase): # 发送列表 self.post_message(Notification( channel=channel, - title=title, text="\n".join(messages), userid=userid)) + title=title, text="\n".join(messages), userid=userid, + link=settings.MP_DOMAIN('#/site'))) def remote_disable(self, arg_str, channel: MessageChannel, userid: Union[str, int] = None): """ diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 51dbfbc1..fbc00828 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -169,10 +169,15 @@ class SubscribeChain(ChainBase): else: text = f"评分:{mediainfo.vote_average}" # 群发 + if mediainfo.type == MediaType.TV: + link = settings.MP_DOMAIN('#/subscribe-tv?tab=mysub') + else: + link = settings.MP_DOMAIN('#/subscribe-movie?tab=mysub') self.post_message(Notification(mtype=NotificationType.Subscribe, title=f"{mediainfo.title_year} {metainfo.season} 已添加订阅", text=text, - image=mediainfo.get_message_image())) + image=mediainfo.get_message_image(), + link=link)) # 发送事件 EventManager().send_event(EventType.SubscribeAdded, { "subscribe_id": sid, @@ -922,9 +927,14 @@ class SubscribeChain(ChainBase): # 删除订阅 self.subscribeoper.delete(subscribe.id) # 发送通知 + if mediainfo.type == MediaType.TV: + link = settings.MP_DOMAIN('#/subscribe-tv?tab=mysub') + else: + link = settings.MP_DOMAIN('#/subscribe-movie?tab=mysub') self.post_message(Notification(mtype=NotificationType.Subscribe, title=f'{mediainfo.title_year} {meta.season} 已完成{msgstr}', - image=mediainfo.get_message_image())) + image=mediainfo.get_message_image(), + link=link)) # 发送事件 EventManager().send_event(EventType.SubscribeComplete, { "subscribe_id": subscribe.id, diff --git a/app/chain/torrents.py b/app/chain/torrents.py index 9b6b1083..84ad90d8 100644 --- a/app/chain/torrents.py +++ b/app/chain/torrents.py @@ -99,7 +99,8 @@ class TorrentsChain(ChainBase, metaclass=Singleton): if not site.get("rss"): logger.error(f'站点 {domain} 未配置RSS地址!') return [] - rss_items = self.rsshelper.parse(site.get("rss"), True if site.get("proxy") else False, timeout=int(site.get("timeout") or 30)) + rss_items = self.rsshelper.parse(site.get("rss"), True if site.get("proxy") else False, + timeout=int(site.get("timeout") or 30)) if rss_items is None: # rss过期,尝试保留原配置生成新的rss self.__renew_rss_url(domain=domain, site=site) @@ -253,10 +254,14 @@ class TorrentsChain(ChainBase, metaclass=Singleton): else: # 发送消息 self.post_message( - Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期")) + Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期", + link=settings.MP_DOMAIN('#/site')) + ) else: self.post_message( - Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期")) + Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期", + link=settings.MP_DOMAIN('#/site'))) except Exception as e: logger.error(f"站点 {domain} RSS链接自动获取失败:{str(e)} - {traceback.format_exc()}") - self.post_message(Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期")) + self.post_message(Notification(mtype=NotificationType.SiteMessage, title=f"站点 {domain} RSS链接已过期", + link=settings.MP_DOMAIN('#/site'))) diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 73f0765b..38f6d359 100644 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -269,7 +269,8 @@ class TransferChain(ChainBase): self.post_message(Notification( mtype=NotificationType.Manual, title=f"{file_path.name} 未识别到媒体信息,无法入库!", - text=f"回复:```\n/redo {his.id} [tmdbid]|[类型]\n``` 手动识别转移。" + text=f"回复:```\n/redo {his.id} [tmdbid]|[类型]\n``` 手动识别转移。", + link=settings.MP_DOMAIN('#/history') )) # 计数 processed_num += 1 @@ -332,7 +333,8 @@ class TransferChain(ChainBase): mtype=NotificationType.Manual, title=f"{file_mediainfo.title_year} {file_meta.season_episode} 入库失败!", text=f"原因:{transferinfo.message or '未知'}", - image=file_mediainfo.get_message_image() + image=file_mediainfo.get_message_image(), + link=settings.MP_DOMAIN('#/history') )) # 计数 processed_num += 1 @@ -506,7 +508,7 @@ class TransferChain(ChainBase): mediaid=media_id) if not state: self.post_message(Notification(channel=channel, title="手动整理失败", - text=errmsg, userid=userid)) + text=errmsg, userid=userid, link=settings.MP_DOMAIN('#/history'))) return def re_transfer(self, logid: int, mtype: MediaType = None, @@ -642,7 +644,8 @@ class TransferChain(ChainBase): # 发送 self.post_message(Notification( mtype=NotificationType.Organize, - title=msg_title, text=msg_str, image=mediainfo.get_message_image())) + title=msg_title, text=msg_str, image=mediainfo.get_message_image(), + link=settings.MP_DOMAIN('#/history'))) def delete_files(self, path: Path) -> Tuple[bool, str]: """ diff --git a/app/core/config.py b/app/core/config.py index 0582c1f5..4342d77d 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -15,6 +15,8 @@ class Settings(BaseSettings): """ # 项目名称 PROJECT_NAME = "MoviePilot" + # 域名 格式;https://movie-pilot.org + APP_DOMAIN: str = "" # API路径 API_V1_STR: str = "/api/v1" # 前端资源路径 @@ -380,6 +382,16 @@ class Settings(BaseSettings): "privateKey": "JTixnYY0vEw97t9uukfO3UWKfHKJdT5kCQDiv3gu894" } + def MP_DOMAIN(self, url: str = None): + if not self.APP_DOMAIN: + return None + domain = self.APP_DOMAIN.rstrip("/") + if not domain.startswith("http"): + domain = "http://" + domain + if not url: + return domain + return domain + "/" + url.lstrip("/") + def __init__(self, **kwargs): super().__init__(**kwargs) with self.CONFIG_PATH as p: diff --git a/app/main.py b/app/main.py index f66fc1c3..e73b43c9 100644 --- a/app/main.py +++ b/app/main.py @@ -156,7 +156,8 @@ def check_auth(): Notification( mtype=NotificationType.Manual, title="MoviePilot用户认证", - text=err_msg + text=err_msg, + link=settings.MP_DOMAIN('#/site') ) ) @@ -165,6 +166,7 @@ def singal_handle(): """ 监听停止信号 """ + def stop_event(signum: int, _: FrameType): """ SIGTERM信号处理 diff --git a/app/modules/slack/__init__.py b/app/modules/slack/__init__.py index 1684c86a..64b12625 100644 --- a/app/modules/slack/__init__.py +++ b/app/modules/slack/__init__.py @@ -202,7 +202,7 @@ class SlackModule(_ModuleBase): :return: 成功或失败 """ self.slack.send_msg(title=message.title, text=message.text, - image=message.image, userid=message.userid) + image=message.image, userid=message.userid, link=message.link) @checkMessage(MessageChannel.Slack) def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: diff --git a/app/modules/slack/slack.py b/app/modules/slack/slack.py index b43c5283..8bf8ccbe 100644 --- a/app/modules/slack/slack.py +++ b/app/modules/slack/slack.py @@ -93,13 +93,13 @@ class Slack: """ return True if self._client else False - def send_msg(self, title: str, text: str = "", image: str = "", url: str = "", userid: str = ""): + def send_msg(self, title: str, text: str = "", image: str = "", link: str = "", userid: str = ""): """ 发送Telegram消息 :param title: 消息标题 :param text: 消息内容 :param image: 消息图片地址 - :param url: 点击消息转转的URL + :param link: 点击消息转转的URL :param userid: 用户ID,如有则只发消息给该用户 :user_id: 发送消息的目标用户ID,为空则发给管理员 """ @@ -132,7 +132,7 @@ class Slack: "alt_text": f"{title}" }}) # 链接 - if url: + if link: blocks.append({ "type": "actions", "elements": [ @@ -144,7 +144,7 @@ class Slack: "emoji": True }, "value": "click_me_url", - "url": f"{url}", + "url": f"{link}", "action_id": "actionId-url" } ] diff --git a/app/modules/synologychat/__init__.py b/app/modules/synologychat/__init__.py index 141b0e54..caf99c52 100644 --- a/app/modules/synologychat/__init__.py +++ b/app/modules/synologychat/__init__.py @@ -74,7 +74,7 @@ class SynologyChatModule(_ModuleBase): :return: 成功或失败 """ self.synologychat.send_msg(title=message.title, text=message.text, - image=message.image, userid=message.userid) + image=message.image, userid=message.userid, link=message.link) @checkMessage(MessageChannel.SynologyChat) def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: @@ -95,4 +95,5 @@ class SynologyChatModule(_ModuleBase): :param torrents: 种子列表 :return: 成功或失败 """ - return self.synologychat.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) + return self.synologychat.send_torrents_msg(title=message.title, torrents=torrents, + userid=message.userid, link=message.link) diff --git a/app/modules/synologychat/synologychat.py b/app/modules/synologychat/synologychat.py index 85fad3f3..84af58ba 100644 --- a/app/modules/synologychat/synologychat.py +++ b/app/modules/synologychat/synologychat.py @@ -36,7 +36,8 @@ class SynologyChat: return True return False - def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]: + def send_msg(self, title: str, text: str = "", image: str = "", + userid: str = "", link: str = None) -> Optional[bool]: """ 发送Telegram消息 :param title: 消息标题 @@ -44,6 +45,7 @@ class SynologyChat: :param image: 消息图片地址 :param userid: 用户ID,如有则只发消息给该用户 :user_id: 发送消息的目标用户ID,为空则发给管理员 + :param link: 链接地址 """ if not title and not text: logger.error("标题和内容不能同时为空") @@ -64,6 +66,10 @@ class SynologyChat: caption = "*%s*\n%s" % (title, text.replace("\n\n", "\n")) else: caption = title + + if link: + caption = f"{caption}\n[查看详情]({link})" + payload_data = {'text': quote(caption)} if image: payload_data['file_url'] = quote(image) @@ -127,7 +133,7 @@ class SynologyChat: return False def send_torrents_msg(self, torrents: List[Context], - userid: str = "", title: str = "") -> Optional[bool]: + userid: str = "", title: str = "", link: str = None) -> Optional[bool]: """ 发送列表消息 """ @@ -157,6 +163,9 @@ class SynologyChat: f"_{description}_" index += 1 + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: userids = [int(userid)] else: diff --git a/app/modules/telegram/__init__.py b/app/modules/telegram/__init__.py index b7d2e7a2..c2bc34db 100644 --- a/app/modules/telegram/__init__.py +++ b/app/modules/telegram/__init__.py @@ -110,7 +110,7 @@ class TelegramModule(_ModuleBase): :return: 成功或失败 """ self.telegram.send_msg(title=message.title, text=message.text, - image=message.image, userid=message.userid) + image=message.image, userid=message.userid, link=message.link) @checkMessage(MessageChannel.Telegram) def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: @@ -121,7 +121,7 @@ class TelegramModule(_ModuleBase): :return: 成功或失败 """ return self.telegram.send_meidas_msg(title=message.title, medias=medias, - userid=message.userid) + userid=message.userid, link=message.link) @checkMessage(MessageChannel.Telegram) def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: @@ -131,7 +131,8 @@ class TelegramModule(_ModuleBase): :param torrents: 种子列表 :return: 成功或失败 """ - return self.telegram.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) + return self.telegram.send_torrents_msg(title=message.title, torrents=torrents, + userid=message.userid, link=message.link) def register_commands(self, commands: Dict[str, dict]): """ diff --git a/app/modules/telegram/telegram.py b/app/modules/telegram/telegram.py index 8cbdc0a3..c6e15364 100644 --- a/app/modules/telegram/telegram.py +++ b/app/modules/telegram/telegram.py @@ -67,13 +67,15 @@ class Telegram: """ return self._bot is not None - def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]: + def send_msg(self, title: str, text: str = "", image: str = "", + userid: str = "", link: str = None) -> Optional[bool]: """ 发送Telegram消息 :param title: 消息标题 :param text: 消息内容 :param image: 消息图片地址 :param userid: 用户ID,如有则只发消息给该用户 + :param link: 跳转链接 :userid: 发送消息的目标用户ID,为空则发给管理员 """ if not self._telegram_token or not self._telegram_chat_id: @@ -89,6 +91,9 @@ class Telegram: else: caption = f"*{title}*" + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: @@ -100,7 +105,8 @@ class Telegram: logger.error(f"发送消息失败:{msg_e}") return False - def send_meidas_msg(self, medias: List[MediaInfo], userid: str = "", title: str = "") -> Optional[bool]: + def send_meidas_msg(self, medias: List[MediaInfo], userid: str = "", + title: str = "", link: str = None) -> Optional[bool]: """ 发送媒体列表消息 """ @@ -127,6 +133,9 @@ class Telegram: f"类型:{media.type.value}") index += 1 + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: @@ -139,7 +148,7 @@ class Telegram: return False def send_torrents_msg(self, torrents: List[Context], - userid: str = "", title: str = "") -> Optional[bool]: + userid: str = "", title: str = "", link: str = None) -> Optional[bool]: """ 发送列表消息 """ @@ -168,6 +177,9 @@ class Telegram: f"{StringUtils.str_filesize(torrent.size)} {free} {seeder}" index += 1 + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: diff --git a/app/modules/vocechat/__init__.py b/app/modules/vocechat/__init__.py index 465ad529..ccd4d774 100644 --- a/app/modules/vocechat/__init__.py +++ b/app/modules/vocechat/__init__.py @@ -103,7 +103,8 @@ class VoceChatModule(_ModuleBase): :param message: 消息内容 :return: 成功或失败 """ - self.vocechat.send_msg(title=message.title, text=message.text, userid=message.userid) + self.vocechat.send_msg(title=message.title, text=message.text, + userid=message.userid, link=message.link) @checkMessage(MessageChannel.VoceChat) def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: @@ -116,7 +117,8 @@ class VoceChatModule(_ModuleBase): # 先发送标题 self.vocechat.send_msg(title=message.title, userid=message.userid) # 再发送内容 - return self.vocechat.send_medias_msg(title=message.title, medias=medias, userid=message.userid) + return self.vocechat.send_medias_msg(title=message.title, medias=medias, + userid=message.userid, link=message.link) @checkMessage(MessageChannel.VoceChat) def post_torrents_message(self, message: Notification, torrents: List[Context]) -> Optional[bool]: @@ -126,7 +128,8 @@ class VoceChatModule(_ModuleBase): :param torrents: 种子列表 :return: 成功或失败 """ - return self.vocechat.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) + return self.vocechat.send_torrents_msg(title=message.title, torrents=torrents, + userid=message.userid, link=message.link) def register_commands(self, commands: Dict[str, dict]): pass diff --git a/app/modules/vocechat/vocechat.py b/app/modules/vocechat/vocechat.py index 512bb113..2ba47e67 100644 --- a/app/modules/vocechat/vocechat.py +++ b/app/modules/vocechat/vocechat.py @@ -58,12 +58,14 @@ class VoceChat: if result and result.status_code == 200: return result.json() - def send_msg(self, title: str, text: str = "", userid: str = None) -> Optional[bool]: + def send_msg(self, title: str, text: str = "", + userid: str = None, link: str = None) -> Optional[bool]: """ 微信消息发送入口,支持文本、图片、链接跳转、指定发送对象 :param title: 消息标题 :param text: 消息内容 :param userid: 消息发送对象的ID,为空则发给所有人 + :param link: 消息链接 :return: 发送状态,错误信息 """ if not self._client: @@ -79,6 +81,9 @@ class VoceChat: else: caption = f"**{title}**" + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: @@ -90,7 +95,8 @@ class VoceChat: logger.error(f"发送消息失败:{msg_e}") return False - def send_medias_msg(self, title: str, medias: List[MediaInfo], userid: str = "") -> Optional[bool]: + def send_medias_msg(self, title: str, medias: List[MediaInfo], + userid: str = "", link: str = None) -> Optional[bool]: """ 发送列表类消息 """ @@ -115,6 +121,9 @@ class VoceChat: f"类型:{media.type.value}") index += 1 + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: @@ -127,7 +136,7 @@ class VoceChat: return False def send_torrents_msg(self, torrents: List[Context], - userid: str = "", title: str = "") -> Optional[bool]: + userid: str = "", title: str = "", link: str = None) -> Optional[bool]: """ 发送列表消息 """ @@ -155,6 +164,9 @@ class VoceChat: f"{StringUtils.str_filesize(torrent.size)} {free} {seeder}" index += 1 + if link: + caption = f"{caption}\n[查看详情]({link})" + if userid: chat_id = userid else: diff --git a/app/modules/wechat/__init__.py b/app/modules/wechat/__init__.py index 5031b003..d363751c 100644 --- a/app/modules/wechat/__init__.py +++ b/app/modules/wechat/__init__.py @@ -141,7 +141,7 @@ class WechatModule(_ModuleBase): :return: 成功或失败 """ self.wechat.send_msg(title=message.title, text=message.text, - image=message.image, userid=message.userid) + image=message.image, userid=message.userid, link=message.link) @checkMessage(MessageChannel.Wechat) def post_medias_message(self, message: Notification, medias: List[MediaInfo]) -> Optional[bool]: @@ -152,7 +152,7 @@ class WechatModule(_ModuleBase): :return: 成功或失败 """ # 先发送标题 - self.wechat.send_msg(title=message.title, userid=message.userid) + self.wechat.send_msg(title=message.title, userid=message.userid, link=message.link) # 再发送内容 return self.wechat.send_medias_msg(medias=medias, userid=message.userid) @@ -164,7 +164,8 @@ class WechatModule(_ModuleBase): :param torrents: 种子列表 :return: 成功或失败 """ - return self.wechat.send_torrents_msg(title=message.title, torrents=torrents, userid=message.userid) + return self.wechat.send_torrents_msg(title=message.title, torrents=torrents, + userid=message.userid, link=message.link) def register_commands(self, commands: Dict[str, dict]): """ diff --git a/app/modules/wechat/wechat.py b/app/modules/wechat/wechat.py index 275ed91f..b6743ecd 100644 --- a/app/modules/wechat/wechat.py +++ b/app/modules/wechat/wechat.py @@ -88,12 +88,14 @@ class WeChat: return None return self._access_token - def __send_message(self, title: str, text: str = None, userid: str = None) -> Optional[bool]: + def __send_message(self, title: str, text: str = None, + userid: str = None, link: str = None) -> Optional[bool]: """ 发送文本消息 :param title: 消息标题 :param text: 消息内容 :param userid: 消息发送对象的ID,为空则发给所有人 + :param link: 跳转链接 :return: 发送状态,错误信息 """ message_url = self._send_msg_url % self.__get_access_token() @@ -102,8 +104,12 @@ class WeChat: else: conent = title + if link: + conent = f"{conent}\n点击查看:{link}" + if not userid: userid = "@all" + req_json = { "touser": userid, "msgtype": "text", @@ -148,13 +154,15 @@ class WeChat: } return self.__post_request(message_url, req_json) - def send_msg(self, title: str, text: str = "", image: str = "", userid: str = None) -> Optional[bool]: + def send_msg(self, title: str, text: str = "", image: str = "", + userid: str = None, link: str = None) -> Optional[bool]: """ 微信消息发送入口,支持文本、图片、链接跳转、指定发送对象 :param title: 消息标题 :param text: 消息内容 :param image: 图片地址 :param userid: 消息发送对象的ID,为空则发给所有人 + :param link: 跳转链接 :return: 发送状态,错误信息 """ if not self.__get_access_token(): @@ -162,9 +170,9 @@ class WeChat: return None if image: - ret_code = self.__send_image_message(title, text, image, userid) + ret_code = self.__send_image_message(title=title, text=text, image_url=image, userid=userid) else: - ret_code = self.__send_message(title, text, userid) + ret_code = self.__send_message(title=title, text=text, userid=userid, link=link) return ret_code @@ -205,7 +213,7 @@ class WeChat: return self.__post_request(message_url, req_json) def send_torrents_msg(self, torrents: List[Context], - userid: str = "", title: str = "") -> Optional[bool]: + userid: str = "", title: str = "", link: str = None) -> Optional[bool]: """ 发送列表消息 """ @@ -215,7 +223,7 @@ class WeChat: # 先发送标题 if title: - self.__send_message(title=title, userid=userid) + self.__send_message(title=title, userid=userid, link=link) # 发送列表 message_url = self._send_msg_url % self.__get_access_token() diff --git a/app/plugins/__init__.py b/app/plugins/__init__.py index 1466334d..e23055ed 100644 --- a/app/plugins/__init__.py +++ b/app/plugins/__init__.py @@ -229,6 +229,8 @@ class _PluginBase(metaclass=ABCMeta): """ 发送消息 """ + if not link: + link = settings.MP_DOMAIN(f"#/plugins?tab=installed&id={self.__class__.__name__}") self.chain.post_message(Notification( channel=channel, mtype=mtype, title=title, text=text, image=image, link=link, userid=userid diff --git a/app/scheduler.py b/app/scheduler.py index 4cd71881..8ee8b535 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -90,7 +90,8 @@ class Scheduler(metaclass=Singleton): Notification( mtype=NotificationType.Manual, title="MoviePilot用户认证成功", - text=f"使用站点:{msg}" + text=f"使用站点:{msg}", + link=settings.MP_DOMAIN('#/site') ) ) else: