diff --git a/back/config/const.ts b/back/config/const.ts index 8bfeff96..8d65a877 100644 --- a/back/config/const.ts +++ b/back/config/const.ts @@ -50,6 +50,7 @@ export const NotificationModeStringMap = { 18: 'chronocat', 19: 'ntfy', 20: 'wxPusherBot', + 21: 'wxPusherSpt', } as const; export const LINUX_DEPENDENCE_COMMAND: Record< diff --git a/back/data/notify.ts b/back/data/notify.ts index 1d048dfe..b1230f8b 100644 --- a/back/data/notify.ts +++ b/back/data/notify.ts @@ -162,6 +162,10 @@ export class WxPusherBotNotification extends NotificationBaseInfo { public wxPusherBotUids = ''; } +export class WxPusherSptNotification extends NotificationBaseInfo { + public wxPusherSptList = ''; +} + export class OpeniLinkNotification extends NotificationBaseInfo { public openiLinkAppToken = ''; public openiLinkHubUrl = ''; @@ -190,4 +194,5 @@ export interface NotificationInfo LarkNotification, NtfyNotification, WxPusherBotNotification, + WxPusherSptNotification, OpeniLinkNotification {} diff --git a/back/protos/api.proto b/back/protos/api.proto index f819c062..d440b3f3 100644 --- a/back/protos/api.proto +++ b/back/protos/api.proto @@ -151,6 +151,7 @@ enum NotificationMode { chronocat = 18; ntfy = 19; wxPusherBot = 20; + wxPusherSpt = 21; } message NotificationInfo { @@ -244,6 +245,8 @@ message NotificationInfo { optional string wxPusherBotAppToken = 66; optional string wxPusherBotTopicIds = 67; optional string wxPusherBotUids = 68; + + optional string wxPusherSptList = 70; } message SystemNotifyRequest { diff --git a/back/protos/api.ts b/back/protos/api.ts index 9359b33b..649ff335 100644 --- a/back/protos/api.ts +++ b/back/protos/api.ts @@ -43,6 +43,7 @@ export enum NotificationMode { chronocat = 18, ntfy = 19, wxPusherBot = 20, + wxPusherSpt = 21, UNRECOGNIZED = -1, } @@ -111,6 +112,9 @@ export function notificationModeFromJSON(object: any): NotificationMode { case 20: case "wxPusherBot": return NotificationMode.wxPusherBot; + case 21: + case "wxPusherSpt": + return NotificationMode.wxPusherSpt; case -1: case "UNRECOGNIZED": default: @@ -162,6 +166,8 @@ export function notificationModeToJSON(object: NotificationMode): string { return "ntfy"; case NotificationMode.wxPusherBot: return "wxPusherBot"; + case NotificationMode.wxPusherSpt: + return "wxPusherSpt"; case NotificationMode.UNRECOGNIZED: default: return "UNRECOGNIZED"; @@ -393,6 +399,7 @@ export interface NotificationInfo { wxPusherBotAppToken?: string | undefined; wxPusherBotTopicIds?: string | undefined; wxPusherBotUids?: string | undefined; + wxPusherSptList?: string | undefined; } export interface SystemNotifyRequest { @@ -2959,6 +2966,7 @@ function createBaseNotificationInfo(): NotificationInfo { wxPusherBotAppToken: undefined, wxPusherBotTopicIds: undefined, wxPusherBotUids: undefined, + wxPusherSptList: undefined, }; } @@ -3171,6 +3179,9 @@ export const NotificationInfo: MessageFns = { if (message.wxPusherBotUids !== undefined) { writer.uint32(546).string(message.wxPusherBotUids); } + if (message.wxPusherSptList !== undefined) { + writer.uint32(562).string(message.wxPusherSptList); + } return writer; }, @@ -3733,6 +3744,14 @@ export const NotificationInfo: MessageFns = { message.wxPusherBotUids = reader.string(); continue; } + case 70: { + if (tag !== 562) { + break; + } + + message.wxPusherSptList = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -3825,6 +3844,7 @@ export const NotificationInfo: MessageFns = { ? globalThis.String(object.wxPusherBotTopicIds) : undefined, wxPusherBotUids: isSet(object.wxPusherBotUids) ? globalThis.String(object.wxPusherBotUids) : undefined, + wxPusherSptList: isSet(object.wxPusherSptList) ? globalThis.String(object.wxPusherSptList) : undefined, }; }, @@ -4037,6 +4057,9 @@ export const NotificationInfo: MessageFns = { if (message.wxPusherBotUids !== undefined) { obj.wxPusherBotUids = message.wxPusherBotUids; } + if (message.wxPusherSptList !== undefined) { + obj.wxPusherSptList = message.wxPusherSptList; + } return obj; }, @@ -4114,6 +4137,7 @@ export const NotificationInfo: MessageFns = { message.wxPusherBotAppToken = object.wxPusherBotAppToken ?? undefined; message.wxPusherBotTopicIds = object.wxPusherBotTopicIds ?? undefined; message.wxPusherBotUids = object.wxPusherBotUids ?? undefined; + message.wxPusherSptList = object.wxPusherSptList ?? undefined; return message; }, }; diff --git a/back/services/notify.ts b/back/services/notify.ts index 099aca12..3e492774 100644 --- a/back/services/notify.ts +++ b/back/services/notify.ts @@ -35,6 +35,7 @@ export default class NotificationService { ['chronocat', this.chronocat], ['ntfy', this.ntfy], ['wxPusherBot', this.wxPusherBot], + ['wxPusherSpt', this.wxPusherSpt], ['openiLink', this.openiLink], ]); @@ -756,6 +757,52 @@ export default class NotificationService { } } + private async wxPusherSpt() { + const { wxPusherSptList } = this.params; + // 处理 SPT,将逗号分隔的字符串转为数组 + const spts = wxPusherSptList + ? wxPusherSptList + .split(',') + .map((spt) => spt.trim()) + .filter((spt) => spt) + : []; + + if (!spts.length) { + throw new Error(t('wxPusher SPT 不能为空')); + } + if (spts.length > 10) { + throw new Error(t('wxPusher SPT 最多支持 10 个')); + } + + const url = `https://wxpusher.zjiecode.com/api/send/message/simple-push`; + const json: any = { + content: `

${this.title}


${this.content}
`, + summary: this.title, + contentType: 2, + }; + // 单个 SPT 用 spt,多个用 sptList + if (spts.length === 1) { + json.spt = spts[0]; + } else { + json.sptList = spts; + } + + try { + const res = await httpClient.post(url, { + ...this.gotOption, + json, + }); + + if (res.code === 1000) { + return true; + } else { + throw new Error(JSON.stringify(res)); + } + } catch (error: any) { + throw new Error(error.response ? error.response.body : error); + } + } + private async chronocat() { const { chronocatURL, chronocatQQ, chronocatToken } = this.params; try { diff --git a/back/shared/i18n.ts b/back/shared/i18n.ts index 2784dbbb..35866c2f 100644 --- a/back/shared/i18n.ts +++ b/back/shared/i18n.ts @@ -79,6 +79,8 @@ const messages: Record> = { 'client_id 或 client_seret 有误': 'Invalid client_id or client_secret', '订阅执行完成': 'Subscription completed', 'wxPusher 服务的 TopicIds 和 Uids 至少配置一个才行': 'wxPusher requires at least one of TopicIds or Uids', + 'wxPusher SPT 不能为空': 'wxPusher SPT cannot be empty', + 'wxPusher SPT 最多支持 10 个': 'wxPusher SPT supports at most 10 tokens', 'Url 或者 Body 中必须包含 $title': 'Url or Body must contain $title', '绝对路径必须在日志目录内或使用 /dev/null': 'Absolute path must be within log directory or use /dev/null', diff --git a/sample/config.sample.sh b/sample/config.sample.sh index 9f653e56..a5e88e05 100644 --- a/sample/config.sample.sh +++ b/sample/config.sample.sh @@ -241,14 +241,19 @@ export NTFY_PASSWORD="" export NTFY_ACTIONS="" ## 21. wxPusher -## 官方文档: https://wxpusher.zjiecode.com/docs/ -## 管理后台: https://wxpusher.zjiecode.com/admin/ +### 方式一:标准发送(更强大) +#### 官方文档: https://wxpusher.zjiecode.com/docs/ +#### 管理后台: https://wxpusher.zjiecode.com/admin/ ## wxPusher 的 appToken export WXPUSHER_APP_TOKEN="" ## wxPusher 的 topicIds,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 export WXPUSHER_TOPIC_IDS="" ## wxPusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 export WXPUSHER_UIDS="" +### 方式二:极简发送(最简单,简单好用,一键配置,更推荐) +#### wxPusher 的 SPT(极简推送),扫码获取 https://wxpusher.zjiecode.com/docs/#/?id=spt +#### 多个用英文逗号,分隔,最多10个;与上面的 appToken 方式二选一即可 +export WXPUSHER_SPT_LIST="" ## 22. 自定义通知 ## 自定义通知 接收回调的URL diff --git a/sample/notify.js b/sample/notify.js index da985b2a..c466a314 100644 --- a/sample/notify.js +++ b/sample/notify.js @@ -147,11 +147,16 @@ const push_config = { NTFY_PASSWORD: '', // 推送用户密码,可选 NTFY_ACTIONS: '', // 推送用户动作,可选 + // 方式一:标准发送(更强大) // 官方文档: https://wxpusher.zjiecode.com/docs/ // 管理后台: https://wxpusher.zjiecode.com/admin/ WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 WXPUSHER_UIDS: '', // wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 + // 方式二:极简发送(最简单,简单好用,一键配置,更推荐) + // wxPusher 的 SPT(极简推送),扫码获取 https://wxpusher.zjiecode.com/docs/#/?id=spt + // 多个用英文逗号,分隔,最多10个;与上面的 appToken 方式二选一即可 + WXPUSHER_SPT_LIST: '', // wxpusher 的 SPT(极简推送),多个用英文逗号,分隔,最多10个 // 官方文档: https://openilink.com/docs/hub/apps OPENILINK_APP_TOKEN: '', // OpeniLink 的 app_token,在 OpeniLink Hub 后台安装 App 后获取 @@ -1431,6 +1436,64 @@ function wxPusherNotify(text, desp) { }); } +function wxPusherSptNotify(text, desp) { + return new Promise((resolve) => { + const { WXPUSHER_SPT_LIST } = push_config; + if (WXPUSHER_SPT_LIST) { + // 处理 SPT,将逗号分隔的字符串转为数组 + const spts = WXPUSHER_SPT_LIST.split(',') + .map((spt) => spt.trim()) + .filter((spt) => spt); + + if (!spts.length) { + console.log('wxpusher SPT 不能为空!!'); + return resolve(); + } + if (spts.length > 10) { + console.log('wxpusher SPT 最多支持 10 个!!'); + return resolve(); + } + + const body = { + content: `

${text}


${desp}
`, + summary: text, + contentType: 2, + // 单个 SPT 用 spt,多个用 sptList + ...(spts.length === 1 ? { spt: spts[0] } : { sptList: spts }), + }; + + const options = { + url: 'https://wxpusher.zjiecode.com/api/send/message/simple-push', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + timeout, + }; + + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('wxpusher SPT 发送通知消息失败!\n', err); + } else { + if (data.code === 1000) { + console.log('wxpusher SPT 发送通知消息完成!'); + } else { + console.log(`wxpusher SPT 发送通知消息异常:${data.msg}`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + function openiLinkNotify(text, desp) { return new Promise((resolve) => { const { OPENILINK_APP_TOKEN, OPENILINK_HUB_URL, OPENILINK_CONTEXT_TOKEN } = @@ -1609,6 +1672,7 @@ async function sendNotify(text, desp, params = {}) { qmsgNotify(text, desp), // 自定义通知 ntfyNotify(text, desp), // Ntfy wxPusherNotify(text, desp), // wxpusher + wxPusherSptNotify(text, desp), // wxpusher SPT openiLinkNotify(text, desp), // OpeniLink ]); } diff --git a/sample/notify.py b/sample/notify.py index 0bc81ffd..7b10d993 100644 --- a/sample/notify.py +++ b/sample/notify.py @@ -133,9 +133,16 @@ push_config = { 'NTFY_PASSWORD': '', # 推送用户密码,可选 'NTFY_ACTIONS': '', # 推送用户动作,可选 + ### 方式一:标准发送(更强大) + #### 官方文档: https://wxpusher.zjiecode.com/docs/ + #### 管理后台: https://wxpusher.zjiecode.com/admin/ 'WXPUSHER_APP_TOKEN': '', # wxpusher 的 appToken 官方文档: https://wxpusher.zjiecode.com/docs/ 管理后台: https://wxpusher.zjiecode.com/admin/ 'WXPUSHER_TOPIC_IDS': '', # wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 'WXPUSHER_UIDS': '', # wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 + ### 方式二:极简发送(最简单,简单好用,一键配置,更推荐) + #### wxPusher 的 SPT(极简推送),扫码获取 https://wxpusher.zjiecode.com/docs/#/?id=spt + #### 多个用英文逗号,分隔,最多10个;与上面的 appToken 方式二选一即可 + 'WXPUSHER_SPT_LIST': '', # wxpusher 的 SPT(极简推送),多个用英文逗号,分隔,最多10个 官方文档: https://wxpusher.zjiecode.com/docs/#/?id=spt 'OPENILINK_APP_TOKEN': '', # OpeniLink 的 app_token,在 OpeniLink Hub 后台安装 App 后获取 官方文档: https://openilink.com/docs/hub/apps 'OPENILINK_HUB_URL': '', # OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址 @@ -902,6 +909,53 @@ def wxpusher_bot(title: str, content: str) -> None: print(f"wxpusher 推送失败!错误信息:{response.get('msg')}") +def wxpusher_spt(title: str, content: str) -> None: + """ + 通过 wxpusher 极简推送(SPT)推送消息。 + 支持的环境变量: + - WXPUSHER_SPT_LIST: SPT, 多个用英文逗号,分隔, 最多10个 + """ + if not push_config.get("WXPUSHER_SPT_LIST"): + return + + # 处理 SPT,将逗号分隔的字符串转为数组 + spts = [ + spt.strip() + for spt in push_config.get("WXPUSHER_SPT_LIST").split(",") + if spt.strip() + ] + + if not spts: + print("wxpusher 服务的 WXPUSHER_SPT_LIST 不能为空!!") + return + if len(spts) > 10: + print("wxpusher 服务的 WXPUSHER_SPT_LIST 最多支持 10 个!!") + return + + print("wxpusher SPT 服务启动") + + url = "https://wxpusher.zjiecode.com/api/send/message/simple-push" + + data = { + "content": f"

{title}


{content}
", + "summary": title, + "contentType": 2, + } + # 单个 SPT 用 spt,多个用 sptList + if len(spts) == 1: + data["spt"] = spts[0] + else: + data["sptList"] = spts + + headers = {"Content-Type": "application/json"} + response = requests.post(url=url, json=data, headers=headers).json() + + if response.get("code") == 1000: + print("wxpusher SPT 推送成功!") + else: + print(f"wxpusher SPT 推送失败!错误信息:{response.get('msg')}") + + def openilink(title: str, content: str) -> None: """ 通过 OpeniLink 推送消息。 @@ -1104,6 +1158,8 @@ def add_notify_function(): push_config.get("WXPUSHER_TOPIC_IDS") or push_config.get("WXPUSHER_UIDS") ): notify_function.append(wxpusher_bot) + if push_config.get("WXPUSHER_SPT_LIST"): + notify_function.append(wxpusher_spt) if push_config.get("OPENILINK_APP_TOKEN"): notify_function.append(openilink) if not notify_function: diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 476b433f..813eecc6 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -54,6 +54,7 @@ "wxPusherBot的appToken": "wxPusherBot's appToken, obtain according to docs https://wxpusher.zjiecode.com/docs/", "wxPusherBot的topicIds": "wxPusherBot's topicIds, at least one of topicIds or uids must be configured", "wxPusherBot的uids": "wxPusherBot's uids, at least one of topicIds or uids must be configured", + "wxPusherSpt的SPT": "WxPusher Simple Push (SPT): standalone app available on 6 platforms — Android, iOS, HarmonyOS, Windows, macOS and Linux (download the app). Scan the QR code below to get your SPT and start sending messages. Separate multiple SPTs with a comma (,), up to 10. See the official docs.
Scan to get SPT", "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则创建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "The 'group code' for one-to-many push (one-to-many push -> your group (if none, create one) -> group code). If you are the creator of the group, you need to click 'View QR code' to scan and bind, otherwise, you won't receive group messages.", "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "The 'Group Code' for One-to-Many Push (Below One-to-Many Push->Your Group (if not, create one)->Group Code, if you are the creator of the group, you also need to click 'View QR Code' to scan and bind, otherwise you cannot receive group messages)", "上传": "Upload", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 9f844f48..37e642f8 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -53,6 +53,7 @@ "wxPusherBot的appToken": "wxPusherBot的appToken, 按照文档获取 https://wxpusher.zjiecode.com/docs/", "wxPusherBot的topicIds": "wxPusherBot的topicIds, topicIds 和 uids 至少配置一个才行", "wxPusherBot的uids": "wxPusherBot的uids, topicIds 和 uids 至少配置一个才行", + "wxPusherSpt的SPT": "WxPusher 极简推送,独立 APP,支持 Android、iOS、Harmony、Window、MacOS、Linux 等 6 大平台(点击下载 APP)。扫描下方二维码获取 SPT,就能发消息。多个 SPT 用英文逗号(,)分隔,最多 10 个,更多请参考官方说明
扫码获取 SPT", "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则创建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则创建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)", "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)", "上传": "上传", diff --git a/src/pages/setting/notification.tsx b/src/pages/setting/notification.tsx index 81baa8fe..b4f1b183 100644 --- a/src/pages/setting/notification.tsx +++ b/src/pages/setting/notification.tsx @@ -70,7 +70,7 @@ const NotificationSetting = ({ data }: any) => { key={x.label} label={x.label} name={x.label} - extra={x.tip} + extra={} rules={[{ required: x.required }]} style={{ maxWidth: 400 }} > diff --git a/src/utils/config.ts b/src/utils/config.ts index 89c3fcab..e5ee7f1b 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -75,6 +75,7 @@ export default { { value: 'pushPlus', label: 'PushPlus' }, { value: 'wePlusBot', label: intl.get('微加机器人') }, { value: 'wxPusherBot', label: 'wxPusher' }, + { value: 'wxPusherSpt', label: 'WxPusher(极简推送SPT-推荐)' }, { value: 'openiLink', label: 'OpeniLink' }, { value: 'chat', label: intl.get('群晖chat') }, { value: 'email', label: intl.get('邮箱') }, @@ -365,6 +366,13 @@ export default { required: false, }, ], + wxPusherSpt: [ + { + label: 'wxPusherSptList', + tip: intl.get('wxPusherSpt的SPT'), + required: true, + }, + ], openiLink: [ { label: 'openiLinkAppToken',