diff --git a/back/data/notify.ts b/back/data/notify.ts index b0899500..1d048dfe 100644 --- a/back/data/notify.ts +++ b/back/data/notify.ts @@ -20,6 +20,7 @@ export enum NotificationMode { 'chronocat' = 'Chronocat', 'ntfy' = 'ntfy', 'wxPusherBot' = 'wxPusherBot', + 'openiLink' = 'openiLink', } abstract class NotificationBaseInfo { @@ -161,6 +162,12 @@ export class WxPusherBotNotification extends NotificationBaseInfo { public wxPusherBotUids = ''; } +export class OpeniLinkNotification extends NotificationBaseInfo { + public openiLinkAppToken = ''; + public openiLinkHubUrl = ''; + public openiLinkContextToken = ''; +} + export interface NotificationInfo extends GoCqHttpBotNotification, GotifyNotification, @@ -182,4 +189,5 @@ export interface NotificationInfo ChronocatNotification, LarkNotification, NtfyNotification, - WxPusherBotNotification {} + WxPusherBotNotification, + OpeniLinkNotification {} diff --git a/back/services/notify.ts b/back/services/notify.ts index 22609748..a8b2aeea 100644 --- a/back/services/notify.ts +++ b/back/services/notify.ts @@ -34,6 +34,7 @@ export default class NotificationService { ['chronocat', this.chronocat], ['ntfy', this.ntfy], ['wxPusherBot', this.wxPusherBot], + ['openiLink', this.openiLink], ]); private title = ''; @@ -858,4 +859,35 @@ export default class NotificationService { } return {}; } + + private async openiLink() { + const { openiLinkAppToken, openiLinkHubUrl, openiLinkContextToken } = + this.params; + const baseUrl = openiLinkHubUrl?.replace(/\/$/, '') || 'https://hub.openilink.com'; + const url = `${baseUrl}/bot/v1/message/send`; + const body: Record = { + type: 'text', + content: `${this.title}\n\n${this.content}`, + }; + if (openiLinkContextToken) { + body.context_token = openiLinkContextToken; + } + try { + const res = await httpClient.post(url, { + ...this.gotOption, + json: body, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${openiLinkAppToken}`, + }, + }); + if (res.ok) { + return true; + } else { + throw new Error(JSON.stringify(res)); + } + } catch (error: any) { + throw new Error(error.response ? error.response.body : error); + } + } } diff --git a/sample/config.sample.sh b/sample/config.sample.sh index 734f4ae5..9c93e78f 100644 --- a/sample/config.sample.sh +++ b/sample/config.sample.sh @@ -259,4 +259,13 @@ export WEBHOOK_METHOD="" ## 支持 text/plain、application/json、multipart/form-data、application/x-www-form-urlencoded export WEBHOOK_CONTENT_TYPE="" +## 23. OpeniLink +## 官方文档: https://openilink.com/docs/hub/apps +## 在 OpeniLink Hub 后台安装 App 后获取 app_token +export OPENILINK_APP_TOKEN="" +## OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址 +export OPENILINK_HUB_URL="" +## OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取 +export OPENILINK_CONTEXT_TOKEN="" + ## 其他需要的变量,脚本中需要的变量使用 export 变量名= 声明即可 diff --git a/sample/notify.js b/sample/notify.js index f935e8ce..89803264 100644 --- a/sample/notify.js +++ b/sample/notify.js @@ -151,6 +151,11 @@ const push_config = { WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 WXPUSHER_UIDS: '', // wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行 + + // 官方文档: https://openilink.com/docs/hub/apps + OPENILINK_APP_TOKEN: '', // OpeniLink 的 app_token,在 OpeniLink Hub 后台安装 App 后获取 + OPENILINK_HUB_URL: '', // OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址 + OPENILINK_CONTEXT_TOKEN: '', // OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取 }; for (const key in push_config) { @@ -1408,6 +1413,54 @@ function wxPusherNotify(text, desp) { }); } +function openiLinkNotify(text, desp) { + return new Promise((resolve) => { + const { OPENILINK_APP_TOKEN, OPENILINK_HUB_URL, OPENILINK_CONTEXT_TOKEN } = + push_config; + if (OPENILINK_APP_TOKEN) { + const baseUrl = OPENILINK_HUB_URL + ? OPENILINK_HUB_URL.replace(/\/$/, '') + : 'https://hub.openilink.com'; + const body = { + type: 'text', + content: `${text}\n\n${desp}`, + }; + if (OPENILINK_CONTEXT_TOKEN) { + body.context_token = OPENILINK_CONTEXT_TOKEN; + } + const options = { + url: `${baseUrl}/bot/v1/message/send`, + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${OPENILINK_APP_TOKEN}`, + }, + timeout, + }; + + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('OpeniLink 发送通知消息失败!\n', err); + } else { + if (data.ok) { + console.log('OpeniLink 发送通知消息成功!'); + } else { + console.log(`OpeniLink 发送通知消息异常:${data.error}`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + function parseString(input, valueFormatFn) { const regex = /(\w+):\s*((?:(?!\n\w+:).)*)/g; const matches = {}; @@ -1538,6 +1591,7 @@ async function sendNotify(text, desp, params = {}) { qmsgNotify(text, desp), // 自定义通知 ntfyNotify(text, desp), // Ntfy wxPusherNotify(text, desp), // wxpusher + openiLinkNotify(text, desp), // OpeniLink ]); } diff --git a/sample/notify.py b/sample/notify.py index 8dd885ca..46383498 100644 --- a/sample/notify.py +++ b/sample/notify.py @@ -135,6 +135,10 @@ push_config = { '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 至少配置一个才行 + + '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 时填写自己的地址 + 'OPENILINK_CONTEXT_TOKEN': '', # OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取 } # fmt: on @@ -898,6 +902,43 @@ def wxpusher_bot(title: str, content: str) -> None: print(f"wxpusher 推送失败!错误信息:{response.get('msg')}") +def openilink(title: str, content: str) -> None: + """ + 通过 OpeniLink 推送消息。 + 支持的环境变量: + - OPENILINK_APP_TOKEN: 在 OpeniLink Hub 后台安装 App 后获取的 app_token + - OPENILINK_HUB_URL: OpeniLink Hub 地址,默认为 https://hub.openilink.com + - OPENILINK_CONTEXT_TOKEN: 消息会话上下文 token,可从消息事件中获取 + """ + if not push_config.get("OPENILINK_APP_TOKEN"): + return + + print("OpeniLink 服务启动") + + base_url = ( + push_config.get("OPENILINK_HUB_URL", "").rstrip("/") + or "https://hub.openilink.com" + ) + url = f"{base_url}/bot/v1/message/send" + headers = { + "Content-Type": "application/json", + "Authorization": f'Bearer {push_config.get("OPENILINK_APP_TOKEN")}', + } + data = { + "type": "text", + "content": f"{title}\n\n{content}", + } + if push_config.get("OPENILINK_CONTEXT_TOKEN"): + data["context_token"] = push_config.get("OPENILINK_CONTEXT_TOKEN") + + response = requests.post(url=url, json=data, headers=headers).json() + + if response.get("ok"): + print("OpeniLink 推送成功!") + else: + print(f'OpeniLink 推送失败!错误信息:{response.get("error")}') + + def parse_headers(headers): if not headers: return {} @@ -1063,6 +1104,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("OPENILINK_APP_TOKEN"): + notify_function.append(openilink) if not notify_function: print(f"无推送渠道,请检查通知变量是否正确") return notify_function diff --git a/src/utils/config.ts b/src/utils/config.ts index b529a7d0..f15a48b5 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -98,6 +98,7 @@ export default { { value: 'pushPlus', label: 'PushPlus' }, { value: 'wePlusBot', label: intl.get('微加机器人') }, { value: 'wxPusherBot', label: 'wxPusher' }, + { value: 'openiLink', label: 'OpeniLink' }, { value: 'chat', label: intl.get('群晖chat') }, { value: 'email', label: intl.get('邮箱') }, { value: 'lark', label: intl.get('飞书机器人') }, @@ -387,6 +388,27 @@ export default { required: false, }, ], + openiLink: [ + { + label: 'openiLinkAppToken', + tip: intl.get( + 'OpeniLink的app_token,在OpeniLink Hub后台安装App后获取,参考 https://openilink.com/docs/hub/apps', + ), + required: true, + }, + { + label: 'openiLinkHubUrl', + tip: intl.get( + 'OpeniLink Hub地址,默认为 https://hub.openilink.com,自建Hub时填写自己的地址', + ), + }, + { + label: 'openiLinkContextToken', + tip: intl.get( + 'OpeniLink的context_token,用于标识消息会话上下文,可从消息事件中获取', + ), + }, + ], lark: [ { label: 'larkKey',