├── .env
├── .eslintrc.js
├── .github
└── workflows
│ └── run.yml
├── .gitignore
├── LICENSE
├── README.md
├── accounts.js
├── image
├── accounts.jpg
├── action.png
├── env.png
├── fork.png
├── group.jpg
├── local.png
├── push.png
└── wxpusher.jpg
├── package.json
├── src
├── accounts.js
├── app.js
├── family.js
└── sendNotify.js
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | TY_ACCOUNTS=[{"userName":"userName","password":"password"}]
2 | # Server
3 | SENDKEY=
4 | #Telegram
5 | TELEGRAM_BOT_TOKEN=
6 | TELEGRAM_CHAT_ID=
7 | #WecomBot
8 | WECOM_BOT_KEY=
9 | WECOM_BOT_TELPHONE=
10 | #WxPusher
11 | WX_PUSHER_APP_TOKEN=
12 | WX_PUSHER_UID=
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | },
5 | extends: 'airbnb-base',
6 | overrides: [
7 | ],
8 | parserOptions: {
9 | ecmaVersion: 'latest',
10 | },
11 | rules: {
12 |
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/.github/workflows/run.yml:
--------------------------------------------------------------------------------
1 | name: Cloud check in action
2 | on:
3 | push:
4 | branches:
5 | - main
6 | - dev
7 | watch:
8 | types: started
9 | workflow_dispatch:
10 | schedule:
11 | - cron: '0 3,21 * * *'
12 | jobs:
13 | build-and-deploy-19071507010:
14 | runs-on: ubuntu-latest
15 | environment: 19071507010
16 | steps:
17 | - uses: actions/checkout@main
18 |
19 | - name: Setup Node.js environment
20 | uses: actions/setup-node@main
21 | with:
22 | node-version: 18
23 |
24 | - name: init secrets
25 | uses: shine1594/secrets-to-env-action@master
26 | with:
27 | secrets: ${{ toJSON(secrets) }}
28 | secrets_env: production
29 | prefix_prod: ""
30 | file_name_prod: .env
31 |
32 | - name: init
33 | run: npm install
34 |
35 | - name: run
36 | id: run
37 | uses: nick-fields/retry@master
38 | with:
39 | timeout_minutes: 50
40 | max_attempts: 3
41 | command: npm start
42 |
43 | - name: Keep Running
44 | run: |
45 | git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
46 | git config --local user.name "${{ github.actor }}"
47 | git remote set-url origin https://${{ github.actor }}:${{ github.token }}@github.com/${{ github.repository }}
48 | git pull --rebase --autostash
49 | git commit --allow-empty -m "Keep Running..."
50 | git push
51 |
52 | - name: Delete old workflow run
53 | uses: Mattraks/delete-workflow-runs@main
54 | with:
55 | token: ${{ github.token }}
56 | repository: ${{ github.repository }}
57 | retain_days: 0
58 | keep_minimum_runs: 50
59 |
60 | build-and-deploy-bing7027:
61 | runs-on: ubuntu-latest
62 | environment: bing7027
63 | steps:
64 | - uses: actions/checkout@main
65 |
66 | - name: Setup Node.js environment
67 | uses: actions/setup-node@main
68 | with:
69 | node-version: 18
70 |
71 | - name: init secrets
72 | uses: shine1594/secrets-to-env-action@master
73 | with:
74 | secrets: ${{ toJSON(secrets) }}
75 | secrets_env: production
76 | prefix_prod: ""
77 | file_name_prod: .env
78 |
79 | - name: init
80 | run: npm install
81 |
82 | - name: run
83 | id: run
84 | uses: nick-fields/retry@master
85 | with:
86 | timeout_minutes: 50
87 | max_attempts: 3
88 | command: npm start
89 |
90 | - name: Keep Running
91 | run: |
92 | git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
93 | git config --local user.name "${{ github.actor }}"
94 | git remote set-url origin https://${{ github.actor }}:${{ github.token }}@github.com/${{ github.repository }}
95 | git pull --rebase --autostash
96 | git commit --allow-empty -m "Keep Running..."
97 | git push
98 |
99 | - name: Delete old workflow run
100 | uses: Mattraks/delete-workflow-runs@main
101 | with:
102 | token: ${{ github.token }}
103 | repository: ${{ github.repository }}
104 | retain_days: 0
105 | keep_minimum_runs: 50
106 |
107 | build-and-deploy-user:
108 | runs-on: ubuntu-latest
109 | environment: user
110 | steps:
111 | - uses: actions/checkout@main
112 |
113 | - name: Setup Node.js environment
114 | uses: actions/setup-node@main
115 | with:
116 | node-version: 18
117 |
118 | - name: init secrets
119 | uses: shine1594/secrets-to-env-action@master
120 | with:
121 | secrets: ${{ toJSON(secrets) }}
122 | secrets_env: production
123 | prefix_prod: ""
124 | file_name_prod: .env
125 |
126 | - name: init
127 | run: npm install
128 |
129 | - name: run
130 | id: run
131 | uses: nick-fields/retry@master
132 | with:
133 | timeout_minutes: 50
134 | max_attempts: 3
135 | command: npm start
136 |
137 | - name: Keep Running
138 | run: |
139 | git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
140 | git config --local user.name "${{ github.actor }}"
141 | git remote set-url origin https://${{ github.actor }}:${{ github.token }}@github.com/${{ github.repository }}
142 | git pull --rebase --autostash
143 | git commit --allow-empty -m "Keep Running..."
144 | git push
145 |
146 | - name: Delete old workflow run
147 | uses: Mattraks/delete-workflow-runs@main
148 | with:
149 | token: ${{ github.token }}
150 | repository: ${{ github.repository }}
151 | retain_days: 0
152 | keep_minimum_runs: 50
153 |
154 |
155 | build-and-deploy-4:
156 | runs-on: ubuntu-latest
157 | environment: 4
158 | steps:
159 | - uses: actions/checkout@main
160 |
161 | - name: Setup Node.js environment
162 | uses: actions/setup-node@main
163 | with:
164 | node-version: 18
165 |
166 | - name: init secrets
167 | uses: shine1594/secrets-to-env-action@master
168 | with:
169 | secrets: ${{ toJSON(secrets) }}
170 | secrets_env: production
171 | prefix_prod: ""
172 | file_name_prod: .env
173 |
174 | - name: init
175 | run: npm install
176 |
177 | - name: run
178 | id: run
179 | uses: nick-fields/retry@master
180 | with:
181 | timeout_minutes: 50
182 | max_attempts: 3
183 | command: npm start
184 |
185 | - name: Keep Running
186 | run: |
187 | git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
188 | git config --local user.name "${{ github.actor }}"
189 | git remote set-url origin https://${{ github.actor }}:${{ github.token }}@github.com/${{ github.repository }}
190 | git pull --rebase --autostash
191 | git commit --allow-empty -m "Keep Running..."
192 | git push
193 |
194 | - name: Delete old workflow run
195 | uses: Mattraks/delete-workflow-runs@main
196 | with:
197 | token: ${{ github.token }}
198 | repository: ${{ github.repository }}
199 | retain_days: 0
200 | keep_minimum_runs: 50
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | package-lock.json
8 | tests/**/coverage/
9 |
10 | # Editor directories and files
11 | .idea
12 | .vscode
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 |
18 | cheese.log
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 WesLin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ☁ **天翼云盘签到脚本** 🤖✨
2 |
3 | ---
4 |
5 | ### 🔑 账号配置 & 环境变量
6 | **路径**: `Settings` → `Secrets and variables` → `Actions` → `Variables` → `Environment variables`
7 |
8 | 在这里新建一个 `user`
9 |
10 | 在`Environment secrets`这里添加以下变量
11 | | 变量名🐈 | 说明 📌 | 示例 🖼️ |
12 | |----------------------|-----------------------------------------------------------------------|-------------------------|
13 | | `TY_ACCOUNTS` | 账号密码组合,格式:`[{"userName":"账号","password":"密码"},...]` | `[{"userName":"u1","password":"p1"},{"userName":"u2","password":"p2"}]` |
14 | | `EXEC_THRESHOLD` | 个人云和家庭云签到线程数(不填或者默认1就行,默认主号签到一次,小号签到10次) | `1` |
15 | | `FAMILYID` | 家庭云ID抓取教程:[Alist文档](https://alist.nn.ci/zh/guide/drivers/189.html#%E5%AE%B6%E5%BA%AD%E8%BD%AC%E7%A7%BB) | `123456` |
16 | | `WXPUSHER_UIDS` | 微信推送UID(扫码获取)[二维码](https://wxpusher.zjiecode.com/api/qrcode/4Ix7noqD3L7DMBoSlvig3t4hqjFWzPkdHqAYsg8IzkPreW7d8uGUHi9LJO4EcyJg.jpg) | `UID_123` |
17 | | `WXPUSHER_APP_TOKEN` | # wxpusher 的 appToken 官方文档: https://wxpusher.zjiecode.com/docs/ 管理后台: https://wxpusher.zjiecode.com/admin/(https://wxpusher.zjiecode.com/api/qrcode/4Ix7noqD3L7DMBoSlvig3t4hqjFWzPkdHqAYsg8IzkPreW7d8uGUHi9LJO4EcyJg.jpg) | `` |
18 |
19 | ---
20 | **如果EXEC_THRESHOLD不设置。或者设置为1。就主号签到一次个人跟家庭。其他号签到10次家庭。不签到个人。如果EXEC_THRESHOLD设置其他数字。所有号个人签到EXEC_THRESHOLD次数。家庭签到EXEC_THRESHOLD次数。TY_ACCOUNTS账密。跟群主格式一致FAMILYID家庭IDEXEC_THRESHOLD线程。不设置默认保号模式PUSH_PLUS_TOKEN推送
21 | ### 🚀 快速执行指南
22 | 1️⃣ **启用Workflow**
23 | ✅ 点击仓库顶部 `操作` → **`I understand my workflows, go ahead 和 enable them`**
24 |
25 | 2️⃣ **触发运行**
26 | 🌟 给仓库点 **星标** 立即执行
27 |
28 | 3️⃣ **定时任务**
29 | ⏰ 每日 **北京时间 5:00** 自动签到
30 |
31 | ---
32 |
33 | ### 🐉 青龙面板部署
34 | ```bash
35 | ql repo https://github.com/Aijiaobin/Cloud189Checkin.git "src|.env" "image" "src|.eslintrc.js|accounts.js|config.js|serverChan.js|telegramBot.js|wecomBot.js|wxPusher.js|.env" "main" "js" "rm -rf /ql/data/repo/wes-lin_Cloud189Checkin"
36 | ```
37 |
38 | ---
39 | # 通知服务配置说明
40 |
41 | ## GOTIFY 通知
42 | - **GOTIFY_URL**:Gotify 地址(如 `https://push.example.de:8080`)
43 | - **GOTIFY_TOKEN**:Gotify 的消息应用 Token
44 | - **GOTIFY_PRIORITY**:推送消息优先级(默认 `0`)
45 |
46 | ---
47 |
48 | ## go-cqhttp 通知
49 | - **GOBOT_URL**:请求地址
50 | - 推送到个人 QQ:`http://127.0.0.1/send_private_msg`
51 | - 推送到群:`http://127.0.0.1/send_group_msg`
52 | - **GOBOT_TOKEN**:go-cqhttp 配置的访问密钥
53 | - **GOBOT_QQ**:
54 | - 若 `GOBOT_URL` 为 `/send_private_msg`,填写 `user_id=个人QQ`
55 | - 若为 `/send_group_msg`,填写 `group_id=QQ群`
56 |
57 | ---
58 |
59 | ## 微信 Server 酱通知
60 | - **PUSH_KEY**:申请的 SCKEY
61 |
62 | ---
63 |
64 | ## PushDeer 通知
65 | - **DEER_KEY**:PushDeer 的 KEY
66 | - **DEER_URL**:PushDeer 的 URL(可选,默认 `https://api2.pushdeer.com/message/push`)
67 |
68 | ---
69 |
70 | ## Synology Chat 通知
71 | - **CHAT_URL**:申请的 CHAT_URL
72 | - **CHAT_TOKEN**:申请的 CHAT_TOKEN
73 |
74 | ---
75 |
76 | ## Bark App 通知
77 | - **BARK_PUSH**:Bark 推送地址(如 `https://api.day.app/XXXXXXXX`)
78 | - **BARK_ICON**:推送图标(仅 iOS15+ 生效)
79 | - **BARK_SOUND**:推送铃声
80 | - **BARK_GROUP**:消息分组(默认 `QingLong`)
81 |
82 | ---
83 |
84 | ## Telegram 机器人通知
85 | - **TG_BOT_TOKEN**:Telegram Bot 的 Token
86 | - **TG_USER_ID**:接收消息用户的 ID
87 | - **TG_PROXY_HOST**:HTTP 代理主机地址
88 | - **TG_PROXY_PORT**:HTTP 代理端口号
89 | - **TG_PROXY_AUTH**:代理认证参数
90 | - **TG_API_HOST**:Telegram API 反向代理地址(默认 `api.telegram.org`)
91 |
92 | ---
93 |
94 | ## 钉钉机器人通知
95 | - **DD_BOT_TOKEN**:钉钉机器人 Webhook
96 | - **DD_BOT_SECRET**:加签密钥(以 `SEC` 开头)
97 |
98 | ---
99 |
100 | ## 企业微信配置
101 | ### 基础设置
102 | - **QYWX_ORIGIN**:反向代理地址(默认 `https://qyapi.weixin.qq.com`)
103 |
104 | ### 机器人通知
105 | - **QYWX_KEY**:机器人 Webhook
106 |
107 | ### 应用消息通知
108 | - **QYWX_AM**:依次填入 `corpid,corpsecret,touser(多成员用|隔开),agentid,消息类型(默认文本)`
109 |
110 | ---
111 |
112 | ## iGot 聚合推送
113 | - **IGOT_PUSH_KEY**:iGot 推送 Key
114 |
115 | ---
116 |
117 | ## Push+ 设置
118 | - **PUSH_PLUS_TOKEN**:一对一/多推送 Token
119 | - **PUSH_PLUS_USER**:群组编码(一对多模式需填)
120 |
121 | ---
122 |
123 | ## Cool Push 设置
124 | - **QQ_SKEY**:Cool Push 授权 Skey
125 | - **QQ_MODE**:推送模式
126 |
127 | ---
128 |
129 | ## 智能微秘书设置
130 | - **AIBOTK_KEY**:个人中心 API Key
131 | - **AIBOTK_TYPE**:发送目标(`room` 或 `contact`)
132 | - **AIBOTK_NAME**:目标名称(与类型对应)
133 |
134 | ---
135 |
136 | ## 飞书机器人设置
137 | - **FSKEY**:飞书机器人 Key
138 |
139 | ---
140 |
141 | ## SMTP 邮件设置
142 | - **SMTP_SERVER**:邮件服务器(如 `smtp.exmail.qq.com:465`)
143 | - **SMTP_SSL**:是否启用 SSL(`true`/`false`)
144 | - **SMTP_EMAIL**:收发件邮箱
145 | - **SMTP_PASSWORD**:登录密码/特殊口令
146 | - **SMTP_NAME**:收发件人姓名
147 |
148 | ---
149 |
150 | ## PushMe 通知
151 | - **PUSHME_KEY**:PushMe 的 KEY
152 | ## WXPUSHER通知
153 | - **WXPUSHER_APP_TOKEN:** '', // wxpusher 的 appToken
154 | - **WXPUSHER_TOPIC_IDS: **'', // wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行**
155 | - **WXPUSHER_UIDS: **'', // wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
156 |
157 | ## 🙏 **特别鸣谢**
158 | - 原项目:[wes-lin/Cloud189Checkin](https://github.com/wes-lin/Cloud189Checkin)
159 | - README优化:[ShelbyAlan](https://github.com/ShelbyAlan) 💡
160 |
161 | ## 交流群
162 |
163 | 
164 |
165 | ## [更新内容](https://github.com/wes-lin/Cloud189Checkin/wiki/更新内容)
166 |
--------------------------------------------------------------------------------
/accounts.js:
--------------------------------------------------------------------------------
1 | module.exports = JSON.parse(process.env.TY_ACCOUNTS || "[]");
2 |
--------------------------------------------------------------------------------
/image/accounts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/accounts.jpg
--------------------------------------------------------------------------------
/image/action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/action.png
--------------------------------------------------------------------------------
/image/env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/env.png
--------------------------------------------------------------------------------
/image/fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/fork.png
--------------------------------------------------------------------------------
/image/group.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/group.jpg
--------------------------------------------------------------------------------
/image/local.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/local.png
--------------------------------------------------------------------------------
/image/push.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/push.png
--------------------------------------------------------------------------------
/image/wxpusher.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aijiaobin/Cloud189Checkin/2e8b7110e2702b247ad3f173e4d7847b5f619abd/image/wxpusher.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./src/family.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "cloud189-sdk": "^1.0.5",
14 | "dotenv": "^16.4.5",
15 | "log4js": "^6.9.1",
16 | "node-jsencrypt": "^1.0.0",
17 | "superagent": "^7.1.3"
18 | },
19 | "devDependencies": {
20 | "eslint": "^8.40.0",
21 | "eslint-config-airbnb-base": "^15.0.0",
22 | "eslint-plugin-import": "^2.27.5"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/accounts.js:
--------------------------------------------------------------------------------
1 | module.exports = JSON.parse(process.env.TY_ACCOUNTS || "[]");
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 |
2 | /* eslint-disable no-await-in-loop */
3 | /*const $ = new Env('天翼网盘签到');*/
4 | require("dotenv").config();
5 | const { pushPlusNotify } = require('./sendNotify.js');
6 | const log4js = require("log4js");
7 | const recording = require("log4js/lib/appenders/recording");
8 | log4js.configure({
9 | appenders: {
10 | vcr: {
11 | type: "recording",
12 | },
13 | out: {
14 | type: "console",
15 | },
16 | },
17 | categories: { default: { appenders: ["vcr", "out"], level: "info" } },
18 | });
19 |
20 | const logger = log4js.getLogger();
21 | // process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
22 | const superagent = require("superagent");
23 | const { CloudClient } = require("cloud189-sdk");
24 | // const serverChan = require("./push/serverChan");
25 | // const telegramBot = require("./push/telegramBot");
26 | // const wecomBot = require("./push/wecomBot");
27 | // const wxpush = require("./push/wxPusher");
28 | const accounts = require("./accounts");
29 | const {sendNotify} = require("./sendNotify");
30 |
31 | const mask = (s, start, end) => s.split("").fill("*", start, end).join("");
32 |
33 | const buildTaskResult = (res, result) => {
34 | const index = result.length;
35 | if (res.errorCode === "User_Not_Chance") {
36 | result.push(`第${index}次抽奖失败,次数不足`);
37 | } else {
38 | result.push(`第${index}次抽奖成功,抽奖获得${res.prizeName}`);
39 | }
40 | };
41 |
42 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
43 |
44 | // 任务 1.签到 2.天天抽红包 3.自动备份抽红包
45 | const doTask = async (cloudClient) => {
46 | const result = [];
47 | const res1 = await cloudClient.userSign();
48 | result.push(
49 | `${res1.isSign ? "已经签到过了," : ""}签到获得${res1.netdiskBonus}M空间`
50 | );
51 | await delay(5000); // 延迟5秒
52 |
53 | const res2 = await cloudClient.taskSign();
54 | buildTaskResult(res2, result);
55 |
56 | await delay(5000); // 延迟5秒
57 | const res3 = await cloudClient.taskPhoto();
58 | buildTaskResult(res3, result);
59 |
60 | return result;
61 | };
62 |
63 | const doFamilyTask = async (cloudClient) => {
64 | const { familyInfoResp } = await cloudClient.getFamilyList();
65 | const result = [];
66 | if (familyInfoResp) {
67 | for (let index = 0; index < familyInfoResp.length; index += 1) {
68 | const { familyId } = familyInfoResp[index];
69 | const res = await cloudClient.familyUserSign(familyId);
70 | result.push(
71 | "家庭任务" +
72 | `${res.signStatus ? "已经签到过了," : ""}签到获得${
73 | res.bonusSpace
74 | }M空间`
75 | );
76 | }
77 | }
78 | return result;
79 | };
80 |
81 | const pushServerChan = (title, desp) => {
82 | if (!serverChan.sendKey) {
83 | return;
84 | }
85 | const data = {
86 | title,
87 | desp,
88 | };
89 | superagent
90 | .post(`https://sctapi.ftqq.com/${serverChan.sendKey}.send`)
91 | .type("form")
92 | .send(data)
93 | .end((err, res) => {
94 | if (err) {
95 | logger.error(`ServerChan推送失败:${JSON.stringify(err)}`);
96 | return;
97 | }
98 | const json = JSON.parse(res.text);
99 | if (json.code !== 0) {
100 | logger.error(`ServerChan推送失败:${JSON.stringify(json)}`);
101 | } else {
102 | logger.info("ServerChan推送成功");
103 | }
104 | });
105 | };
106 |
107 | const pushTelegramBot = (title, desp) => {
108 | if (!(telegramBot.botToken && telegramBot.chatId)) {
109 | return;
110 | }
111 | const data = {
112 | chat_id: telegramBot.chatId,
113 | text: `${title}\n\n${desp}`,
114 | };
115 | superagent
116 | .post(`https://api.telegram.org/bot${telegramBot.botToken}/sendMessage`)
117 | .type("form")
118 | .send(data)
119 | .end((err, res) => {
120 | if (err) {
121 | logger.error(`TelegramBot推送失败:${JSON.stringify(err)}`);
122 | return;
123 | }
124 | const json = JSON.parse(res.text);
125 | if (!json.ok) {
126 | logger.error(`TelegramBot推送失败:${JSON.stringify(json)}`);
127 | } else {
128 | logger.info("TelegramBot推送成功");
129 | }
130 | });
131 | };
132 |
133 | const pushWecomBot = (title, desp) => {
134 | if (!(wecomBot.key && wecomBot.telphone)) {
135 | return;
136 | }
137 | const data = {
138 | msgtype: "text",
139 | text: {
140 | content: `${title}\n\n${desp}`,
141 | mentioned_mobile_list: [wecomBot.telphone],
142 | },
143 | };
144 | superagent
145 | .post(
146 | `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${wecomBot.key}`
147 | )
148 | .send(data)
149 | .end((err, res) => {
150 | if (err) {
151 | logger.error(`wecomBot推送失败:${JSON.stringify(err)}`);
152 | return;
153 | }
154 | const json = JSON.parse(res.text);
155 | if (json.errcode) {
156 | logger.error(`wecomBot推送失败:${JSON.stringify(json)}`);
157 | } else {
158 | logger.info("wecomBot推送成功");
159 | }
160 | });
161 | };
162 |
163 | const pushWxPusher = (title, desp) => {
164 | if (!(wxpush.appToken && wxpush.uid)) {
165 | return;
166 | }
167 | const data = {
168 | appToken: wxpush.appToken,
169 | contentType: 1,
170 | summary: title,
171 | content: desp,
172 | uids: [wxpush.uid],
173 | };
174 | superagent
175 | .post("https://wxpusher.zjiecode.com/api/send/message")
176 | .send(data)
177 | .end((err, res) => {
178 | if (err) {
179 | logger.error(`wxPusher推送失败:${JSON.stringify(err)}`);
180 | return;
181 | }
182 | const json = JSON.parse(res.text);
183 | if (json.data[0].code !== 1000) {
184 | logger.error(`wxPusher推送失败:${JSON.stringify(json)}`);
185 | } else {
186 | logger.info("wxPusher推送成功");
187 | }
188 | });
189 | };
190 |
191 | const push = (title, desp) => {
192 | // pushServerChan(title, desp);
193 | // pushTelegramBot(title, desp);
194 | // pushWecomBot(title, desp);
195 | // pushWxPusher(title, desp);
196 | // 调用 pushPlusNotify 发送通知
197 |
198 | // pushPlusNotify("title", desp);
199 | sendNotify("天翼网盘自动签到", desp)
200 | };
201 |
202 | // 开始执行程序
203 | async function main() {
204 | for (let index = 0; index < accounts.length; index += 1) {
205 | const account = accounts[index];
206 | const { userName, password } = account;
207 | if (userName && password) {
208 | const userNameInfo = mask(userName, 3, 7);
209 | try {
210 | logger.log(`账户 ${userNameInfo}开始执行`);
211 | const cloudClient = new CloudClient(userName, password);
212 | await cloudClient.login();
213 | const result = await doTask(cloudClient);
214 | result.forEach((r) => logger.log(r));
215 | const familyResult = await doFamilyTask(cloudClient);
216 | familyResult.forEach((r) => logger.log(r));
217 | logger.log("任务执行完毕");
218 | const { cloudCapacityInfo, familyCapacityInfo } =
219 | await cloudClient.getUserSizeInfo();
220 | logger.log(
221 | `个人总容量:${(
222 | cloudCapacityInfo.totalSize /
223 | 1024 /
224 | 1024 /
225 | 1024
226 | ).toFixed(2)}G,家庭总容量:${(
227 | familyCapacityInfo.totalSize /
228 | 1024 /
229 | 1024 /
230 | 1024
231 | ).toFixed(2)}G`
232 | );
233 | } catch (e) {
234 | logger.error(e);
235 | if (e.code === "ETIMEDOUT") {
236 | throw e;
237 | }
238 | } finally {
239 | logger.log(`账户 ${userNameInfo}执行完毕`);
240 | }
241 | }
242 | }
243 | }
244 |
245 | (async () => {
246 | try {
247 | await main();
248 | } finally {
249 | const events = recording.replay();
250 | const content = events.map((e) => `${e.data.join("")}`).join(" \n");
251 | push("天翼云盘自动签到任务", content);
252 | recording.erase();
253 | }
254 | })();
255 |
--------------------------------------------------------------------------------
/src/family.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-await-in-loop */
2 | /* cron: 0 7,19 * * *
3 | const $ = new Env('天翼网盘签到'); */
4 | require('dotenv').config();
5 | const log4js = require('log4js');
6 | const { CloudClient } = require('cloud189-sdk');
7 | const { sendNotify } = require('./sendNotify');
8 | // 新增环境变量处理(在日志配置之前)
9 | const EXEC_THRESHOLD = parseInt(process.env.EXEC_THRESHOLD || 1); // 默认值为1
10 | // 日志配置
11 | log4js.configure({
12 | appenders: {
13 | debug: {
14 | type: 'console',
15 | layout: { type: 'pattern', pattern: '%[%d{hh:mm:ss} %p %f{1}:%l%] %m' },
16 | },
17 | },
18 | categories: { default: { appenders: ['debug'], level: 'debug' } },
19 | });
20 | const logger = log4js.getLogger();
21 |
22 | // 调试工具
23 | const benchmark = {
24 | start: Date.now(),
25 | lap() {
26 | return `${((Date.now() - this.start) / 1000).toFixed(2)}s`;
27 | },
28 | };
29 |
30 | // 新增工具函数:带超时的 Promise
31 | function timeout(promise, ms) {
32 | return Promise.race([
33 | promise,
34 | new Promise((_, reject) => setTimeout(() => reject(new Error(`请求超时(${ms}ms)`)), ms)),
35 | ]);
36 | }
37 |
38 | // 核心签到逻辑
39 | async function stressTest(account, familyId, personalCount = 10, familyCount = 10) {
40 | let personalTotal = 0; let
41 | familyTotal = 0;
42 | let actualPersonal = 0; let
43 | actualFamily = 0;
44 | const report = [];
45 |
46 | try {
47 | logger.debug(`🚦 开始压力测试 (账号: ${mask(account.userName)})`);
48 |
49 | const client = new CloudClient(account.userName, account.password);
50 | await client.login().catch(() => { throw new Error('登录失败'); });
51 | // 获取初始容量信息
52 | const userSizeInfo = await client.getUserSizeInfo().catch(() => null);
53 | // 个人签到10连击(新增30秒超时)
54 | const personalPromises = Array(personalCount).fill().map(() => timeout(client.userSign(), 30000) // 30秒超时控制
55 | .then((res) => {
56 | const mb = res.netdiskBonus;
57 | logger.debug(`[${Date.now()}] 🎯 个人签到 ✅ 获得: ${mb}MB`);
58 | return mb;
59 | })
60 | .catch((err) => {
61 | const message = err.message.includes('超时') ? `请求超时(30秒)` : err.message;
62 | report.push(`[${Date.now()}] 🎯 个人签到 ❌ 获得: 0MB (原因: ${message})`);
63 | return 0;
64 | }));
65 | const personalResults = await Promise.allSettled(personalPromises);
66 | personalTotal = personalResults.reduce((sum, r) => sum + r.value, 0);
67 | report.push(`🎯 个人签到完成 累计获得: ${personalTotal}MB`);
68 |
69 | // 家庭签到10连击(新增30秒超时)
70 | const familyPromises = Array(familyCount).fill().map(() => timeout(client.familyUserSign(familyId), 30000) // 30秒超时控制
71 | .then((res) => {
72 | const mb = res.bonusSpace;
73 | logger.debug(`[${Date.now()}] 🏠 家庭签到 ✅ 获得: ${mb}MB`);
74 | return mb;
75 | })
76 | .catch((err) => {
77 | const message = err.message.includes('超时') ? `请求超时(30秒)` : err.message;
78 | report.push(`[${Date.now()}] 🏠 家庭签到 ❌ 获得: 0MB (原因: ${message})`);
79 | return 0;
80 | }));
81 | const familyResults = await Promise.allSettled(familyPromises);
82 | familyTotal = familyResults.reduce((sum, r) => sum + r.value, 0);
83 | report.push(`🏠 家庭签到完成 本次获得: ${familyTotal}MB`);
84 | // 获取签到后容量信息
85 | const afterUserSizeInfo = await client.getUserSizeInfo().catch(() => null);
86 |
87 | // 计算实际容量变化
88 | if (userSizeInfo && afterUserSizeInfo) {
89 | actualPersonal = (afterUserSizeInfo.cloudCapacityInfo.totalSize - userSizeInfo.cloudCapacityInfo.totalSize) / 1024 / 1024;
90 | actualFamily = (afterUserSizeInfo.familyCapacityInfo.totalSize - userSizeInfo.familyCapacityInfo.totalSize) / 1024 / 1024;
91 | report.push(`📊 实际容量变化 | 个人: ${actualPersonal.toFixed(2)}MB | 家庭: ${actualFamily.toFixed(2)}MB`);
92 | } else {
93 | report.push(`⚠️ 容量信息获取失败,无法计算实际变化`);
94 | }
95 | return {
96 | success: true,
97 | personalTotal,
98 | familyTotal,
99 | actualFamily,
100 | report: `账号 ${mask(account.userName)}\n${report.join('\n')}`,
101 | };
102 | } catch (e) {
103 | return {
104 | success: false,
105 | report: `❌ ${mask(account.userName)} 签到失败: ${e.message}`,
106 | };
107 | }
108 | }
109 |
110 | // 辅助方法
111 | function mask(s) {
112 | return s.replace(/(\d{3})\d+(\d{4})/, '$1****$2');
113 | }
114 | const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
115 |
116 | // 修改后的执行测试
117 | (async () => {
118 | try {
119 | logger.debug('🔥 启动专项压力测试');
120 | const accounts = require('./accounts');
121 | const familyId = process.env.FAMILYID;
122 |
123 | if (!familyId) throw new Error('未配置环境变量 FAMILYID');
124 |
125 | // 新增:在主作用域声明变量
126 | let mainAccountClient = null;
127 | let initialSizeInfo = null;
128 | let finalSizeInfo = null;
129 |
130 | // 仅当存在账号时初始化主账号
131 | if (accounts.length > 0) {
132 | const mainAccount = accounts[0];
133 | mainAccountClient = new CloudClient(mainAccount.userName, mainAccount.password);
134 | await mainAccountClient.login().catch((e) => {
135 | throw new Error(`主账号登录失败: ${e.message}`);
136 | });
137 | initialSizeInfo = await mainAccountClient.getUserSizeInfo().catch(() => null);
138 | if (!initialSizeInfo) throw new Error('无法获取初始容量信息');
139 | logger.debug(`🏠 初始家庭容量: ${initialSizeInfo.familyCapacityInfo.totalSize} Bytes`);
140 | }
141 | let totalFamily = 0;
142 | let totalActualFamily = 0;
143 | const reports = [];
144 |
145 | for (let index = 0; index < accounts.length; index++) {
146 | const account = accounts[index];
147 | if (!account.userName || !account.password) {
148 | logger.error(`账号配置错误: accounts[${index}]`);
149 | continue;
150 | }
151 | // 新增签到次数控制逻辑
152 | let personalCount = 10;
153 | let familyCount = 10;
154 |
155 | if (EXEC_THRESHOLD === 1) {
156 | if (index === 0) { // 第一个账号
157 | personalCount = 1;
158 | familyCount = 1;
159 | } else { // 其他账号
160 | personalCount = 0;
161 | familyCount = 10;
162 | }
163 | } else { // 其他账号
164 | personalCount = EXEC_THRESHOLD;
165 | familyCount = EXEC_THRESHOLD;
166 | }// 若 EXEC_THRESHOLD=0 保持默认值10
167 | const result = await stressTest(
168 | { userName: account.userName, password: account.password },
169 | familyId,
170 | personalCount,
171 | familyCount,
172 | );
173 |
174 | reports.push(result.report);
175 |
176 | if (result.success) {
177 | totalFamily += result.familyTotal;
178 | totalActualFamily += result.actualFamily;
179 | }
180 |
181 | if (accounts.length > 1 && index < accounts.length - 1) {
182 | await sleep(5000);
183 | }
184 | }
185 | // 最终容量统计(确保主账号客户端存在)
186 | if (mainAccountClient) {
187 | finalSizeInfo = await mainAccountClient.getUserSizeInfo().catch(() => null);
188 | if (finalSizeInfo) {
189 | logger.debug(`🏠 最终家庭容量: ${finalSizeInfo.familyCapacityInfo.totalSize} Bytes`);
190 | const actualFamilyTotal = (finalSizeInfo.familyCapacityInfo.totalSize - initialSizeInfo.familyCapacityInfo.totalSize) / 1024 / 1024;
191 | var finalMessage = `📈 实际家庭容量总增加: ${actualFamilyTotal.toFixed(2)}MB\n⏱️ 执行耗时: ${benchmark.lap()}`;
192 | }
193 | }
194 |
195 | const finalReport = [
196 | reports.join('\n\n'),
197 | `🏠 所有家庭签到累计获得: ${totalFamily}MB`,
198 | finalMessage || '⚠️ 无法计算实际容量变化',
199 | ].join('\n\n');
200 |
201 | sendNotify('天翼云压力测试报告', finalReport);
202 | logger.debug(`📊 测试结果:\n${finalReport}`);
203 | } catch (e) {
204 | logger.error('致命错误:', e.message);
205 | process.exit(1);
206 | }
207 | })();
208 |
--------------------------------------------------------------------------------
/src/sendNotify.js:
--------------------------------------------------------------------------------
1 | const querystring = require('node:querystring');
2 | const got = require('got');
3 | const crypto = require("crypto");
4 | const timeout = 15000;
5 |
6 | const push_config = {
7 | HITOKOTO: true, // 启用一言(随机句子)
8 |
9 | BARK_PUSH: '', // bark IP 或设备码,例:https://api.day.app/DxHcxxxxxRxxxxxxcm/
10 | BARK_ARCHIVE: '', // bark 推送是否存档
11 | BARK_GROUP: '', // bark 推送分组
12 | BARK_SOUND: '', // bark 推送声音
13 | BARK_ICON: '', // bark 推送图标
14 | BARK_LEVEL: '', // bark 推送时效性
15 | BARK_URL: '', // bark 推送跳转URL
16 |
17 | DD_BOT_SECRET: '', // 钉钉机器人的 DD_BOT_SECRET
18 | DD_BOT_TOKEN: '', // 钉钉机器人的 DD_BOT_TOKEN
19 |
20 | FSKEY: '', // 飞书机器人的 FSKEY
21 |
22 | // 推送到个人QQ:http://127.0.0.1/send_private_msg
23 | // 群:http://127.0.0.1/send_group_msg
24 | GOBOT_URL: '', // go-cqhttp
25 | // 推送到个人QQ 填入 user_id=个人QQ
26 | // 群 填入 group_id=QQ群
27 | GOBOT_QQ: '', // go-cqhttp 的推送群或用户
28 | GOBOT_TOKEN: '', // go-cqhttp 的 access_token
29 |
30 | GOTIFY_URL: '', // gotify地址,如https://push.example.de:8080
31 | GOTIFY_TOKEN: '', // gotify的消息应用token
32 | GOTIFY_PRIORITY: 0, // 推送消息优先级,默认为0
33 |
34 | IGOT_PUSH_KEY: '', // iGot 聚合推送的 IGOT_PUSH_KEY,例如:https://push.hellyw.com/XXXXXXXX
35 |
36 | PUSH_KEY: '', // server 酱的 PUSH_KEY,兼容旧版与 Turbo 版
37 |
38 | DEER_KEY: '', // PushDeer 的 PUSHDEER_KEY
39 | DEER_URL: '', // PushDeer 的 PUSHDEER_URL
40 |
41 | CHAT_URL: '', // synology chat url
42 | CHAT_TOKEN: '', // synology chat token
43 |
44 | // 官方文档:https://www.pushplus.plus/
45 | PUSH_PLUS_TOKEN: '', // pushplus 推送的用户令牌
46 | PUSH_PLUS_USER: '', // pushplus 推送的群组编码
47 | PUSH_PLUS_TEMPLATE: 'html', // pushplus 发送模板,支持html,txt,json,markdown,cloudMonitor,jenkins,route,pay
48 | PUSH_PLUS_CHANNEL: 'wechat', // pushplus 发送渠道,支持wechat,webhook,cp,mail,sms
49 | PUSH_PLUS_WEBHOOK: '', // pushplus webhook编码,可在pushplus公众号上扩展配置出更多渠道
50 | PUSH_PLUS_CALLBACKURL: '', // pushplus 发送结果回调地址,会把推送最终结果通知到这个地址上
51 | PUSH_PLUS_TO: '', // pushplus 好友令牌,微信公众号渠道填写好友令牌,企业微信渠道填写企业微信用户id
52 |
53 | // 微加机器人,官方网站:https://www.weplusbot.com/
54 | WE_PLUS_BOT_TOKEN: '', // 微加机器人的用户令牌
55 | WE_PLUS_BOT_RECEIVER: '', // 微加机器人的消息接收人
56 | WE_PLUS_BOT_VERSION: 'pro', //微加机器人调用版本,pro和personal;为空默认使用pro(专业版),个人版填写:personal
57 |
58 | QMSG_KEY: '', // qmsg 酱的 QMSG_KEY
59 | QMSG_TYPE: '', // qmsg 酱的 QMSG_TYPE
60 |
61 | QYWX_ORIGIN: 'https://qyapi.weixin.qq.com', // 企业微信代理地址
62 |
63 | /*
64 | 此处填你企业微信应用消息的值(详见文档 https://work.weixin.qq.com/api/doc/90000/90135/90236)
65 | 环境变量名 QYWX_AM依次填入 corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型)
66 | 注意用,号隔开(英文输入法的逗号),例如:wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat
67 | 可选推送消息类型(推荐使用图文消息(mpnews)):
68 | - 文本卡片消息: 0 (数字零)
69 | - 文本消息: 1 (数字一)
70 | - 图文消息(mpnews): 素材库图片id, 可查看此教程(http://note.youdao.com/s/HMiudGkb)或者(https://note.youdao.com/ynoteshare1/index.html?id=1a0c8aff284ad28cbd011b29b3ad0191&type=note)
71 | */
72 | QYWX_AM: '', // 企业微信应用
73 |
74 | QYWX_KEY: '', // 企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
75 |
76 | TG_BOT_TOKEN: '', // tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
77 | TG_USER_ID: '', // tg 机器人的 TG_USER_ID,例:1434078534
78 | TG_API_HOST: 'https://api.telegram.org', // tg 代理 api
79 | TG_PROXY_AUTH: '', // tg 代理认证参数
80 | TG_PROXY_HOST: '', // tg 机器人的 TG_PROXY_HOST
81 | TG_PROXY_PORT: '', // tg 机器人的 TG_PROXY_PORT
82 |
83 | AIBOTK_KEY: '', // 智能微秘书 个人中心的apikey 文档地址:http://wechat.aibotk.com/docs/about
84 | AIBOTK_TYPE: '', // 智能微秘书 发送目标 room 或 contact
85 | AIBOTK_NAME: '', // 智能微秘书 发送群名 或者好友昵称和type要对应好
86 |
87 | SMTP_SERVICE: '', // 邮箱服务名称,比如 126、163、Gmail、QQ 等,支持列表 https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json
88 | SMTP_EMAIL: '', // SMTP 收发件邮箱,通知将会由自己发给自己
89 | SMTP_PASSWORD: '', // SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
90 | SMTP_NAME: '', // SMTP 收发件人姓名,可随意填写
91 |
92 | PUSHME_KEY: '', // 官方文档:https://push.i-i.me,PushMe 酱的 PUSHME_KEY
93 |
94 | // CHRONOCAT API https://chronocat.vercel.app/install/docker/official/
95 | CHRONOCAT_QQ: '', // 个人: user_id=个人QQ 群则填入 group_id=QQ群 多个用英文;隔开同时支持个人和群
96 | CHRONOCAT_TOKEN: '', // 填写在CHRONOCAT文件生成的访问密钥
97 | CHRONOCAT_URL: '', // Red 协议连接地址 例: http://127.0.0.1:16530
98 |
99 | WEBHOOK_URL: '', // 自定义通知 请求地址
100 | WEBHOOK_BODY: '', // 自定义通知 请求体
101 | WEBHOOK_HEADERS: '', // 自定义通知 请求头
102 | WEBHOOK_METHOD: '', // 自定义通知 请求方法
103 | WEBHOOK_CONTENT_TYPE: '', // 自定义通知 content-type
104 |
105 | NTFY_URL: '', // ntfy地址,如https://ntfy.sh,默认为https://ntfy.sh
106 | NTFY_TOPIC: '', // ntfy的消息应用topic
107 | NTFY_PRIORITY: '3', // 推送消息优先级,默认为3
108 |
109 | // 官方文档: https://wxpusher.zjiecode.com/docs/
110 | // 管理后台: https://wxpusher.zjiecode.com/admin/
111 | WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken
112 | WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
113 | WXPUSHER_UIDS: '', // wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
114 | };
115 |
116 | for (const key in push_config) {
117 | const v = process.env[key];
118 | if (v) {
119 | push_config[key] = v;
120 | }
121 | }
122 |
123 | const $ = {
124 | post: (params, callback) => {
125 | const { url, ...others } = params;
126 | got.post(url, others).then(
127 | (res) => {
128 | let body = res.body;
129 | try {
130 | body = JSON.parse(body);
131 | } catch (error) {}
132 | callback(null, res, body);
133 | },
134 | (err) => {
135 | callback(err?.response?.body || err);
136 | },
137 | );
138 | },
139 | get: (params, callback) => {
140 | const { url, ...others } = params;
141 | got.get(url, others).then(
142 | (res) => {
143 | let body = res.body;
144 | try {
145 | body = JSON.parse(body);
146 | } catch (error) {}
147 | callback(null, res, body);
148 | },
149 | (err) => {
150 | callback(err?.response?.body || err);
151 | },
152 | );
153 | },
154 | logErr: console.log,
155 | };
156 |
157 | async function one() {
158 | const url = 'https://v1.hitokoto.cn/';
159 | const res = await got.get(url);
160 | const body = JSON.parse(res.body);
161 | return `${body.hitokoto} ----${body.from}`;
162 | }
163 |
164 | function gotifyNotify(text, desp) {
165 | return new Promise((resolve) => {
166 | const { GOTIFY_URL, GOTIFY_TOKEN, GOTIFY_PRIORITY } = push_config;
167 | if (GOTIFY_URL && GOTIFY_TOKEN) {
168 | const options = {
169 | url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
170 | body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(
171 | desp,
172 | )}&priority=${GOTIFY_PRIORITY}`,
173 | headers: {
174 | 'Content-Type': 'application/x-www-form-urlencoded',
175 | },
176 | };
177 | $.post(options, (err, resp, data) => {
178 | try {
179 | if (err) {
180 | console.log('Gotify 发送通知调用API失败😞\n', err);
181 | } else {
182 | if (data.id) {
183 | console.log('Gotify 发送通知消息成功🎉\n');
184 | } else {
185 | console.log(`Gotify 发送通知调用API失败😞 ${data.message}\n`);
186 | }
187 | }
188 | } catch (e) {
189 | $.logErr(e, resp);
190 | } finally {
191 | resolve();
192 | }
193 | });
194 | } else {
195 | resolve();
196 | }
197 | });
198 | }
199 |
200 | function gobotNotify(text, desp) {
201 | return new Promise((resolve) => {
202 | const { GOBOT_URL, GOBOT_TOKEN, GOBOT_QQ } = push_config;
203 | if (GOBOT_URL) {
204 | const options = {
205 | url: `${GOBOT_URL}?access_token=${GOBOT_TOKEN}&${GOBOT_QQ}`,
206 | json: { message: `${text}\n${desp}` },
207 | headers: {
208 | 'Content-Type': 'application/json',
209 | },
210 | timeout,
211 | };
212 | $.post(options, (err, resp, data) => {
213 | try {
214 | if (err) {
215 | console.log('Go-cqhttp 通知调用API失败😞\n', err);
216 | } else {
217 | if (data.retcode === 0) {
218 | console.log('Go-cqhttp 发送通知消息成功🎉\n');
219 | } else if (data.retcode === 100) {
220 | console.log(`Go-cqhttp 发送通知消息异常 ${data.errmsg}\n`);
221 | } else {
222 | console.log(`Go-cqhttp 发送通知消息异常 ${JSON.stringify(data)}`);
223 | }
224 | }
225 | } catch (e) {
226 | $.logErr(e, resp);
227 | } finally {
228 | resolve(data);
229 | }
230 | });
231 | } else {
232 | resolve();
233 | }
234 | });
235 | }
236 |
237 | function serverNotify(text, desp) {
238 | return new Promise((resolve) => {
239 | const { PUSH_KEY } = push_config;
240 | if (PUSH_KEY) {
241 | // 微信server酱推送通知一个\n不会换行,需要两个\n才能换行,故做此替换
242 | desp = desp.replace(/[\n\r]/g, '\n\n');
243 |
244 | const matchResult = PUSH_KEY.match(/^sctp(\d+)t/i);
245 | const options = {
246 | url:
247 | matchResult && matchResult[1]
248 | ? `https://${matchResult[1]}.push.ft07.com/send/${PUSH_KEY}.send`
249 | : `https://sctapi.ftqq.com/${PUSH_KEY}.send`,
250 | body: `text=${encodeURIComponent(text)}&desp=${encodeURIComponent(
251 | desp,
252 | )}`,
253 | headers: {
254 | 'Content-Type': 'application/x-www-form-urlencoded',
255 | },
256 | timeout,
257 | };
258 | $.post(options, (err, resp, data) => {
259 | try {
260 | if (err) {
261 | console.log('Server 酱发送通知调用API失败😞\n', err);
262 | } else {
263 | // server酱和Server酱·Turbo版的返回json格式不太一样
264 | if (data.errno === 0 || data.data.errno === 0) {
265 | console.log('Server 酱发送通知消息成功🎉\n');
266 | } else if (data.errno === 1024) {
267 | // 一分钟内发送相同的内容会触发
268 | console.log(`Server 酱发送通知消息异常 ${data.errmsg}\n`);
269 | } else {
270 | console.log(`Server 酱发送通知消息异常 ${JSON.stringify(data)}`);
271 | }
272 | }
273 | } catch (e) {
274 | $.logErr(e, resp);
275 | } finally {
276 | resolve(data);
277 | }
278 | });
279 | } else {
280 | resolve();
281 | }
282 | });
283 | }
284 |
285 | function pushDeerNotify(text, desp) {
286 | return new Promise((resolve) => {
287 | const { DEER_KEY, DEER_URL } = push_config;
288 | if (DEER_KEY) {
289 | // PushDeer 建议对消息内容进行 urlencode
290 | desp = encodeURI(desp);
291 | const options = {
292 | url: DEER_URL || `https://api2.pushdeer.com/message/push`,
293 | body: `pushkey=${DEER_KEY}&text=${text}&desp=${desp}&type=markdown`,
294 | headers: {
295 | 'Content-Type': 'application/x-www-form-urlencoded',
296 | },
297 | timeout,
298 | };
299 | $.post(options, (err, resp, data) => {
300 | try {
301 | if (err) {
302 | console.log('PushDeer 通知调用API失败😞\n', err);
303 | } else {
304 | // 通过返回的result的长度来判断是否成功
305 | if (
306 | data.content.result.length !== undefined &&
307 | data.content.result.length > 0
308 | ) {
309 | console.log('PushDeer 发送通知消息成功🎉\n');
310 | } else {
311 | console.log(
312 | `PushDeer 发送通知消息异常😞 ${JSON.stringify(data)}`,
313 | );
314 | }
315 | }
316 | } catch (e) {
317 | $.logErr(e, resp);
318 | } finally {
319 | resolve(data);
320 | }
321 | });
322 | } else {
323 | resolve();
324 | }
325 | });
326 | }
327 |
328 | function chatNotify(text, desp) {
329 | return new Promise((resolve) => {
330 | const { CHAT_URL, CHAT_TOKEN } = push_config;
331 | if (CHAT_URL && CHAT_TOKEN) {
332 | // 对消息内容进行 urlencode
333 | desp = encodeURI(desp);
334 | const options = {
335 | url: `${CHAT_URL}${CHAT_TOKEN}`,
336 | body: `payload={"text":"${text}\n${desp}"}`,
337 | headers: {
338 | 'Content-Type': 'application/x-www-form-urlencoded',
339 | },
340 | };
341 | $.post(options, (err, resp, data) => {
342 | try {
343 | if (err) {
344 | console.log('Chat 发送通知调用API失败😞\n', err);
345 | } else {
346 | if (data.success) {
347 | console.log('Chat 发送通知消息成功🎉\n');
348 | } else {
349 | console.log(`Chat 发送通知消息异常 ${JSON.stringify(data)}`);
350 | }
351 | }
352 | } catch (e) {
353 | $.logErr(e);
354 | } finally {
355 | resolve(data);
356 | }
357 | });
358 | } else {
359 | resolve();
360 | }
361 | });
362 | }
363 |
364 | function barkNotify(text, desp, params = {}) {
365 | return new Promise((resolve) => {
366 | let {
367 | BARK_PUSH,
368 | BARK_ICON,
369 | BARK_SOUND,
370 | BARK_GROUP,
371 | BARK_LEVEL,
372 | BARK_ARCHIVE,
373 | BARK_URL,
374 | } = push_config;
375 | if (BARK_PUSH) {
376 | // 兼容BARK本地用户只填写设备码的情况
377 | if (!BARK_PUSH.startsWith('http')) {
378 | BARK_PUSH = `https://api.day.app/${BARK_PUSH}`;
379 | }
380 | const options = {
381 | url: `${BARK_PUSH}`,
382 | json: {
383 | title: text,
384 | body: desp,
385 | icon: BARK_ICON,
386 | sound: BARK_SOUND,
387 | group: BARK_GROUP,
388 | isArchive: BARK_ARCHIVE,
389 | level: BARK_LEVEL,
390 | url: BARK_URL,
391 | ...params,
392 | },
393 | headers: {
394 | 'Content-Type': 'application/json',
395 | },
396 | timeout,
397 | };
398 | $.post(options, (err, resp, data) => {
399 | try {
400 | if (err) {
401 | console.log('Bark APP 发送通知调用API失败😞\n', err);
402 | } else {
403 | if (data.code === 200) {
404 | console.log('Bark APP 发送通知消息成功🎉\n');
405 | } else {
406 | console.log(`Bark APP 发送通知消息异常 ${data.message}\n`);
407 | }
408 | }
409 | } catch (e) {
410 | $.logErr(e, resp);
411 | } finally {
412 | resolve();
413 | }
414 | });
415 | } else {
416 | resolve();
417 | }
418 | });
419 | }
420 |
421 | function tgBotNotify(text, desp) {
422 | return new Promise((resolve) => {
423 | const {
424 | TG_BOT_TOKEN,
425 | TG_USER_ID,
426 | TG_PROXY_HOST,
427 | TG_PROXY_PORT,
428 | TG_API_HOST,
429 | TG_PROXY_AUTH,
430 | } = push_config;
431 | if (TG_BOT_TOKEN && TG_USER_ID) {
432 | let options = {
433 | url: `${TG_API_HOST}/bot${TG_BOT_TOKEN}/sendMessage`,
434 | json: {
435 | chat_id: `${TG_USER_ID}`,
436 | text: `${text}\n\n${desp}`,
437 | disable_web_page_preview: true,
438 | },
439 | headers: {
440 | 'Content-Type': 'application/json',
441 | },
442 | timeout,
443 | };
444 | if (TG_PROXY_HOST && TG_PROXY_PORT) {
445 | const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
446 | const _options = {
447 | keepAlive: true,
448 | keepAliveMsecs: 1000,
449 | maxSockets: 256,
450 | maxFreeSockets: 256,
451 | proxy: `http://${TG_PROXY_AUTH}${TG_PROXY_HOST}:${TG_PROXY_PORT}`,
452 | };
453 | const httpAgent = new HttpProxyAgent(_options);
454 | const httpsAgent = new HttpsProxyAgent(_options);
455 | const agent = {
456 | http: httpAgent,
457 | https: httpsAgent,
458 | };
459 | options.agent = agent;
460 | }
461 | $.post(options, (err, resp, data) => {
462 | try {
463 | if (err) {
464 | console.log('Telegram 发送通知消息失败😞\n', err);
465 | } else {
466 | if (data.ok) {
467 | console.log('Telegram 发送通知消息成功🎉。\n');
468 | } else if (data.error_code === 400) {
469 | console.log(
470 | '请主动给bot发送一条消息并检查接收用户ID是否正确。\n',
471 | );
472 | } else if (data.error_code === 401) {
473 | console.log('Telegram bot token 填写错误。\n');
474 | }
475 | }
476 | } catch (e) {
477 | $.logErr(e, resp);
478 | } finally {
479 | resolve(data);
480 | }
481 | });
482 | } else {
483 | resolve();
484 | }
485 | });
486 | }
487 | function ddBotNotify(text, desp) {
488 | return new Promise((resolve) => {
489 | const { DD_BOT_TOKEN, DD_BOT_SECRET } = push_config;
490 | const options = {
491 | url: `https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}`,
492 | json: {
493 | msgtype: 'text',
494 | text: {
495 | content: `${text}\n\n${desp}`,
496 | },
497 | },
498 | headers: {
499 | 'Content-Type': 'application/json',
500 | },
501 | timeout,
502 | };
503 | if (DD_BOT_TOKEN && DD_BOT_SECRET) {
504 | const crypto = require('crypto');
505 | const dateNow = Date.now();
506 | const hmac = crypto.createHmac('sha256', DD_BOT_SECRET);
507 | hmac.update(`${dateNow}\n${DD_BOT_SECRET}`);
508 | const result = encodeURIComponent(hmac.digest('base64'));
509 | options.url = `${options.url}×tamp=${dateNow}&sign=${result}`;
510 | $.post(options, (err, resp, data) => {
511 | try {
512 | if (err) {
513 | console.log('钉钉发送通知消息失败😞\n', err);
514 | } else {
515 | if (data.errcode === 0) {
516 | console.log('钉钉发送通知消息成功🎉\n');
517 | } else {
518 | console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
519 | }
520 | }
521 | } catch (e) {
522 | $.logErr(e, resp);
523 | } finally {
524 | resolve(data);
525 | }
526 | });
527 | } else if (DD_BOT_TOKEN) {
528 | $.post(options, (err, resp, data) => {
529 | try {
530 | if (err) {
531 | console.log('钉钉发送通知消息失败😞\n', err);
532 | } else {
533 | if (data.errcode === 0) {
534 | console.log('钉钉发送通知消息成功🎉\n');
535 | } else {
536 | console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
537 | }
538 | }
539 | } catch (e) {
540 | $.logErr(e, resp);
541 | } finally {
542 | resolve(data);
543 | }
544 | });
545 | } else {
546 | resolve();
547 | }
548 | });
549 | }
550 |
551 | function qywxBotNotify(text, desp) {
552 | return new Promise((resolve) => {
553 | const { QYWX_ORIGIN, QYWX_KEY } = push_config;
554 | const options = {
555 | url: `${QYWX_ORIGIN}/cgi-bin/webhook/send?key=${QYWX_KEY}`,
556 | json: {
557 | msgtype: 'text',
558 | text: {
559 | content: `${text}\n\n${desp}`,
560 | },
561 | },
562 | headers: {
563 | 'Content-Type': 'application/json',
564 | },
565 | timeout,
566 | };
567 | if (QYWX_KEY) {
568 | $.post(options, (err, resp, data) => {
569 | try {
570 | if (err) {
571 | console.log('企业微信发送通知消息失败😞\n', err);
572 | } else {
573 | if (data.errcode === 0) {
574 | console.log('企业微信发送通知消息成功🎉。\n');
575 | } else {
576 | console.log(`企业微信发送通知消息异常 ${data.errmsg}\n`);
577 | }
578 | }
579 | } catch (e) {
580 | $.logErr(e, resp);
581 | } finally {
582 | resolve(data);
583 | }
584 | });
585 | } else {
586 | resolve();
587 | }
588 | });
589 | }
590 |
591 | function ChangeUserId(desp) {
592 | const { QYWX_AM } = push_config;
593 | const QYWX_AM_AY = QYWX_AM.split(',');
594 | if (QYWX_AM_AY[2]) {
595 | const userIdTmp = QYWX_AM_AY[2].split('|');
596 | let userId = '';
597 | for (let i = 0; i < userIdTmp.length; i++) {
598 | const count = '账号' + (i + 1);
599 | const count2 = '签到号 ' + (i + 1);
600 | if (desp.match(count2)) {
601 | userId = userIdTmp[i];
602 | }
603 | }
604 | if (!userId) userId = QYWX_AM_AY[2];
605 | return userId;
606 | } else {
607 | return '@all';
608 | }
609 | }
610 |
611 | async function qywxamNotify(text, desp) {
612 | const MAX_LENGTH = 900;
613 | if (desp.length > MAX_LENGTH) {
614 | let d = desp.substr(0, MAX_LENGTH) + '\n==More==';
615 | await do_qywxamNotify(text, d);
616 | await qywxamNotify(text, desp.substr(MAX_LENGTH));
617 | } else {
618 | return await do_qywxamNotify(text, desp);
619 | }
620 | }
621 |
622 | function do_qywxamNotify(text, desp) {
623 | return new Promise((resolve) => {
624 | const { QYWX_AM, QYWX_ORIGIN } = push_config;
625 | if (QYWX_AM) {
626 | const QYWX_AM_AY = QYWX_AM.split(',');
627 | const options_accesstoken = {
628 | url: `${QYWX_ORIGIN}/cgi-bin/gettoken`,
629 | json: {
630 | corpid: `${QYWX_AM_AY[0]}`,
631 | corpsecret: `${QYWX_AM_AY[1]}`,
632 | },
633 | headers: {
634 | 'Content-Type': 'application/json',
635 | },
636 | timeout,
637 | };
638 | $.post(options_accesstoken, (err, resp, json) => {
639 | let html = desp.replace(/\n/g, '
');
640 | let accesstoken = json.access_token;
641 | let options;
642 |
643 | switch (QYWX_AM_AY[4]) {
644 | case '0':
645 | options = {
646 | msgtype: 'textcard',
647 | textcard: {
648 | title: `${text}`,
649 | description: `${desp}`,
650 | url: 'https://github.com/whyour/qinglong',
651 | btntxt: '更多',
652 | },
653 | };
654 | break;
655 |
656 | case '1':
657 | options = {
658 | msgtype: 'text',
659 | text: {
660 | content: `${text}\n\n${desp}`,
661 | },
662 | };
663 | break;
664 |
665 | default:
666 | options = {
667 | msgtype: 'mpnews',
668 | mpnews: {
669 | articles: [
670 | {
671 | title: `${text}`,
672 | thumb_media_id: `${QYWX_AM_AY[4]}`,
673 | author: `智能助手`,
674 | content_source_url: ``,
675 | content: `${html}`,
676 | digest: `${desp}`,
677 | },
678 | ],
679 | },
680 | };
681 | }
682 | if (!QYWX_AM_AY[4]) {
683 | // 如不提供第四个参数,则默认进行文本消息类型推送
684 | options = {
685 | msgtype: 'text',
686 | text: {
687 | content: `${text}\n\n${desp}`,
688 | },
689 | };
690 | }
691 | options = {
692 | url: `${QYWX_ORIGIN}/cgi-bin/message/send?access_token=${accesstoken}`,
693 | json: {
694 | touser: `${ChangeUserId(desp)}`,
695 | agentid: `${QYWX_AM_AY[3]}`,
696 | safe: '0',
697 | ...options,
698 | },
699 | headers: {
700 | 'Content-Type': 'application/json',
701 | },
702 | };
703 |
704 | $.post(options, (err, resp, data) => {
705 | try {
706 | if (err) {
707 | console.log(
708 | '成员ID:' +
709 | ChangeUserId(desp) +
710 | '企业微信应用消息发送通知消息失败😞\n',
711 | err,
712 | );
713 | } else {
714 | if (data.errcode === 0) {
715 | console.log(
716 | '成员ID:' +
717 | ChangeUserId(desp) +
718 | '企业微信应用消息发送通知消息成功🎉。\n',
719 | );
720 | } else {
721 | console.log(
722 | `企业微信应用消息发送通知消息异常 ${data.errmsg}\n`,
723 | );
724 | }
725 | }
726 | } catch (e) {
727 | $.logErr(e, resp);
728 | } finally {
729 | resolve(data);
730 | }
731 | });
732 | });
733 | } else {
734 | resolve();
735 | }
736 | });
737 | }
738 |
739 | function iGotNotify(text, desp, params = {}) {
740 | return new Promise((resolve) => {
741 | const { IGOT_PUSH_KEY } = push_config;
742 | if (IGOT_PUSH_KEY) {
743 | // 校验传入的IGOT_PUSH_KEY是否有效
744 | const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$');
745 | if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) {
746 | console.log('您所提供的 IGOT_PUSH_KEY 无效\n');
747 | resolve();
748 | return;
749 | }
750 | const options = {
751 | url: `https://push.hellyw.com/${IGOT_PUSH_KEY.toLowerCase()}`,
752 | body: `title=${text}&content=${desp}&${querystring.stringify(params)}`,
753 | headers: {
754 | 'Content-Type': 'application/x-www-form-urlencoded',
755 | },
756 | timeout,
757 | };
758 | $.post(options, (err, resp, data) => {
759 | try {
760 | if (err) {
761 | console.log('IGot 发送通知调用API失败😞\n', err);
762 | } else {
763 | if (data.ret === 0) {
764 | console.log('IGot 发送通知消息成功🎉\n');
765 | } else {
766 | console.log(`IGot 发送通知消息异常 ${data.errMsg}\n`);
767 | }
768 | }
769 | } catch (e) {
770 | $.logErr(e, resp);
771 | } finally {
772 | resolve(data);
773 | }
774 | });
775 | } else {
776 | resolve();
777 | }
778 | });
779 | }
780 |
781 | function pushPlusNotify(text, desp) {
782 | return new Promise((resolve) => {
783 | const {
784 | PUSH_PLUS_TOKEN,
785 | PUSH_PLUS_USER,
786 | PUSH_PLUS_TEMPLATE,
787 | PUSH_PLUS_CHANNEL,
788 | PUSH_PLUS_WEBHOOK,
789 | PUSH_PLUS_CALLBACKURL,
790 | PUSH_PLUS_TO,
791 | } = push_config;
792 | if (PUSH_PLUS_TOKEN) {
793 | desp = desp.replace(/[\n\r]/g, '
'); // 默认为html, 不支持plaintext
794 | const body = {
795 | token: `${PUSH_PLUS_TOKEN}`,
796 | title: `${text}`,
797 | content: `${desp}`,
798 | topic: `${PUSH_PLUS_USER}`,
799 | template: `${PUSH_PLUS_TEMPLATE}`,
800 | channel: `${PUSH_PLUS_CHANNEL}`,
801 | webhook: `${PUSH_PLUS_WEBHOOK}`,
802 | callbackUrl: `${PUSH_PLUS_CALLBACKURL}`,
803 | to: `${PUSH_PLUS_TO}`,
804 | };
805 | const options = {
806 | url: `https://www.pushplus.plus/send`,
807 | body: JSON.stringify(body),
808 | headers: {
809 | 'Content-Type': ' application/json',
810 | },
811 | timeout,
812 | };
813 | $.post(options, (err, resp, data) => {
814 | try {
815 | if (err) {
816 | console.log(
817 | `pushplus 发送${
818 | PUSH_PLUS_USER ? '一对多' : '一对一'
819 | }通知消息失败😞\n`,
820 | err,
821 | );
822 | } else {
823 | if (data.code === 200) {
824 | console.log(
825 | `pushplus 发送${
826 | PUSH_PLUS_USER ? '一对多' : '一对一'
827 | }通知请求成功🎉,可根据流水号查询推送结果:${
828 | data.data
829 | }\n注意:请求成功并不代表推送成功,如未收到消息,请到pushplus官网使用流水号查询推送最终结果`,
830 | );
831 | } else {
832 | console.log(
833 | `pushplus 发送${
834 | PUSH_PLUS_USER ? '一对多' : '一对一'
835 | }通知消息异常 ${data.msg}\n`,
836 | );
837 | }
838 | }
839 | } catch (e) {
840 | $.logErr(e, resp);
841 | } finally {
842 | resolve(data);
843 | }
844 | });
845 | } else {
846 | resolve();
847 | }
848 | });
849 | }
850 |
851 | function wePlusBotNotify(text, desp) {
852 | return new Promise((resolve) => {
853 | const { WE_PLUS_BOT_TOKEN, WE_PLUS_BOT_RECEIVER, WE_PLUS_BOT_VERSION } =
854 | push_config;
855 | if (WE_PLUS_BOT_TOKEN) {
856 | let template = 'txt';
857 | if (desp.length > 800) {
858 | desp = desp.replace(/[\n\r]/g, '
');
859 | template = 'html';
860 | }
861 | const body = {
862 | token: `${WE_PLUS_BOT_TOKEN}`,
863 | title: `${text}`,
864 | content: `${desp}`,
865 | template: `${template}`,
866 | receiver: `${WE_PLUS_BOT_RECEIVER}`,
867 | version: `${WE_PLUS_BOT_VERSION}`,
868 | };
869 | const options = {
870 | url: `https://www.weplusbot.com/send`,
871 | body: JSON.stringify(body),
872 | headers: {
873 | 'Content-Type': ' application/json',
874 | },
875 | timeout,
876 | };
877 | $.post(options, (err, resp, data) => {
878 | try {
879 | if (err) {
880 | console.log(`微加机器人发送通知消息失败😞\n`, err);
881 | } else {
882 | if (data.code === 200) {
883 | console.log(`微加机器人发送通知消息完成🎉\n`);
884 | } else {
885 | console.log(`微加机器人发送通知消息异常 ${data.msg}\n`);
886 | }
887 | }
888 | } catch (e) {
889 | $.logErr(e, resp);
890 | } finally {
891 | resolve(data);
892 | }
893 | });
894 | } else {
895 | resolve();
896 | }
897 | });
898 | }
899 |
900 | function aibotkNotify(text, desp) {
901 | return new Promise((resolve) => {
902 | const { AIBOTK_KEY, AIBOTK_TYPE, AIBOTK_NAME } = push_config;
903 | if (AIBOTK_KEY && AIBOTK_TYPE && AIBOTK_NAME) {
904 | let json = {};
905 | let url = '';
906 | switch (AIBOTK_TYPE) {
907 | case 'room':
908 | url = 'https://api-bot.aibotk.com/openapi/v1/chat/room';
909 | json = {
910 | apiKey: `${AIBOTK_KEY}`,
911 | roomName: `${AIBOTK_NAME}`,
912 | message: {
913 | type: 1,
914 | content: `【青龙快讯】\n\n${text}\n${desp}`,
915 | },
916 | };
917 | break;
918 | case 'contact':
919 | url = 'https://api-bot.aibotk.com/openapi/v1/chat/contact';
920 | json = {
921 | apiKey: `${AIBOTK_KEY}`,
922 | name: `${AIBOTK_NAME}`,
923 | message: {
924 | type: 1,
925 | content: `【青龙快讯】\n\n${text}\n${desp}`,
926 | },
927 | };
928 | break;
929 | }
930 | const options = {
931 | url: url,
932 | json,
933 | headers: {
934 | 'Content-Type': 'application/json',
935 | },
936 | timeout,
937 | };
938 | $.post(options, (err, resp, data) => {
939 | try {
940 | if (err) {
941 | console.log('智能微秘书发送通知消息失败😞\n', err);
942 | } else {
943 | if (data.code === 0) {
944 | console.log('智能微秘书发送通知消息成功🎉。\n');
945 | } else {
946 | console.log(`智能微秘书发送通知消息异常 ${data.error}\n`);
947 | }
948 | }
949 | } catch (e) {
950 | $.logErr(e, resp);
951 | } finally {
952 | resolve(data);
953 | }
954 | });
955 | } else {
956 | resolve();
957 | }
958 | });
959 | }
960 |
961 | function fsBotNotify(text, desp) {
962 | return new Promise((resolve) => {
963 | const { FSKEY } = push_config;
964 | if (FSKEY) {
965 | const options = {
966 | url: `https://open.feishu.cn/open-apis/bot/v2/hook/${FSKEY}`,
967 | json: { msg_type: 'text', content: { text: `${text}\n\n${desp}` } },
968 | headers: {
969 | 'Content-Type': 'application/json',
970 | },
971 | timeout,
972 | };
973 | $.post(options, (err, resp, data) => {
974 | try {
975 | if (err) {
976 | console.log('飞书发送通知调用API失败😞\n', err);
977 | } else {
978 | if (data.StatusCode === 0 || data.code === 0) {
979 | console.log('飞书发送通知消息成功🎉\n');
980 | } else {
981 | console.log(`飞书发送通知消息异常 ${data.msg}\n`);
982 | }
983 | }
984 | } catch (e) {
985 | $.logErr(e, resp);
986 | } finally {
987 | resolve(data);
988 | }
989 | });
990 | } else {
991 | resolve();
992 | }
993 | });
994 | }
995 |
996 | async function smtpNotify(text, desp) {
997 | const { SMTP_EMAIL, SMTP_PASSWORD, SMTP_SERVICE, SMTP_NAME } = push_config;
998 | if (![SMTP_EMAIL, SMTP_PASSWORD].every(Boolean) || !SMTP_SERVICE) {
999 | return;
1000 | }
1001 |
1002 | try {
1003 | const nodemailer = require('nodemailer');
1004 | const transporter = nodemailer.createTransport({
1005 | service: SMTP_SERVICE,
1006 | auth: {
1007 | user: SMTP_EMAIL,
1008 | pass: SMTP_PASSWORD,
1009 | },
1010 | });
1011 |
1012 | const addr = SMTP_NAME ? `"${SMTP_NAME}" <${SMTP_EMAIL}>` : SMTP_EMAIL;
1013 | const info = await transporter.sendMail({
1014 | from: addr,
1015 | to: addr,
1016 | subject: text,
1017 | html: `${desp.replace(/\n/g, '
')}`,
1018 | });
1019 |
1020 | transporter.close();
1021 |
1022 | if (info.messageId) {
1023 | console.log('SMTP 发送通知消息成功🎉\n');
1024 | return true;
1025 | }
1026 | console.log('SMTP 发送通知消息失败😞\n');
1027 | } catch (e) {
1028 | console.log('SMTP 发送通知消息出现异常😞\n', e);
1029 | }
1030 | }
1031 |
1032 | function pushMeNotify(text, desp, params = {}) {
1033 | return new Promise((resolve) => {
1034 | const { PUSHME_KEY, PUSHME_URL } = push_config;
1035 | if (PUSHME_KEY) {
1036 | const options = {
1037 | url: PUSHME_URL || 'https://push.i-i.me',
1038 | json: { push_key: PUSHME_KEY, title: text, content: desp, ...params },
1039 | headers: {
1040 | 'Content-Type': 'application/json',
1041 | },
1042 | timeout,
1043 | };
1044 | $.post(options, (err, resp, data) => {
1045 | try {
1046 | if (err) {
1047 | console.log('PushMe 发送通知调用API失败😞\n', err);
1048 | } else {
1049 | if (data === 'success') {
1050 | console.log('PushMe 发送通知消息成功🎉\n');
1051 | } else {
1052 | console.log(`PushMe 发送通知消息异常 ${data}\n`);
1053 | }
1054 | }
1055 | } catch (e) {
1056 | $.logErr(e, resp);
1057 | } finally {
1058 | resolve(data);
1059 | }
1060 | });
1061 | } else {
1062 | resolve();
1063 | }
1064 | });
1065 | }
1066 |
1067 | function chronocatNotify(title, desp) {
1068 | return new Promise((resolve) => {
1069 | const { CHRONOCAT_TOKEN, CHRONOCAT_QQ, CHRONOCAT_URL } = push_config;
1070 | if (!CHRONOCAT_TOKEN || !CHRONOCAT_QQ || !CHRONOCAT_URL) {
1071 | resolve();
1072 | return;
1073 | }
1074 |
1075 | const user_ids = CHRONOCAT_QQ.match(/user_id=(\d+)/g)?.map(
1076 | (match) => match.split('=')[1],
1077 | );
1078 | const group_ids = CHRONOCAT_QQ.match(/group_id=(\d+)/g)?.map(
1079 | (match) => match.split('=')[1],
1080 | );
1081 |
1082 | const url = `${CHRONOCAT_URL}/api/message/send`;
1083 | const headers = {
1084 | 'Content-Type': 'application/json',
1085 | Authorization: `Bearer ${CHRONOCAT_TOKEN}`,
1086 | };
1087 |
1088 | for (const [chat_type, ids] of [
1089 | [1, user_ids],
1090 | [2, group_ids],
1091 | ]) {
1092 | if (!ids) {
1093 | continue;
1094 | }
1095 | for (const chat_id of ids) {
1096 | const data = {
1097 | peer: {
1098 | chatType: chat_type,
1099 | peerUin: chat_id,
1100 | },
1101 | elements: [
1102 | {
1103 | elementType: 1,
1104 | textElement: {
1105 | content: `${title}\n\n${desp}`,
1106 | },
1107 | },
1108 | ],
1109 | };
1110 | const options = {
1111 | url: url,
1112 | json: data,
1113 | headers,
1114 | timeout,
1115 | };
1116 | $.post(options, (err, resp, data) => {
1117 | try {
1118 | if (err) {
1119 | console.log('Chronocat 发送QQ通知消息失败😞\n', err);
1120 | } else {
1121 | if (chat_type === 1) {
1122 | console.log(`Chronocat 个人消息 ${ids}推送成功🎉`);
1123 | } else {
1124 | console.log(`Chronocat 群消息 ${ids}推送成功🎉`);
1125 | }
1126 | }
1127 | } catch (e) {
1128 | $.logErr(e, resp);
1129 | } finally {
1130 | resolve(data);
1131 | }
1132 | });
1133 | }
1134 | }
1135 | });
1136 | }
1137 |
1138 | function qmsgNotify(text, desp) {
1139 | return new Promise((resolve) => {
1140 | const { QMSG_KEY, QMSG_TYPE } = push_config;
1141 | if (QMSG_KEY && QMSG_TYPE) {
1142 | const options = {
1143 | url: `https://qmsg.zendee.cn/${QMSG_TYPE}/${QMSG_KEY}`,
1144 | body: `msg=${text}\n\n${desp.replace('----', '-')}`,
1145 | headers: {
1146 | 'Content-Type': 'application/x-www-form-urlencoded',
1147 | },
1148 | timeout,
1149 | };
1150 | $.post(options, (err, resp, data) => {
1151 | try {
1152 | if (err) {
1153 | console.log('Qmsg 发送通知调用API失败😞\n', err);
1154 | } else {
1155 | if (data.code === 0) {
1156 | console.log('Qmsg 发送通知消息成功🎉\n');
1157 | } else {
1158 | console.log(`Qmsg 发送通知消息异常 ${data}\n`);
1159 | }
1160 | }
1161 | } catch (e) {
1162 | $.logErr(e, resp);
1163 | } finally {
1164 | resolve(data);
1165 | }
1166 | });
1167 | } else {
1168 | resolve();
1169 | }
1170 | });
1171 | }
1172 |
1173 | function webhookNotify(text, desp) {
1174 | return new Promise((resolve) => {
1175 | const {
1176 | WEBHOOK_URL,
1177 | WEBHOOK_BODY,
1178 | WEBHOOK_HEADERS,
1179 | WEBHOOK_CONTENT_TYPE,
1180 | WEBHOOK_METHOD,
1181 | } = push_config;
1182 | if (
1183 | !WEBHOOK_METHOD ||
1184 | !WEBHOOK_URL ||
1185 | (!WEBHOOK_URL.includes('$title') && !WEBHOOK_BODY.includes('$title'))
1186 | ) {
1187 | resolve();
1188 | return;
1189 | }
1190 |
1191 | const headers = parseHeaders(WEBHOOK_HEADERS);
1192 | const body = parseBody(WEBHOOK_BODY, WEBHOOK_CONTENT_TYPE, (v) =>
1193 | v
1194 | ?.replaceAll('$title', text?.replaceAll('\n', '\\n'))
1195 | ?.replaceAll('$content', desp?.replaceAll('\n', '\\n')),
1196 | );
1197 | const bodyParam = formatBodyFun(WEBHOOK_CONTENT_TYPE, body);
1198 | const options = {
1199 | method: WEBHOOK_METHOD,
1200 | headers,
1201 | allowGetBody: true,
1202 | ...bodyParam,
1203 | timeout,
1204 | retry: 1,
1205 | };
1206 |
1207 | const formatUrl = WEBHOOK_URL.replaceAll(
1208 | '$title',
1209 | encodeURIComponent(text),
1210 | ).replaceAll('$content', encodeURIComponent(desp));
1211 | got(formatUrl, options).then((resp) => {
1212 | try {
1213 | if (resp.statusCode !== 200) {
1214 | console.log(`自定义发送通知消息失败😞 ${resp.body}\n`);
1215 | } else {
1216 | console.log(`自定义发送通知消息成功🎉 ${resp.body}\n`);
1217 | }
1218 | } catch (e) {
1219 | $.logErr(e, resp);
1220 | } finally {
1221 | resolve(resp.body);
1222 | }
1223 | });
1224 | });
1225 | }
1226 |
1227 | function ntfyNotify(text, desp) {
1228 | function encodeRFC2047(text) {
1229 | const encodedBase64 = Buffer.from(text).toString('base64');
1230 | return `=?utf-8?B?${encodedBase64}?=`;
1231 | }
1232 |
1233 | return new Promise((resolve) => {
1234 | const { NTFY_URL, NTFY_TOPIC, NTFY_PRIORITY } = push_config;
1235 | if (NTFY_TOPIC) {
1236 | const options = {
1237 | url: `${NTFY_URL || 'https://ntfy.sh'}/${NTFY_TOPIC}`,
1238 | body: `${desp}`,
1239 | headers: {
1240 | Title: `${encodeRFC2047(text)}`,
1241 | Priority: NTFY_PRIORITY || '3',
1242 | },
1243 | timeout,
1244 | };
1245 | $.post(options, (err, resp, data) => {
1246 | try {
1247 | if (err) {
1248 | console.log('Ntfy 通知调用API失败😞\n', err);
1249 | } else {
1250 | if (data.id) {
1251 | console.log('Ntfy 发送通知消息成功🎉\n');
1252 | } else {
1253 | console.log(`Ntfy 发送通知消息异常 ${JSON.stringify(data)}`);
1254 | }
1255 | }
1256 | } catch (e) {
1257 | $.logErr(e, resp);
1258 | } finally {
1259 | resolve(data);
1260 | }
1261 | });
1262 | } else {
1263 | resolve();
1264 | }
1265 | });
1266 | }
1267 |
1268 | function wxPusherNotify(text, desp) {
1269 | return new Promise((resolve) => {
1270 | const { WXPUSHER_APP_TOKEN, WXPUSHER_TOPIC_IDS, WXPUSHER_UIDS } =
1271 | push_config;
1272 | if (WXPUSHER_APP_TOKEN) {
1273 | // 处理topic_ids,将分号分隔的字符串转为数组
1274 | const topicIds = WXPUSHER_TOPIC_IDS
1275 | ? WXPUSHER_TOPIC_IDS.split(';')
1276 | .map((id) => id.trim())
1277 | .filter((id) => id)
1278 | .map((id) => parseInt(id))
1279 | : [];
1280 |
1281 | // 处理uids,将分号分隔的字符串转为数组
1282 | const uids = WXPUSHER_UIDS
1283 | ? WXPUSHER_UIDS.split(';')
1284 | .map((uid) => uid.trim())
1285 | .filter((uid) => uid)
1286 | : [];
1287 |
1288 | // topic_ids uids 至少有一个
1289 | if (!topicIds.length && !uids.length) {
1290 | console.log(
1291 | 'wxpusher 服务的 WXPUSHER_TOPIC_IDS 和 WXPUSHER_UIDS 至少设置一个!!',
1292 | );
1293 | return resolve();
1294 | }
1295 |
1296 | const body = {
1297 | appToken: WXPUSHER_APP_TOKEN,
1298 | content: `