From c9ecdc6c972b48bb5b0ae05b573f8310673d5737 Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 11 Jun 2023 15:03:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=90=86=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20QYWX=5FORIGIN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/data/notify.ts | 2 + back/services/notify.ts | 10 +-- sample/notify.js | 28 ++++++--- sample/notify.py | 136 ++++++++++++++++++++++++++-------------- src/utils/config.ts | 8 +++ 5 files changed, 123 insertions(+), 61 deletions(-) diff --git a/back/data/notify.ts b/back/data/notify.ts index 37e9d046..d744f01a 100644 --- a/back/data/notify.ts +++ b/back/data/notify.ts @@ -72,10 +72,12 @@ export class DingtalkBotNotification extends NotificationBaseInfo { export class WeWorkBotNotification extends NotificationBaseInfo { public weWorkBotKey = ''; + public weWorkOrigin = ''; } export class WeWorkAppNotification extends NotificationBaseInfo { public weWorkAppKey = ''; + public weWorkOrigin = ''; } export class AibotkNotification extends NotificationBaseInfo { diff --git a/back/services/notify.ts b/back/services/notify.ts index 3983b6cb..54e036c0 100644 --- a/back/services/notify.ts +++ b/back/services/notify.ts @@ -303,8 +303,8 @@ export default class NotificationService { } private async weWorkBot() { - const { weWorkBotKey } = this.params; - const url = `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${weWorkBotKey}`; + const { weWorkBotKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } = this.params; + const url = `${weWorkOrigin}/cgi-bin/webhook/send?key=${weWorkBotKey}`; try { const res: any = await got .post(url, { @@ -328,10 +328,10 @@ export default class NotificationService { } private async weWorkApp() { - const { weWorkAppKey } = this.params; + const { weWorkAppKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } = this.params; const [corpid, corpsecret, touser, agentid, thumb_media_id = '1'] = weWorkAppKey.split(','); - const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken`; + const url = `${weWorkOrigin}/cgi-bin/gettoken`; const tokenRes: any = await got .post(url, { ...this.gotOption, @@ -383,7 +383,7 @@ export default class NotificationService { try { const res: any = await got .post( - `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${tokenRes.access_token}`, + `${weWorkOrigin}/cgi-bin/message/send?access_token=${tokenRes.access_token}`, { ...this.gotOption, json: { diff --git a/sample/notify.js b/sample/notify.js index a6fb4d06..c4307640 100644 --- a/sample/notify.js +++ b/sample/notify.js @@ -76,6 +76,10 @@ let DD_BOT_TOKEN = ''; //密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串 let DD_BOT_SECRET = ''; +// =======================================企业微信基础设置=========================================== +// 企业微信反向代理地址 +//(环境变量名 QYWX_ORIGIN) +let QYWX_ORIGIN = ''; // =======================================企业微信机器人通知设置区域=========================================== //此处填你企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa //(环境变量名 QYWX_KEY) @@ -230,6 +234,12 @@ if (process.env.DD_BOT_TOKEN) { } } +if (process.env.QYWX_ORIGIN) { + QYWX_ORIGIN = process.env.QYWX_ORIGIN; +} else { + QYWX_ORIGIN = 'https://qyapi.weixin.qq.com'; +} + if (process.env.QYWX_KEY) { QYWX_KEY = process.env.QYWX_KEY; } @@ -298,11 +308,11 @@ async function sendNotify( desp += author; //增加作者信息,防止被贩卖等 // 根据标题跳过一些消息推送,环境变量:SKIP_PUSH_TITLE 用回车分隔 - let skipTitle = process.env.SKIP_PUSH_TITLE - if(skipTitle) { - if(skipTitle.split('\n').includes(text)) { - console.info(text + "在SKIP_PUSH_TITLE环境变量内,跳过推送!"); - return + let skipTitle = process.env.SKIP_PUSH_TITLE; + if (skipTitle) { + if (skipTitle.split('\n').includes(text)) { + console.info(text + '在SKIP_PUSH_TITLE环境变量内,跳过推送!'); + return; } } @@ -692,7 +702,7 @@ function ddBotNotify(text, desp) { function qywxBotNotify(text, desp) { return new Promise((resolve) => { const options = { - url: `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${QYWX_KEY}`, + url: `${QYWX_ORIGIN}/cgi-bin/webhook/send?key=${QYWX_KEY}`, json: { msgtype: 'text', text: { @@ -754,7 +764,7 @@ function qywxamNotify(text, desp) { if (QYWX_AM) { const QYWX_AM_AY = QYWX_AM.split(','); const options_accesstoken = { - url: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`, + url: `${QYWX_ORIGIN}/cgi-bin/gettoken`, json: { corpid: `${QYWX_AM_AY[0]}`, corpsecret: `${QYWX_AM_AY[1]}`, @@ -819,7 +829,7 @@ function qywxamNotify(text, desp) { }; } options = { - url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${accesstoken}`, + url: `${QYWX_ORIGIN}/cgi-bin/message/send?access_token=${accesstoken}`, json: { touser: `${ChangeUserId(desp)}`, agentid: `${QYWX_AM_AY[3]}`, @@ -1113,4 +1123,4 @@ module.exports = { }; // prettier-ignore -function Env(t,s){return new class{constructor(t,s){this.name=t,this.data=null,this.dataFile="box.dat",this.logs=[],this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,s),this.log("",`\ud83d\udd14${this.name}, \u5f00\u59cb!`)}isNode(){return"undefined"!=typeof module&&!!module.exports}isQuanX(){return"undefined"!=typeof $task}isSurge(){return"undefined"!=typeof $httpClient&&"undefined"==typeof $loon}isLoon(){return"undefined"!=typeof $loon}getScript(t){return new Promise(s=>{$.get({url:t},(t,e,i)=>s(i))})}runScript(t,s){return new Promise(e=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let o=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");o=o?1*o:20,o=s&&s.timeout?s.timeout:o;const[h,a]=i.split("@"),r={url:`http://${a}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:o},headers:{"X-Key":h,Accept:"*/*"}};$.post(r,(t,s,i)=>e(i))}).catch(t=>this.logErr(t))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s);if(!e&&!i)return{};{const i=e?t:s;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s),o=JSON.stringify(this.data);e?this.fs.writeFileSync(t,o):i?this.fs.writeFileSync(s,o):this.fs.writeFileSync(t,o)}}lodash_get(t,s,e){const i=s.replace(/\[(\d+)\]/g,".$1").split(".");let o=t;for(const t of i)if(o=Object(o)[t],void 0===o)return e;return o}lodash_set(t,s,e){return Object(t)!==t?t:(Array.isArray(s)||(s=s.toString().match(/[^.[\]]+/g)||[]),s.slice(0,-1).reduce((t,e,i)=>Object(t[e])===t[e]?t[e]:t[e]=Math.abs(s[i+1])>>0==+s[i+1]?[]:{},t)[s[s.length-1]]=e,t)}getdata(t){let s=this.getval(t);if(/^@/.test(t)){const[,e,i]=/^@(.*?)\.(.*?)$/.exec(t),o=e?this.getval(e):"";if(o)try{const t=JSON.parse(o);s=t?this.lodash_get(t,i,""):s}catch(t){s=""}}return s}setdata(t,s){let e=!1;if(/^@/.test(s)){const[,i,o]=/^@(.*?)\.(.*?)$/.exec(s),h=this.getval(i),a=i?"null"===h?null:h||"{}":"{}";try{const s=JSON.parse(a);this.lodash_set(s,o,t),e=this.setval(JSON.stringify(s),i)}catch(s){const h={};this.lodash_set(h,o,t),e=this.setval(JSON.stringify(h),i)}}else e=$.setval(t,s);return e}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,s){return this.isSurge()||this.isLoon()?$persistentStore.write(t,s):this.isQuanX()?$prefs.setValueForKey(t,s):this.isNode()?(this.data=this.loaddata(),this.data[s]=t,this.writedata(),!0):this.data&&this.data[s]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,s=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?$httpClient.get(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)}):this.isQuanX()?$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)):this.isNode()&&(this.initGotEnv(t),this.got(t).on("redirect",(t,s)=>{try{const e=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();this.ckjar.setCookieSync(e,null),s.cookieJar=this.ckjar}catch(t){this.logErr(t)}}).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)))}post(t,s=(()=>{})){if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),delete t.headers["Content-Length"],this.isSurge()||this.isLoon())$httpClient.post(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)});else if(this.isQuanX())t.method="POST",$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t));else if(this.isNode()){this.initGotEnv(t);const{url:e,...i}=t;this.got.post(e,i).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t))}}time(t){let s={"M+":(new Date).getMonth()+1,"d+":(new Date).getDate(),"H+":(new Date).getHours(),"m+":(new Date).getMinutes(),"s+":(new Date).getSeconds(),"q+":Math.floor(((new Date).getMonth()+3)/3),S:(new Date).getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,((new Date).getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in s)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?s[e]:("00"+s[e]).substr((""+s[e]).length)));return t}msg(s=t,e="",i="",o){const h=t=>!t||!this.isLoon()&&this.isSurge()?t:"string"==typeof t?this.isLoon()?t:this.isQuanX()?{"open-url":t}:void 0:"object"==typeof t&&(t["open-url"]||t["media-url"])?this.isLoon()?t["open-url"]:this.isQuanX()?t:void 0:void 0;$.isMute||(this.isSurge()||this.isLoon()?$notification.post(s,e,i,h(o)):this.isQuanX()&&$notify(s,e,i,h(o))),this.logs.push("","==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="),this.logs.push(s),e&&this.logs.push(e),i&&this.logs.push(i)}log(...t){t.length>0?this.logs=[...this.logs,...t]:console.log(this.logs.join(this.logSeparator))}logErr(t,s){const e=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();e?$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t.stack):$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t)}wait(t){return new Promise(s=>setTimeout(s,t))}done(t={}){const s=(new Date).getTime(),e=(s-this.startTime)/1e3;this.log("",`\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${e} \u79d2`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,s)} +function Env(t, s) { return new class { constructor(t, s) { this.name = t, this.data = null, this.dataFile = "box.dat", this.logs = [], this.logSeparator = "\n", this.startTime = (new Date).getTime(), Object.assign(this, s), this.log("", `\ud83d\udd14${this.name}, \u5f00\u59cb!`) } isNode() { return "undefined" != typeof module && !!module.exports } isQuanX() { return "undefined" != typeof $task } isSurge() { return "undefined" != typeof $httpClient && "undefined" == typeof $loon } isLoon() { return "undefined" != typeof $loon } getScript(t) { return new Promise(s => { $.get({ url: t }, (t, e, i) => s(i)) }) } runScript(t, s) { return new Promise(e => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let o = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); o = o ? 1 * o : 20, o = s && s.timeout ? s.timeout : o; const [h, a] = i.split("@"), r = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: o }, headers: { "X-Key": h, Accept: "*/*" } }; $.post(r, (t, s, i) => e(i)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), s = this.path.resolve(process.cwd(), this.dataFile), e = this.fs.existsSync(t), i = !e && this.fs.existsSync(s); if (!e && !i) return {}; { const i = e ? t : s; try { return JSON.parse(this.fs.readFileSync(i)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), s = this.path.resolve(process.cwd(), this.dataFile), e = this.fs.existsSync(t), i = !e && this.fs.existsSync(s), o = JSON.stringify(this.data); e ? this.fs.writeFileSync(t, o) : i ? this.fs.writeFileSync(s, o) : this.fs.writeFileSync(t, o) } } lodash_get(t, s, e) { const i = s.replace(/\[(\d+)\]/g, ".$1").split("."); let o = t; for (const t of i) if (o = Object(o)[t], void 0 === o) return e; return o } lodash_set(t, s, e) { return Object(t) !== t ? t : (Array.isArray(s) || (s = s.toString().match(/[^.[\]]+/g) || []), s.slice(0, -1).reduce((t, e, i) => Object(t[e]) === t[e] ? t[e] : t[e] = Math.abs(s[i + 1]) >> 0 == +s[i + 1] ? [] : {}, t)[s[s.length - 1]] = e, t) } getdata(t) { let s = this.getval(t); if (/^@/.test(t)) { const [, e, i] = /^@(.*?)\.(.*?)$/.exec(t), o = e ? this.getval(e) : ""; if (o) try { const t = JSON.parse(o); s = t ? this.lodash_get(t, i, "") : s } catch (t) { s = "" } } return s } setdata(t, s) { let e = !1; if (/^@/.test(s)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(s), h = this.getval(i), a = i ? "null" === h ? null : h || "{}" : "{}"; try { const s = JSON.parse(a); this.lodash_set(s, o, t), e = this.setval(JSON.stringify(s), i) } catch (s) { const h = {}; this.lodash_set(h, o, t), e = this.setval(JSON.stringify(h), i) } } else e = $.setval(t, s); return e } getval(t) { return this.isSurge() || this.isLoon() ? $persistentStore.read(t) : this.isQuanX() ? $prefs.valueForKey(t) : this.isNode() ? (this.data = this.loaddata(), this.data[t]) : this.data && this.data[t] || null } setval(t, s) { return this.isSurge() || this.isLoon() ? $persistentStore.write(t, s) : this.isQuanX() ? $prefs.setValueForKey(t, s) : this.isNode() ? (this.data = this.loaddata(), this.data[s] = t, this.writedata(), !0) : this.data && this.data[s] || null } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, s = (() => { })) { t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"]), this.isSurge() || this.isLoon() ? $httpClient.get(t, (t, e, i) => { !t && e && (e.body = i, e.statusCode = e.status), s(t, e, i) }) : this.isQuanX() ? $task.fetch(t).then(t => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h) }, t => s(t)) : this.isNode() && (this.initGotEnv(t), this.got(t).on("redirect", (t, s) => { try { const e = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); this.ckjar.setCookieSync(e, null), s.cookieJar = this.ckjar } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h) }, t => s(t))) } post(t, s = (() => { })) { if (t.body && t.headers && !t.headers["Content-Type"] && (t.headers["Content-Type"] = "application/x-www-form-urlencoded"), delete t.headers["Content-Length"], this.isSurge() || this.isLoon()) $httpClient.post(t, (t, e, i) => { !t && e && (e.body = i, e.statusCode = e.status), s(t, e, i) }); else if (this.isQuanX()) t.method = "POST", $task.fetch(t).then(t => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h) }, t => s(t)); else if (this.isNode()) { this.initGotEnv(t); const { url: e, ...i } = t; this.got.post(e, i).then(t => { const { statusCode: e, statusCode: i, headers: o, body: h } = t; s(null, { status: e, statusCode: i, headers: o, body: h }, h) }, t => s(t)) } } time(t) { let s = { "M+": (new Date).getMonth() + 1, "d+": (new Date).getDate(), "H+": (new Date).getHours(), "m+": (new Date).getMinutes(), "s+": (new Date).getSeconds(), "q+": Math.floor(((new Date).getMonth() + 3) / 3), S: (new Date).getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, ((new Date).getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in s) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? s[e] : ("00" + s[e]).substr(("" + s[e]).length))); return t } msg(s = t, e = "", i = "", o) { const h = t => !t || !this.isLoon() && this.isSurge() ? t : "string" == typeof t ? this.isLoon() ? t : this.isQuanX() ? { "open-url": t } : void 0 : "object" == typeof t && (t["open-url"] || t["media-url"]) ? this.isLoon() ? t["open-url"] : this.isQuanX() ? t : void 0 : void 0; $.isMute || (this.isSurge() || this.isLoon() ? $notification.post(s, e, i, h(o)) : this.isQuanX() && $notify(s, e, i, h(o))), this.logs.push("", "==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="), this.logs.push(s), e && this.logs.push(e), i && this.logs.push(i) } log(...t) { t.length > 0 ? this.logs = [...this.logs, ...t] : console.log(this.logs.join(this.logSeparator)) } logErr(t, s) { const e = !this.isSurge() && !this.isQuanX() && !this.isLoon(); e ? $.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t.stack) : $.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t) } wait(t) { return new Promise(s => setTimeout(s, t)) } done(t = {}) { const s = (new Date).getTime(), e = (s - this.startTime) / 1e3; this.log("", `\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${e} \u79d2`), this.log(), (this.isSurge() || this.isQuanX() || this.isLoon()) && $done(t) } }(t, s) } diff --git a/sample/notify.py b/sample/notify.py index 9b1bd802..b550e9a6 100644 --- a/sample/notify.py +++ b/sample/notify.py @@ -76,6 +76,8 @@ push_config = { 'QMSG_KEY': '', # qmsg 酱的 QMSG_KEY 'QMSG_TYPE': '', # qmsg 酱的 QMSG_TYPE + 'QYWX_ORIGIN': '', # 企业微信代理地址 + 'QYWX_AM': '', # 企业微信应用 'QYWX_KEY': '', # 企业微信机器人 @@ -164,8 +166,7 @@ def dingding_bot(title: str, content: str) -> None: timestamp = str(round(time.time() * 1000)) secret_enc = push_config.get("DD_BOT_SECRET").encode("utf-8") - string_to_sign = "{}\n{}".format( - timestamp, push_config.get("DD_BOT_SECRET")) + string_to_sign = "{}\n{}".format(timestamp, push_config.get("DD_BOT_SECRET")) string_to_sign_enc = string_to_sign.encode("utf-8") hmac_code = hmac.new( secret_enc, string_to_sign_enc, digestmod=hashlib.sha256 @@ -231,8 +232,11 @@ def gotify(title: str, content: str) -> None: print("gotify 服务启动") url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}' - data = {"title": title, "message": content, - "priority": push_config.get("GOTIFY_PRIORITY")} + data = { + "title": title, + "message": content, + "priority": push_config.get("GOTIFY_PRIORITY"), + } response = requests.post(url, data=data).json() if response.get("id"): @@ -291,9 +295,13 @@ def pushdeer(title: str, content: str) -> None: print("PushDeer 服务的 DEER_KEY 未设置!!\n取消推送") return print("PushDeer 服务启动") - data = {"text": title, "desp": content, "type": "markdown", - "pushkey": push_config.get("DEER_KEY")} - url = 'https://api2.pushdeer.com/message/push' + data = { + "text": title, + "desp": content, + "type": "markdown", + "pushkey": push_config.get("DEER_KEY"), + } + url = "https://api2.pushdeer.com/message/push" if push_config.get("DEER_URL"): url = push_config.get("DEER_URL") @@ -313,7 +321,7 @@ def chat(title: str, content: str) -> None: print("chat 服务的 CHAT_URL或CHAT_TOKEN 未设置!!\n取消推送") return print("chat 服务启动") - data = 'payload=' + json.dumps({'text': title + '\n' + content}) + data = "payload=" + json.dumps({"text": title + "\n" + content}) url = push_config.get("CHAT_URL") + push_config.get("CHAT_TOKEN") response = requests.post(url, data=data) @@ -347,11 +355,9 @@ def pushplus_bot(title: str, content: str) -> None: print("PUSHPLUS 推送成功!") else: - url_old = "http://pushplus.hxtrip.com/send" headers["Accept"] = "application/json" - response = requests.post( - url=url_old, data=body, headers=headers).json() + response = requests.post(url=url_old, data=body, headers=headers).json() if response["code"] == 200: print("PUSHPLUS(hxtrip) 推送成功!") @@ -370,8 +376,7 @@ def qmsg_bot(title: str, content: str) -> None: print("qmsg 服务启动") url = f'https://qmsg.zendee.cn/{push_config.get("QMSG_TYPE")}/{push_config.get("QMSG_KEY")}' - payload = { - "msg": f'{title}\n\n{content.replace("----", "-")}'.encode("utf-8")} + payload = {"msg": f'{title}\n\n{content.replace("----", "-")}'.encode("utf-8")} response = requests.post(url=url, params=payload).json() if response["code"] == 0: @@ -420,9 +425,12 @@ class WeCom: self.CORPID = corpid self.CORPSECRET = corpsecret self.AGENTID = agentid + self.ORIGIN = "https://qyapi.weixin.qq.com" + if push_config.get("QYWX_ORIGIN"): + self.ORIGIN = push_config.get("QYWX_ORIGIN") def get_access_token(self): - url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken" + url = f"{self.ORIGIN}/cgi-bin/gettoken" values = { "corpid": self.CORPID, "corpsecret": self.CORPSECRET, @@ -432,10 +440,7 @@ class WeCom: return data["access_token"] def send_text(self, message, touser="@all"): - send_url = ( - "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" - + self.get_access_token() - ) + send_url = f"{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}" send_values = { "touser": touser, "msgtype": "text", @@ -449,10 +454,7 @@ class WeCom: return respone["errmsg"] def send_mpnews(self, title, message, media_id, touser="@all"): - send_url = ( - "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" - + self.get_access_token() - ) + send_url = f"https://{self.HOST}/cgi-bin/message/send?access_token={self.get_access_token()}" send_values = { "touser": touser, "msgtype": "mpnews", @@ -485,7 +487,11 @@ def wecom_bot(title: str, content: str) -> None: return print("企业微信机器人服务启动") - url = f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={push_config.get('QYWX_KEY')}" + origin = "https://qyapi.weixin.qq.com" + if push_config.get("QYWX_ORIGIN"): + origin = push_config.get("QYWX_ORIGIN") + + url = f"{origin}/cgi-bin/webhook/send?key={push_config.get('QYWX_KEY')}" headers = {"Content-Type": "application/json;charset=utf-8"} data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}} response = requests.post( @@ -547,24 +553,28 @@ def aibotk(title: str, content: str) -> None: """ 使用 智能微秘书 推送消息。 """ - if not push_config.get("AIBOTK_KEY") or not push_config.get("AIBOTK_TYPE") or not push_config.get("AIBOTK_NAME"): + if ( + not push_config.get("AIBOTK_KEY") + or not push_config.get("AIBOTK_TYPE") + or not push_config.get("AIBOTK_NAME") + ): print("智能微秘书 的 AIBOTK_KEY 或者 AIBOTK_TYPE 或者 AIBOTK_NAME 未设置!!\n取消推送") return print("智能微秘书 服务启动") - if push_config.get("AIBOTK_TYPE") == 'room': + if push_config.get("AIBOTK_TYPE") == "room": url = "https://api-bot.aibotk.com/openapi/v1/chat/room" data = { "apiKey": push_config.get("AIBOTK_KEY"), "roomName": push_config.get("AIBOTK_NAME"), - "message": {"type": 1, "content": f'【青龙快讯】\n\n{title}\n{content}'} + "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"}, } else: url = "https://api-bot.aibotk.com/openapi/v1/chat/contact" data = { "apiKey": push_config.get("AIBOTK_KEY"), "name": push_config.get("AIBOTK_NAME"), - "message": {"type": 1, "content": f'【青龙快讯】\n\n{title}\n{content}'} + "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"}, } body = json.dumps(data).encode(encoding="utf-8") headers = {"Content-Type": "application/json"} @@ -580,29 +590,52 @@ def smtp(title: str, content: str) -> None: """ 使用 SMTP 邮件 推送消息。 """ - if not push_config.get("SMTP_SERVER") or not push_config.get("SMTP_SSL") or not push_config.get("SMTP_EMAIL") or not push_config.get("SMTP_PASSWORD") or not push_config.get("SMTP_NAME"): - print("SMTP 邮件 的 SMTP_SERVER 或者 SMTP_SSL 或者 SMTP_EMAIL 或者 SMTP_PASSWORD 或者 SMTP_NAME 未设置!!\n取消推送") + if ( + not push_config.get("SMTP_SERVER") + or not push_config.get("SMTP_SSL") + or not push_config.get("SMTP_EMAIL") + or not push_config.get("SMTP_PASSWORD") + or not push_config.get("SMTP_NAME") + ): + print( + "SMTP 邮件 的 SMTP_SERVER 或者 SMTP_SSL 或者 SMTP_EMAIL 或者 SMTP_PASSWORD 或者 SMTP_NAME 未设置!!\n取消推送" + ) return print("SMTP 邮件 服务启动") - message = MIMEText(content, 'plain', 'utf-8') - message['From'] = formataddr((Header(push_config.get( - "SMTP_NAME"), 'utf-8').encode(), push_config.get("SMTP_EMAIL"))) - message['To'] = formataddr((Header(push_config.get( - "SMTP_NAME"), 'utf-8').encode(), push_config.get("SMTP_EMAIL"))) - message['Subject'] = Header(title, 'utf-8') + message = MIMEText(content, "plain", "utf-8") + message["From"] = formataddr( + ( + Header(push_config.get("SMTP_NAME"), "utf-8").encode(), + push_config.get("SMTP_EMAIL"), + ) + ) + message["To"] = formataddr( + ( + Header(push_config.get("SMTP_NAME"), "utf-8").encode(), + push_config.get("SMTP_EMAIL"), + ) + ) + message["Subject"] = Header(title, "utf-8") try: - smtp_server = smtplib.SMTP_SSL(push_config.get("SMTP_SERVER")) if push_config.get( - "SMTP_SSL") == 'true' else smtplib.SMTP(push_config.get("SMTP_SERVER")) - smtp_server.login(push_config.get("SMTP_EMAIL"), - push_config.get("SMTP_PASSWORD")) - smtp_server.sendmail(push_config.get("SMTP_EMAIL"), - push_config.get("SMTP_EMAIL"), message.as_bytes()) + smtp_server = ( + smtplib.SMTP_SSL(push_config.get("SMTP_SERVER")) + if push_config.get("SMTP_SSL") == "true" + else smtplib.SMTP(push_config.get("SMTP_SERVER")) + ) + smtp_server.login( + push_config.get("SMTP_EMAIL"), push_config.get("SMTP_PASSWORD") + ) + smtp_server.sendmail( + push_config.get("SMTP_EMAIL"), + push_config.get("SMTP_EMAIL"), + message.as_bytes(), + ) smtp_server.close() print("SMTP 邮件 推送成功!") except Exception as e: - print(f'SMTP 邮件 推送失败!{e}') + print(f"SMTP 邮件 推送失败!{e}") def one() -> str: @@ -645,9 +678,19 @@ if push_config.get("QYWX_KEY"): notify_function.append(wecom_bot) if push_config.get("TG_BOT_TOKEN") and push_config.get("TG_USER_ID"): notify_function.append(telegram_bot) -if push_config.get("AIBOTK_KEY") and push_config.get("AIBOTK_TYPE") and push_config.get("AIBOTK_NAME"): +if ( + push_config.get("AIBOTK_KEY") + and push_config.get("AIBOTK_TYPE") + and push_config.get("AIBOTK_NAME") +): notify_function.append(aibotk) -if push_config.get("SMTP_SERVER") and push_config.get("SMTP_SSL") and push_config.get("SMTP_EMAIL") and push_config.get("SMTP_PASSWORD") and push_config.get("SMTP_NAME"): +if ( + push_config.get("SMTP_SERVER") + and push_config.get("SMTP_SSL") + and push_config.get("SMTP_EMAIL") + and push_config.get("SMTP_PASSWORD") + and push_config.get("SMTP_NAME") +): notify_function.append(smtp) @@ -659,7 +702,7 @@ def send(title: str, content: str) -> None: # 根据标题跳过一些消息推送,环境变量:SKIP_PUSH_TITLE 用回车分隔 skipTitle = os.getenv("SKIP_PUSH_TITLE") if skipTitle: - if (title in re.split("\n", skipTitle)): + if title in re.split("\n", skipTitle): print(f"{title} 在SKIP_PUSH_TITLE环境变量内,跳过推送!") return @@ -669,8 +712,7 @@ def send(title: str, content: str) -> None: content += "\n\n" + text ts = [ - threading.Thread(target=mode, args=( - title, content), name=mode.__name__) + threading.Thread(target=mode, args=(title, content), name=mode.__name__) for mode in notify_function ] [t.start() for t in ts] diff --git a/src/utils/config.ts b/src/utils/config.ts index fd95e62b..640026c4 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -196,6 +196,10 @@ export default { tip: '企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa', required: true, }, + { + label: 'weWorkOrigin', + tip: '企业微信代理地址', + }, ], weWorkApp: [ { @@ -203,6 +207,10 @@ export default { tip: 'corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型) 注意用,号隔开(英文输入法的逗号),例如:wwcfrs,B-76WERQ,qinglong,1000001,2COat', required: true, }, + { + label: 'weWorkOrigin', + tip: '企业微信代理地址', + }, ], aibotk: [ {