mirror of
https://github.com/jxxghp/MoviePilot-Plugins.git
synced 2026-03-27 10:05:57 +00:00
WxPusher消息推送
This commit is contained in:
BIN
icons/WxPusherMsg_A.png
Normal file
BIN
icons/WxPusherMsg_A.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
10
package.json
10
package.json
@@ -1018,5 +1018,15 @@
|
||||
"history": {
|
||||
"v1.3": "消息限流发送,以缓解IYUU服务器压力"
|
||||
}
|
||||
},
|
||||
"WxPusherMsg": {
|
||||
"name": "WxPusher消息推送",
|
||||
"description": "支持微信(暂时停止)、APP(无后台)、浏览器插件通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.0",
|
||||
"icon": "WxPusherMsg_A.png",
|
||||
"author": "zhjay",
|
||||
"level": 1,
|
||||
"v2": true
|
||||
}
|
||||
}
|
||||
|
||||
334
plugins/wxpushermsg/__init__.py
Normal file
334
plugins/wxpushermsg/__init__.py
Normal file
@@ -0,0 +1,334 @@
|
||||
from typing import Any, List, Dict, Tuple, Optional
|
||||
|
||||
from app.core.event import eventmanager, Event
|
||||
from app.log import logger
|
||||
from app.plugins import _PluginBase
|
||||
from app.schemas.types import EventType, NotificationType
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
class WxPusherMsg(_PluginBase):
|
||||
"""
|
||||
WxPusher 消息通知插件
|
||||
支持微信(暂时停止)、APP(无后台)、浏览器插件通知。
|
||||
"""
|
||||
plugin_name: str = "WxPusher消息推送"
|
||||
plugin_desc: str = "支持微信(暂时停止)、APP(无后台)、浏览器插件通知。"
|
||||
plugin_icon: str = "WxPusherMsg_A.png"
|
||||
plugin_version: str = "1.0"
|
||||
plugin_author: str = "zhjay"
|
||||
author_url: str = "https://github.com/Jie795"
|
||||
plugin_config_prefix: str = "wxpushermsg_"
|
||||
plugin_order: int = 30
|
||||
auth_level: int = 1
|
||||
api_url: str = "https://wxpusher.zjiecode.com/api/send/message"
|
||||
default_content_type: int = 1
|
||||
|
||||
# 插件配置属性
|
||||
_enabled: bool = False
|
||||
_appToken: Optional[str] = None
|
||||
_contentType: int = default_content_type
|
||||
_uids: Optional[str] = None
|
||||
_topicIds: Optional[str] = None
|
||||
_msgtypes: List[str] = []
|
||||
_onlyonce: bool = False
|
||||
|
||||
def init_plugin(self, config: Optional[dict] = None) -> None:
|
||||
"""
|
||||
插件初始化,加载配置。
|
||||
"""
|
||||
if config:
|
||||
self._enabled = config.get("enabled", False)
|
||||
self._appToken = config.get("appToken")
|
||||
self._contentType = config.get("contentType", self.default_content_type)
|
||||
self._uids = config.get("uids")
|
||||
self._topicIds = config.get("topicIds")
|
||||
self._msgtypes = config.get("msgtypes") or []
|
||||
self._onlyonce = config.get("onlyonce", False)
|
||||
# 立即运行一次逻辑
|
||||
if self._onlyonce:
|
||||
try:
|
||||
event = Event(EventType.NoticeMessage, {
|
||||
"type": NotificationType.SiteMessage,
|
||||
"title": "测试消息",
|
||||
"text": "这是一条测试消息,用于验证WxPusher消息发送功能是否正常。",
|
||||
"summary": "测试消息",
|
||||
"force_send": True
|
||||
})
|
||||
self.send(event)
|
||||
except Exception as e:
|
||||
logger.error(f"WxPusher立即运行一次失败:{str(e)}")
|
||||
# 关闭一次性开关并保存配置
|
||||
self._onlyonce = False
|
||||
if hasattr(self, 'update_config'):
|
||||
self.update_config({
|
||||
"enabled": self._enabled,
|
||||
"appToken": self._appToken,
|
||||
"contentType": self._contentType,
|
||||
"uids": self._uids,
|
||||
"topicIds": self._topicIds,
|
||||
"msgtypes": self._msgtypes,
|
||||
"onlyonce": False
|
||||
})
|
||||
|
||||
def get_state(self) -> bool:
|
||||
"""
|
||||
获取插件当前启用状态。
|
||||
"""
|
||||
return bool(self._enabled and self._appToken)
|
||||
|
||||
@staticmethod
|
||||
def get_command() -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取插件命令定义。
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_api(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取插件API定义。
|
||||
"""
|
||||
return [{
|
||||
"path": "/run",
|
||||
"endpoint": self.run_once,
|
||||
"methods": ["GET"],
|
||||
"summary": "运行一次",
|
||||
"description": "运行一次WxPusher消息发送"
|
||||
}]
|
||||
|
||||
def run_once(self) -> Dict[str, Any]:
|
||||
"""
|
||||
运行一次WxPusher消息发送测试。
|
||||
"""
|
||||
if not self.get_state():
|
||||
return {"code": 500, "msg": "插件未启用或未配置"}
|
||||
try:
|
||||
event = Event(EventType.NoticeMessage, {
|
||||
"type": NotificationType.SiteMessage,
|
||||
"title": "测试消息",
|
||||
"text": "这是一条测试消息,用于验证WxPusher消息发送功能是否正常。",
|
||||
"summary": "测试消息",
|
||||
"force_send": True
|
||||
})
|
||||
self.send(event)
|
||||
return {"code": 0, "msg": "运行成功"}
|
||||
except Exception as e:
|
||||
logger.error(f"运行失败:{str(e)}")
|
||||
return {"code": 500, "msg": f"运行失败:{str(e)}"}
|
||||
|
||||
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
|
||||
"""
|
||||
获取插件配置表单及默认值。
|
||||
"""
|
||||
msg_type_options = [
|
||||
{"title": item.value, "value": item.name}
|
||||
for item in NotificationType
|
||||
]
|
||||
return [
|
||||
{
|
||||
'component': 'VForm',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12, 'md': 6},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {'model': 'enabled', 'label': '启用插件'}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12, 'md': 6},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {'model': 'onlyonce', 'label': '立即运行一次'}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {'model': 'appToken', 'label': 'WxPusher应用Token', 'placeholder': 'AT_xxx'}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'uids',
|
||||
'label': '用户UID(逗号分隔)',
|
||||
'placeholder': 'UID1,UID2',
|
||||
'hint': '用户UID和主题ID不能同时为空,至少填写一项'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'topicIds',
|
||||
'label': '主题ID(逗号分隔)',
|
||||
'placeholder': '123,456',
|
||||
'hint': '用户UID和主题ID不能同时为空,至少填写一项'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'model': 'msgtypes',
|
||||
'label': '消息类型',
|
||||
'items': msg_type_options
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {'cols': 12},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'contentType',
|
||||
'label': '内容类型',
|
||||
'items': [
|
||||
{'title': '文字', 'value': 1},
|
||||
{'title': 'HTML', 'value': 2},
|
||||
{'title': 'Markdown', 'value': 3}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], {
|
||||
"enabled": False,
|
||||
'appToken': '',
|
||||
'uids': '',
|
||||
'topicIds': '',
|
||||
'contentType': self.default_content_type,
|
||||
'msgtypes': [],
|
||||
'onlyonce': False
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_page() -> List[dict]:
|
||||
"""
|
||||
获取插件页面定义。
|
||||
"""
|
||||
pass
|
||||
|
||||
@eventmanager.register(EventType.NoticeMessage)
|
||||
def send(self, event: Event) -> None:
|
||||
"""
|
||||
发送WxPusher消息。
|
||||
"""
|
||||
if not self.get_state():
|
||||
return
|
||||
if not event or not event.event_data:
|
||||
return
|
||||
msg_body = event.event_data
|
||||
channel = msg_body.get("channel")
|
||||
if channel:
|
||||
return
|
||||
msg_type: Optional[NotificationType] = msg_body.get("type")
|
||||
title: Optional[str] = msg_body.get("title")
|
||||
text: Optional[str] = msg_body.get("text")
|
||||
summary: str = msg_body.get("summary", "")
|
||||
content_type: int = msg_body.get("contentType", self._contentType)
|
||||
uids: Optional[str] = msg_body.get("uids", self._uids)
|
||||
topic_ids: Optional[str] = msg_body.get("topicIds", self._topicIds)
|
||||
if not title and not text:
|
||||
logger.warn("标题和内容不能同时为空")
|
||||
return
|
||||
# 立即运行一次时不做类型判断
|
||||
if not msg_body.get("force_send"):
|
||||
if msg_type and (not self._msgtypes or msg_type.name not in self._msgtypes):
|
||||
logger.info(f"消息类型 {msg_type.value} 未开启消息发送")
|
||||
return
|
||||
try:
|
||||
payload = {
|
||||
"appToken": self._appToken,
|
||||
"content": text or title,
|
||||
"summary": summary or title,
|
||||
"contentType": content_type,
|
||||
}
|
||||
if uids:
|
||||
payload["uids"] = [i.strip() for i in uids.split(",") if i.strip()]
|
||||
if topic_ids:
|
||||
payload["topicIds"] = [i.strip() for i in topic_ids.split(",") if i.strip()]
|
||||
res = RequestUtils(content_type="application/json").post_res(self.api_url, json=payload)
|
||||
if res and res.status_code == 200:
|
||||
ret_json = res.json()
|
||||
code = ret_json.get('code')
|
||||
msg = ret_json.get('msg')
|
||||
if code == 1000:
|
||||
logger.info("WxPusher消息发送成功")
|
||||
else:
|
||||
logger.warn(f"WxPusher消息发送失败,错误码:{code},原因:{msg}")
|
||||
elif res is not None:
|
||||
logger.warn(f"WxPusher消息发送失败,HTTP错误码:{res.status_code},原因:{res.reason}")
|
||||
else:
|
||||
logger.warn("WxPusher消息发送失败,未获取到返回信息")
|
||||
except Exception as e:
|
||||
logger.error(f"WxPusher消息发送异常,{str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def stop_service() -> None:
|
||||
"""
|
||||
停止插件服务(如需实现)。
|
||||
"""
|
||||
# 如需实现停止服务逻辑,补充此方法
|
||||
pass
|
||||
Reference in New Issue
Block a user