diff --git a/.env.example b/.env.example index 4e961a4e..c09d375c 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ MONGODB_URI='mongodb://' YIYAN_MONGODB_URI='' +CRON_PORT=5500 PORT=5600 LOG_LEVEL='debug' diff --git a/README.md b/README.md index 65d0c66f..938b850a 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ python和javaScript的定时任务管理面板 ## 多谢 -* [https://github.com/nevinee/jd_shell](https://github.com/nevinee/jd_shell) +* [nevinee](https://gitee.com/evine) -* [https://github.com/alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui) +* [crontab-ui](https://github.com/alseambusher/crontab-ui) * [Ant Design](https://ant.design) diff --git a/back/api/config.ts b/back/api/config.ts index e900ff75..c52bc637 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -26,10 +26,6 @@ export default (app: Router) => { case 'crontab': content = getFileContentByName(config.crontabFile); break; - case 'shareCode': - let shareCodeFile = getLastModifyFilePath(config.shareCodeDir); - content = getFileContentByName(shareCodeFile); - break; case 'extra': content = getFileContentByName(config.extraFile); break; diff --git a/back/api/cron.ts b/back/api/cron.ts index 2f6811ef..702ebbeb 100644 --- a/back/api/cron.ts +++ b/back/api/cron.ts @@ -1,10 +1,9 @@ import { Router, Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; import { Logger } from 'winston'; -import * as fs from 'fs'; -import config from '../config'; import CronService from '../services/cron'; import { celebrate, Joi } from 'celebrate'; +import cron_parser from 'cron-parser'; const route = Router(); export default (app: Router) => { @@ -32,14 +31,36 @@ export default (app: Router) => { body: Joi.object({ command: Joi.string().required(), schedule: Joi.string().required(), - name: Joi.string(), + name: Joi.string().optional(), }), }), + async (req: Request, res: Response, next: NextFunction) => { + const logger: Logger = Container.get('logger'); + try { + if (cron_parser.parseExpression(req.body.schedule).hasNext()) { + const cronService = Container.get(CronService); + const data = await cronService.create(req.body); + return res.send({ code: 200, data }); + } else { + return res.send({ code: 400, message: 'param schedule error' }); + } + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); + } + }, + ); + + route.put( + '/crons/run', + celebrate({ + body: Joi.array().items(Joi.string().required()), + }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.create(req.body); + const data = await cronService.run(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -48,18 +69,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/run', + route.put( + '/crons/stop', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.run(req.params.id); + const data = await cronService.stop(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -68,18 +87,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/disable', + route.put( + '/crons/disable', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.disabled(req.params.id); + const data = await cronService.disabled(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -88,18 +105,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/enable', + route.put( + '/crons/enable', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.enabled(req.params.id); + const data = await cronService.enabled(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -134,16 +149,20 @@ export default (app: Router) => { body: Joi.object({ command: Joi.string().required(), schedule: Joi.string().required(), - name: Joi.string(), + name: Joi.string().optional(), _id: Joi.string().required(), }), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cronService = Container.get(CronService); - const data = await cronService.update(req.body); - return res.send({ code: 200, data }); + if (cron_parser.parseExpression(req.body.schedule).hasNext()) { + const cronService = Container.get(CronService); + const data = await cronService.update(req.body); + return res.send({ code: 200, data }); + } else { + return res.send({ code: 400, message: 'param schedule error' }); + } } catch (e) { logger.error('🔥 error: %o', e); return next(e); @@ -152,17 +171,15 @@ export default (app: Router) => { ); route.delete( - '/crons/:id', + '/crons', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.remove(req.params.id); + const data = await cronService.remove(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); diff --git a/back/config/index.ts b/back/config/index.ts index 39781407..17c55ac4 100644 --- a/back/config/index.ts +++ b/back/config/index.ts @@ -7,11 +7,10 @@ const envFound = dotenv.config(); const rootPath = path.resolve(__dirname, '../../'); const cookieFile = path.join(rootPath, 'config/cookie.sh'); const confFile = path.join(rootPath, 'config/config.sh'); -const sampleFile = path.join(rootPath, 'sample/config.sh.sample'); +const sampleFile = path.join(rootPath, 'sample/config.sample.sh'); const crontabFile = path.join(rootPath, 'config/crontab.list'); const confBakDir = path.join(rootPath, 'config/bak/'); const authConfigFile = path.join(rootPath, 'config/auth.json'); -const shareCodeDir = path.join(rootPath, 'log/export_sharecodes/'); const extraFile = path.join(rootPath, 'config/extra.sh'); const logPath = path.join(rootPath, 'log/'); const authError = '错误的用户名密码,请重试'; @@ -28,6 +27,7 @@ if (envFound.error) { export default { port: parseInt(process.env.PORT as string, 10), + cronPort: parseInt(process.env.CRON_PORT as string, 10), secret: process.env.SECRET, logs: { level: process.env.LOG_LEVEL || 'silly', @@ -40,7 +40,6 @@ export default { authError, logPath, extraFile, - shareCodeDir, authConfigFile, confBakDir, crontabFile, diff --git a/back/data/cron.ts b/back/data/cron.ts index 9c6e96ad..39d7f1b0 100644 --- a/back/data/cron.ts +++ b/back/data/cron.ts @@ -8,6 +8,7 @@ export class Crontab { _id?: string; status?: CrontabStatus; isSystem?: 1 | 0; + pid?: number; constructor(options: Crontab) { this.name = options.name; @@ -19,6 +20,7 @@ export class Crontab { this.status = options.status || CrontabStatus.idle; this.timestamp = new Date().toString(); this.isSystem = options.isSystem || 0; + this.pid = options.pid; } } diff --git a/back/loaders/express.ts b/back/loaders/express.ts index 6856fd2b..35b16192 100644 --- a/back/loaders/express.ts +++ b/back/loaders/express.ts @@ -49,7 +49,10 @@ export default ({ app }: { app: Application }) => { next: NextFunction, ) => { if (err.name === 'UnauthorizedError') { - return res.status(err.status).send({ message: err.message }).end(); + return res + .status(err.status) + .send({ code: 401, message: err.message }) + .end(); } return next(err); }, @@ -64,6 +67,7 @@ export default ({ app }: { app: Application }) => { ) => { res.status(err.status || 500); res.json({ + code: err.status || 500, message: err.message, }); }, diff --git a/back/loaders/initData.ts b/back/loaders/initData.ts index 7a39bc0c..0753cccc 100644 --- a/back/loaders/initData.ts +++ b/back/loaders/initData.ts @@ -5,10 +5,7 @@ import CronService from '../services/cron'; const initData = [ { name: '更新面板', - command: `sleep ${randomSchedule( - 60, - 1, - )} && git_pull >> $QL_DIR/log/git_pull.log 2>&1`, + command: `sleep ${randomSchedule(60, 1)} && ql update`, schedule: `${randomSchedule(60, 1)} ${randomSchedule( 24, 7, @@ -16,46 +13,22 @@ const initData = [ status: CrontabStatus.idle, }, { - name: 'build面板', - command: 'rebuild >> ${QL_DIR}/log/rebuild.log 2>&1', + name: '重启并编译面板', + command: 'ql restart', schedule: '30 7 */7 * *', status: CrontabStatus.disabled, }, - { - name: '自定义仓库', - command: `sleep ${randomSchedule( - 60, - 1, - )} && diy https://ghproxy.com/https://github.com/whyour/hundun.git "quanx/jx|quanx/jd" tokens >> $QL_DIR/log/diy_pull.log 2>&1`, - schedule: `${randomSchedule(60, 1)} ${randomSchedule( - 24, - 6, - ).toString()} * * *`, - status: CrontabStatus.idle, - }, - { - name: '互助码导出', - command: 'export_sharecodes', - schedule: '48 5 * * *', - status: CrontabStatus.idle, - }, { name: '删除日志', - command: 'rm_log >/dev/null 2>&1', + command: 'ql rmlog 7', schedule: '30 7 */7 * *', - status: CrontabStatus.disabled, + status: CrontabStatus.idle, }, { - name: '重置密码', - command: 'js resetpwd', - schedule: '33 6 */7 * *', - status: CrontabStatus.disabled, - }, - { - name: '运行所有脚本(慎用)', - command: 'js runall', - schedule: '33 6 */7 * *', - status: CrontabStatus.disabled, + name: '互助码', + command: 'ql code', + schedule: '30 7 * * *', + status: CrontabStatus.idle, }, ]; diff --git a/back/schedule.ts b/back/schedule.ts new file mode 100644 index 00000000..960b2488 --- /dev/null +++ b/back/schedule.ts @@ -0,0 +1,59 @@ +import schedule from 'node-schedule'; +import express from 'express'; +import { exec } from 'child_process'; +import Logger from './loaders/logger'; +import { Container } from 'typedi'; +import CronService from './services/cron'; +import { CrontabStatus } from './data/cron'; +import config from './config'; + +const app = express(); + +const run = async () => { + const cronService = Container.get(CronService); + const cronDb = cronService.getDb(); + + cronDb + .find({}) + .sort({ created: 1 }) + .exec((err, docs) => { + if (err) { + Logger.error(err); + process.exit(1); + } + + if (docs && docs.length > 0) { + for (let i = 0; i < docs.length; i++) { + const task = docs[i]; + const _schedule = task.schedule && task.schedule.split(' '); + if ( + _schedule && + _schedule.length > 5 && + task.status !== CrontabStatus.disabled + ) { + schedule.scheduleJob(task.schedule, function () { + let command = task.command as string; + if (!command.includes('task ') && !command.includes('ql ')) { + command = `task ${command}`; + } + exec(command); + }); + } + } + } + }); +}; + +app + .listen(config.cronPort, () => { + run(); + Logger.info(` + ################################################ + 🛡️ Schedule listening on port: ${config.cronPort} 🛡️ + ################################################ + `); + }) + .on('error', (err) => { + Logger.error(err); + process.exit(1); + }); diff --git a/back/services/cookie.ts b/back/services/cookie.ts index 76a596ba..af1469f1 100644 --- a/back/services/cookie.ts +++ b/back/services/cookie.ts @@ -121,6 +121,16 @@ export default class CookieService { }); } + private async formatCookies(cookies: Cookie[]) { + const result = []; + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i]; + const { status, nickname } = await this.getJdInfo(cookie.value); + result.push({ ...cookie, status, nickname }); + } + return result; + } + public async create(payload: string[]): Promise { const cookies = await this.cookies(''); let position = initCookiePosition; @@ -135,7 +145,7 @@ export default class CookieService { }); const docs = await this.insert(tabs); await this.set_cookies(); - return docs; + return await this.formatCookies(docs); } public async insert(payload: Cookie[]): Promise { @@ -156,20 +166,21 @@ export default class CookieService { const tab = new Cookie({ ...doc, ...other }); const newDoc = await this.updateDb(tab); await this.set_cookies(); - return newDoc; + const [newCookie] = await this.formatCookies([newDoc]); + return newCookie; } - public async updateDb(payload: Cookie): Promise { + private async updateDb(payload: Cookie): Promise { return new Promise((resolve) => { this.cronDb.update( { _id: payload._id }, payload, { returnUpdatedDocs: true }, - (err, docs) => { + (err, num, doc) => { if (err) { this.logger.error(err); } else { - resolve(docs as Cookie); + resolve(doc as Cookie); } }, ); @@ -228,6 +239,11 @@ export default class CookieService { ], }; } + const newDocs = await this.find(query, sort); + return await this.formatCookies(newDocs); + } + + private async find(query: any, sort: any): Promise { return new Promise((resolve) => { this.cronDb .find(query) diff --git a/back/services/cron.ts b/back/services/cron.ts index da084bab..d9ac9003 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -22,12 +22,20 @@ export default class CronService { return this.cronDb; } + private isSixCron(cron: Crontab) { + const { schedule } = cron; + if (schedule.split(' ').length === 6) { + return true; + } + return false; + } + public async create(payload: Crontab): Promise { const tab = new Crontab(payload); tab.created = new Date().valueOf(); tab.saved = false; const doc = await this.insert(tab); - await this.set_crontab(); + await this.set_crontab(this.isSixCron(doc)); return doc; } @@ -74,9 +82,9 @@ export default class CronService { this.cronDb.update({ _id }, { $set: { stopped, saved: false } }); } - public async remove(_id: string) { - this.cronDb.remove({ _id }, {}); - await this.set_crontab(); + public async remove(ids: string[]) { + this.cronDb.remove({ _id: { $in: ids } }, { multi: true }); + await this.set_crontab(true); } public async crontabs(searchText?: string): Promise { @@ -112,68 +120,98 @@ export default class CronService { }); } - public async run(_id: string) { - this.cronDb.find({ _id }).exec((err, docs: Crontab[]) => { - let res = docs[0]; - - this.logger.silly('Running job'); - this.logger.silly('ID: ' + _id); - this.logger.silly('Original command: ' + res.command); - - let logFile = `${config.manualLogPath}${res._id}.log`; - fs.writeFileSync(logFile, `开始执行...\n\n${new Date().toString()}\n`); - - let cmdStr = res.command; - if (res.command.startsWith('js') && !res.command.endsWith('now')) { - cmdStr = `${res.command} now`; - } else if (/&& (.*) >>/.test(res.command)) { - cmdStr = res.command.match(/&& (.*) >>/)[1]; + public async run(ids: string[]) { + this.cronDb.find({ _id: { $in: ids } }).exec((err, docs: Crontab[]) => { + for (let i = 0; i < docs.length; i++) { + const doc = docs[i]; + this.runSingle(doc); } - const cmd = spawn(cmdStr, { shell: true }); - - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.running } }); - - cmd.stdout.on('data', (data) => { - this.logger.silly(`stdout: ${data}`); - fs.appendFileSync(logFile, data); - }); - - cmd.stderr.on('data', (data) => { - this.logger.error(`stderr: ${data}`); - fs.appendFileSync(logFile, data); - }); - - cmd.on('close', (code) => { - this.logger.silly(`child process exited with code ${code}`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - }); - - cmd.on('error', (err) => { - this.logger.silly(err); - fs.appendFileSync(logFile, err.stack); - }); - - cmd.on('exit', (code: number, signal: any) => { - this.logger.silly(`cmd exit ${code}`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - fs.appendFileSync(logFile, `\n\n执行结束...`); - }); - - cmd.on('disconnect', () => { - this.logger.silly(`cmd disconnect`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - fs.appendFileSync(logFile, `\n\n连接断开...`); - }); }); } - public async disabled(_id: string) { - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.disabled } }); + public async stop(ids: string[]) { + this.cronDb.find({ _id: { $in: ids } }).exec((err, docs: Crontab[]) => { + for (let i = 0; i < docs.length; i++) { + const doc = docs[i]; + if (doc.pid) { + exec(`kill -9 ${doc.pid}`); + } + } + }); + } + + private async runSingle(cron: Crontab) { + let { _id, command } = cron; + + this.logger.silly('Running job'); + this.logger.silly('ID: ' + _id); + this.logger.silly('Original command: ' + command); + + let logFile = `${config.manualLogPath}${_id}.log`; + fs.writeFileSync(logFile, `开始执行...\n\n${new Date().toString()}\n`); + + let cmdStr = command; + if (!cmdStr.includes('task ') && !cmdStr.includes('ql ')) { + cmdStr = `task ${cmdStr}`; + } + if (cmdStr.endsWith('.js')) { + cmdStr = `${cmdStr} now`; + } + const cmd = spawn(cmdStr, { shell: true }); + + this.cronDb.update( + { _id }, + { $set: { status: CrontabStatus.running, pid: cmd.pid } }, + ); + + cmd.stdout.on('data', (data) => { + this.logger.silly(`stdout: ${data}`); + fs.appendFileSync(logFile, data); + }); + + cmd.stderr.on('data', (data) => { + this.logger.error(`stderr: ${data}`); + fs.appendFileSync(logFile, data); + }); + + cmd.on('close', (code) => { + this.logger.silly(`child process exited with code ${code}`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + }); + + cmd.on('error', (err) => { + this.logger.silly(err); + fs.appendFileSync(logFile, err.stack); + }); + + cmd.on('exit', (code: number, signal: any) => { + this.logger.silly(`cmd exit ${code}`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + fs.appendFileSync(logFile, `\n\n执行结束...`); + }); + + cmd.on('disconnect', () => { + this.logger.silly(`cmd disconnect`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + fs.appendFileSync(logFile, `\n\n连接断开...`); + }); + } + + public async disabled(ids: string[]) { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: CrontabStatus.disabled } }, + { multi: true }, + ); await this.set_crontab(); } - public async enabled(_id: string) { - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + public async enabled(ids: string[]) { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: CrontabStatus.idle } }, + { multi: true }, + ); } public async log(_id: string) { @@ -186,11 +224,12 @@ export default class CronService { return crontab_job_string; } - private async set_crontab() { + private async set_crontab(needReloadSchedule: boolean = false) { const tabs = await this.crontabs(); var crontab_string = ''; tabs.forEach((tab) => { - if (tab.status === CrontabStatus.disabled) { + const _schedule = tab.schedule && tab.schedule.split(' '); + if (tab.status === CrontabStatus.disabled || _schedule.length !== 5) { crontab_string += '# '; crontab_string += tab.schedule; crontab_string += ' '; @@ -208,6 +247,9 @@ export default class CronService { fs.writeFileSync(config.crontabFile, crontab_string); execSync(`crontab ${config.crontabFile}`); + if (needReloadSchedule) { + exec(`pm2 reload schedule`); + } this.cronDb.update({}, { $set: { saved: true } }, { multi: true }); } @@ -226,11 +268,11 @@ export default class CronService { var command = line.replace(regex, '').trim(); var schedule = line.replace(command, '').trim(); - var is_valid = false; - try { - is_valid = cron_parser.parseString(line).expressions.length > 0; - } catch (e) {} - if (command && schedule && is_valid) { + if ( + command && + schedule && + cron_parser.parseExpression(schedule).hasNext() + ) { var name = namePrefix + '_' + index; this.cronDb.findOne({ command, schedule }, (err, doc) => { diff --git a/docker/Dockerfile b/docker/Dockerfile index 57954725..6df86183 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ FROM node:lts-alpine LABEL maintainer="whyour" -ARG QL_BASE_URL=https://github.com.cnpmjs.org/whyour/qinglong -ARG QL_BASE_BRANCH=master +ARG QL_URL=https://ghproxy.com/https://github.com/whyour/qinglong.git +ARG QL_BRANCH=master ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ LANG=zh_CN.UTF-8 \ SHELL=/bin/bash \ @@ -27,17 +27,9 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && echo "Asia/Shanghai" > /etc/timezone \ && touch ~/.bashrc \ && mkdir /run/nginx \ - && git clone -b ${QL_BASE_BRANCH} ${QL_BASE_URL} ${QL_DIR} \ + && git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ && cd ${QL_DIR} \ && cp -f .env.example .env \ - && ln -sf ${QL_DIR}/shell/js.sh /usr/local/bin/js \ - && ln -sf ${QL_DIR}/shell/git_pull.sh /usr/local/bin/git_pull \ - && ln -sf ${QL_DIR}/shell/rm_log.sh /usr/local/bin/rm_log \ - && ln -sf ${QL_DIR}/shell/export_sharecodes.sh /usr/local/bin/export_sharecodes \ - && ln -sf ${QL_DIR}/shell/git_diy.sh /usr/local/bin/diy \ - && ln -sf ${QL_DIR}/shell/notify.sh /usr/local/bin/notify \ - && ln -sf ${QL_DIR}/shell/rebuild.sh /usr/local/bin/rebuild \ - && ln -sf ${QL_DIR}/docker/docker-entrypoint.sh /usr/local/bin/entrypoint \ && chmod 777 ${QL_DIR}/shell/*.sh \ && chmod 777 ${QL_DIR}/docker/*.sh \ && npm install -g pm2 \ @@ -47,4 +39,4 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && yarn build-back \ && yarn install --production --network-timeout 100000 \ && yarn cache clean -ENTRYPOINT ["entrypoint"] \ No newline at end of file +ENTRYPOINT ["./docker/docker-entrypoint.sh"] \ No newline at end of file diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 59118709..93989ec7 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -1,64 +1,37 @@ #!/bin/bash set -e -echo -e "======================1. 检测配置文件========================\n" -[ ! -d ${QL_DIR}/config ] && mkdir -p ${QL_DIR}/config -[ ! -d ${QL_DIR}/log ] && mkdir -p ${QL_DIR}/log -[ ! -d ${QL_DIR}/db ] && mkdir -p ${QL_DIR}/db -[ ! -d ${QL_DIR}/manual_log ] && mkdir -p ${QL_DIR}/manual_log - -if [ ! -s ${QL_DIR}/config/crontab.list ] -then - echo -e "检测到config配置目录下不存在crontab.list或存在但文件为空,从示例文件复制一份用于初始化...\n" - cp -fv ${QL_DIR}/sample/crontab.list.sample ${QL_DIR}/config/crontab.list - sed -i "s,MY_PATH,${QL_DIR},g" ${QL_DIR}/config/crontab.list - sed -i "s,ENV_PATH=,PATH=$PATH,g" ${QL_DIR}/config/crontab.list -fi -crond -crontab ${QL_DIR}/config/crontab.list -echo -e "成功添加定时任务...\n" - -if [ ! -s ${QL_DIR}/config/cookie.sh ]; then - echo -e "检测到config配置目录下不存在cookie.sh,从示例文件复制一份用于初始化...\n" - touch ${QL_DIR}/config/cookie.sh - echo -fi - -if [ ! -s ${QL_DIR}/config/config.sh ]; then - echo -e "检测到config配置目录下不存在config.sh,从示例文件复制一份用于初始化...\n" - cp -fv ${QL_DIR}/sample/config.sh.sample ${QL_DIR}/config/config.sh - echo -fi - -if [ ! -s ${QL_DIR}/config/auth.json ]; then - echo -e "检测到config配置目录下不存在auth.json,从示例文件复制一份用于初始化...\n" - cp -fv ${QL_DIR}/sample/auth.json ${QL_DIR}/config/auth.json - echo -fi - -if [ -s /etc/nginx/conf.d/default.conf ]; then - echo -e "检测到默认nginx配置文件,删除...\n" - rm -f /etc/nginx/conf.d/default.conf - echo -fi - -cp -fv ${QL_DIR}/docker/front.conf /etc/nginx/conf.d/front.conf - -echo -e "======================2. 启动nginx========================\n" -nginx -c /etc/nginx/nginx.conf +dir_shell=/ql/shell +. $dir_shell/share.sh +link_shell +echo -e "======================1. 更新源代码========================\n" +ql update echo -echo -e "======================3. 启动控制面板========================\n" -pm2 start ${QL_DIR}/build/app.js -n panel +echo -e "======================2. 检测配置文件========================\n" +fix_config +cp -fv $dir_root/docker/front.conf /etc/nginx/conf.d/front.conf +echo + +echo -e "======================3. 启动nginx========================\n" +nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf +echo -e "nginx启动成功...\n" + +echo -e "======================4. 启动控制面板========================\n" +cd $dir_root +pm2 reload panel 2>/dev/null || pm2 start $dir_root/build/app.js -n panel echo -e "控制面板启动成功...\n" -echo -e "\n容器启动成功...\n" -echo -e "\n请先访问5700端口,登录面板成功之后先手动执行一次git_pull命令...\n" -echo -e "\n如果需要启动挂机程序手动执行docker exec -it qinglong js hangup...\n" -echo -e "\n或者去cron管理搜索hangup手动执行挂机任务...\n" +echo -e "======================5. 启动定时任务========================\n" +cd $dir_root +pm2 reload schedule 2>/dev/null || pm2 start $dir_root/build/schedule.js -n schedule +echo -e "定时任务启动成功...\n" -if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ]; then - set -- node "$@" -fi +echo -e "############################################################\n" +echo -e "容器启动成功..." +echo -e "\n请先访问5700端口,登录成功面板之后再执行添加定时任务..." +echo -e "############################################################\n" + +crond -f exec "$@" \ No newline at end of file diff --git a/package.json b/package.json index 400d144a..076acce4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "build": "umi build", "build-back": "tsc -p tsconfig.back.json", "start-back": "nodemon", - "pm2": "npm run build-back && node build/app.js", + "panel": "npm run build-back && node build/app.js", + "schedule": "npm run build-back && node build/schedule.js", "prepare": "umi generate tmp", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "test": "umi-test", @@ -26,7 +27,7 @@ "body-parser": "^1.19.0", "celebrate": "^13.0.3", "cors": "^2.8.5", - "cron-parser": "^3.3.0", + "cron-parser": "^3.5.0", "dotenv": "^8.2.0", "express": "^4.17.1", "express-jwt": "^6.0.0", @@ -34,22 +35,13 @@ "jsonwebtoken": "^8.5.1", "nedb": "^1.8.0", "node-fetch": "^2.6.1", + "node-schedule": "^2.0.0", "reflect-metadata": "^0.1.13", "typedi": "^0.8.0", "winston": "^3.3.3" }, "devDependencies": { - "umi": "^3.3.9", - "umi-request": "^1.3.5", - "react-codemirror2": "^7.2.1", - "react-diff-viewer": "^3.1.1", - "react-dnd": "^14.0.2", - "react-dnd-html5-backend": "^14.0.0", - "qrcode.react": "^1.0.1", - "darkreader": "^4.9.27", - "codemirror": "^5.59.4", "@ant-design/pro-layout": "^6.5.0", - "@umijs/plugin-antd": "^0.9.1", "@types/cors": "^2.8.10", "@types/express": "^4.17.8", "@types/express-jwt": "^6.0.1", @@ -60,15 +52,25 @@ "@types/qrcode.react": "^1.0.1", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", + "@umijs/plugin-antd": "^0.9.1", "@umijs/test": "^3.3.9", + "codemirror": "^5.59.4", "compression-webpack-plugin": "6.1.1", + "darkreader": "^4.9.27", "lint-staged": "^10.0.7", "nodemon": "^2.0.4", "prettier": "^2.2.0", + "qrcode.react": "^1.0.1", "react": "17.x", + "react-codemirror2": "^7.2.1", + "react-diff-viewer": "^3.1.1", + "react-dnd": "^14.0.2", + "react-dnd-html5-backend": "^14.0.0", "react-dom": "17.x", "ts-node": "^9.0.0", "typescript": "^4.1.2", + "umi": "^3.3.9", + "umi-request": "^1.3.5", "webpack": "^5.28.0", "yorkie": "^2.0.0" } diff --git a/sample/auth.json b/sample/auth.sample.json similarity index 100% rename from sample/auth.json rename to sample/auth.sample.json diff --git a/sample/config.sample.sh b/sample/config.sample.sh new file mode 100644 index 00000000..2ff6aff5 --- /dev/null +++ b/sample/config.sample.sh @@ -0,0 +1,117 @@ +## Version: v2.0.0 +## Date: 2021-05-10 +## Update Content: 青龙v2 + +## 上面版本号中,如果第2位数字有变化,那么代表增加了新的参数,如果只有第3位数字有变化,仅代表更新了注释,没有增加新的参数,可更新可不更新 + +## 在运行 ql repo 命令时,是否自动删除失效的脚本与定时任务 +AutoDelCron="true" + +## 在运行 ql repo 命令时,是否自动增加新的本地定时任务 +AutoAddCron="true" + +## 设置定时任务执行的超时时间,默认1h,后缀"s"代表秒(默认值), "m"代表分, "h"代表小时, "d"代表天 +CommandTimeoutTime="1h" + +## 在运行 task 命令时,随机延迟启动任务的最大延迟时间 +## 如果任务不是必须准点运行的任务,那么给它增加一个随机延迟,由你定义最大延迟时间,单位为秒,如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行 +## 在crontab.list中,在每小时第0-2分、第30-31分、第59分这几个时间内启动的任务,均算作必须准点运行的任务,在启动这些任务时,即使你定义了RandomDelay,也将准点运行,不启用随机延迟 +## 在crontab.list中,除掉每小时上述时间启动的任务外,其他任务在你定义了 RandomDelay 的情况下,一律启用随机延迟,但如果你给某些任务添加了 "now" 或者 "conc",那么这些任务也将无视随机延迟直接启动 +RandomDelay="300" + +## 如果你自己会写shell脚本,并且希望在每次运行 ql update 命令时,额外运行你的 shell 脚本,请赋值为 "true",默认为true +EnableExtraShell="true" + +## 自动按顺序进行账号间互助(选填) 设置为 true 时,将直接导入code最新日志来进行互助 +AutoHelpOther="" + +## 定义 jcode 脚本导出的互助码模板样式(选填) +## 不填 使用“按编号顺序助力模板”,Cookie编号在前的优先助力 +## 填 0 使用“全部一致助力模板”,所有账户要助力的码全部一致 +## 填 1 使用“均等机会助力模板”,所有账户获得助力次数一致 +## 填 2 使用“随机顺序助力模板”,本套脚本内账号间随机顺序助力,每次生成的顺序都不一致。 +HelpType="" + + +## 需组合的环境变量列表,env_name需要和var_name一一对应,如何有新活动按照格式添加(不懂勿动) +env_name=( + JD_COOKIE + FRUITSHARECODES + PETSHARECODES + PLANT_BEAN_SHARECODES + DREAM_FACTORY_SHARE_CODES + DDFACTORY_SHARECODES + JDZZ_SHARECODES + JDJOY_SHARECODES + JXNC_SHARECODES + BOOKSHOP_SHARECODES + JD_CASH_SHARECODES + JDSGMH_SHARECODES + JDCFD_SHARECODES + JDHEALTH_SHARECODES +) +var_name=( + Cookie + ForOtherFruit + ForOtherPet + ForOtherBean + ForOtherDreamFactory + ForOtherJdFactory + ForOtherJdzz + ForOtherJoy + ForOtherJxnc + ForOtherBookShop + ForOtherCash + ForOtherSgmh + ForOtherCfd + ForOtherHealth +) + +## name_js为脚本文件名,如果使用ql repo命令拉取,文件名含有作者名 +## 所有有互助码的活动,把脚本名称列在 name_js 中,对应 config.sh 中互助码后缀列在 name_config 中,中文名称列在 name_chinese 中。 +## name_js、name_config 和 name_chinese 中的三个名称必须一一对应。 +name_js=( + jd_fruit + jd_pet + jd_plantBean + jd_dreamFactory + jd_jdfactory + jd_jdzz + jd_crazy_joy + jd_jxnc + jd_bookshop + jd_cash + jd_sgmh + jd_cfd + jd_health +) +name_config=( + Fruit + Pet + Bean + DreamFactory + JdFactory + Jdzz + Joy + Jxnc + BookShop + Cash + Sgmh + Cfd + Health +) +name_chinese=( + 东东农场 + 东东萌宠 + 京东种豆得豆 + 京喜工厂 + 东东工厂 + 京东赚赚 + crazyJoy任务 + 京喜农场 + 口袋书店 + 签到领现金 + 闪购盲盒 + 京喜财富岛 + 东东健康社区 +) diff --git a/sample/config.sh.sample b/sample/config.sh.sample deleted file mode 100644 index 9a7d0172..00000000 --- a/sample/config.sh.sample +++ /dev/null @@ -1,599 +0,0 @@ -## Version: v3.30.6 -## Date: 2021-3-8 -## Update Content: 京小兑控制变量,宠汪汪自己内部账户互助变量,环球挑战赛互助变量。 - -## 上面版本号中,如果第2位数字有变化,那么代表增加了新的参数,如果只有第3位数字有变化,仅代表更新了注释,没有增加新的参数,可更新可不更新 - -################################## 说明 ################################## -## 以下配置中,带有 export 申明的,均由LXK9301大佬定义,详见:https://gitee.com/lxk0301/jd_scripts/blob/master/githubAction.md -## 其他互助码,考虑到直接按LXK9301大佬定义的变量去填的话,一是不方便记忆,二是容易搞混,所以最终的变量将由脚本去组合,你只要按注释去填即可 -## 除此之外,还额外增加了是否自动删除失效任务AutoDelCron、是否自动增加新任务AutoAddCron、删除旧日志时间RmLogDaysAgo、随机延迟启动任务RandomDelay、是否添加自定义脚本EnableExtraShell五个人性化的设置供选择 -## 所有赋值等号两边不能有空格,所有的值请一律在两侧添加半角的双引号,如果变量值中含有双引号,则外侧改为一对半角的单引号。 -## 所有的赋值都可以参考 “定义东东萌宠是否静默运行” 部分,在不同时间设置不同的值,以达到你想要的效果,具体判断条件如下: -## $(date "+%d") 当前的日期,如:13 -## $(date "+%w") 当前是星期几,如:3 -## $(date "+%H") 当前的小时数,如:23 -## $(date "+%M") 当前的分钟数,如:49 -## 其他date命令的更多用法,可以在命令行中输入 date --help 查看 -## 判断条件 -eq -ne -gt -ge -lt -le ,具体含义可百度一下 - - -################################## 临时屏蔽某个Cookie(选填) ################################## -## 如果某些Cookie已经失效了,但暂时还没法更新,可以使用此功能在不删除该Cookie和重新修改Cookie编号的前提下,临时屏蔽掉某些编号的Cookie -## 多个Cookie编号以半角的空格分隔,两侧一对半角双引号,使用此功能后,在运行js脚本时账户编号将发生变化 -## 举例1:TempBlockCookie="2" 临时屏蔽掉Cookie2 -## 举例2:TempBlockCookie="2 4" 临时屏蔽掉Cookie2和Cookie4 -## 如果只是想要屏蔽某个账号不玩某些小游戏,可以参考下面 case 这个命令的例子来控制,脚本名称请去掉后缀 “.js” -## case $1 in -## jd_fruit) -## TempBlockCookie="5" # 账号5不玩东东农场 -## ;; -## jd_dreamFactory | jd_jdfactory) -## TempBlockCookie="2" # 账号2不玩京喜工厂和东东工厂 -## ;; -## jd_jdzz | jd_joy) -## TempBlockCookie="3 6" # 账号3、账号6不玩京东赚赚和宠汪汪 -## ;; -## esac -TempBlockCookie="" - - -################################## 定义是否自动删除失效的脚本与定时任务(选填) ################################## -## 有的时候,某些JS脚本只在特定的时间有效,过了时间就失效了,需要自动删除失效的本地定时任务,则设置为 "true" ,否则请设置为 "false" -## 检测文件:LXK9301/jd_scripts 仓库中的 docker/crontab_list.sh,和 shylocks/Loon 仓库中的 docker/crontab_list.sh -## 当设置为 "true" 时,会自动从检测文件中读取比对删除的任务(识别以“jd_”、“jr_”、“jx_”开头的任务) -## 当设置为 "true" 时,脚本只会删除一整行失效的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的crontab.list -## 当设置为 "true" 时,如果你有添加额外脚本是以“jd_”“jr_”“jx_”开头的,如检测文件中,会被删除,不是以“jd_”“jr_”“jx_”开头的任务则不受影响 -AutoDelCron="true" - - -################################## 定义是否自动增加新的本地定时任务(选填) ################################## -## LXK9301 大佬会在有需要的时候,增加定时任务,如需要本地自动增加新的定时任务,则设置为 "true" ,否则请设置为 "false" -## 检测文件:LXK9301/jd_scripts 仓库中的 docker/crontab_list.sh,和 shylocks/Loon 仓库中的 docker/crontab_list.sh -## 当设置为 "true" 时,如果检测到检测文件中有增加新的定时任务,那么在本地也增加(识别以“jd_”、“jr_”、“jx_”开头的任务) -## 当设置为 "true" 时,会自动从检测文件新增加的任务中读取时间,该时间为北京时间 -## 当设置为 "true" 时,脚本只会增加新的定时任务,不会修改其他现有任务,所以任何时候,你都可以自己调整你的crontab.list -AutoAddCron="true" - - -################################## 定义删除日志的时间(选填) ################################## -## 定义在运行删除旧的日志任务时,要删除多少天以前的日志,请输入正整数,不填则禁用删除日志的功能 -RmLogDaysAgo="7" - - -################################## 定义随机延迟启动任务(选填) ################################## -## 如果任务不是必须准点运行的任务,那么给它增加一个随机延迟,由你定义最大延迟时间,单位为秒,如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行 -## 在crontab.list中,在每小时第0-2分、第30-31分、第59分这几个时间内启动的任务,均算作必须准点运行的任务,在启动这些任务时,即使你定义了RandomDelay,也将准点运行,不启用随机延迟 -## 在crontab.list中,除掉每小时上述时间启动的任务外,其他任务在你定义了 RandomDelay 的情况下,一律启用随机延迟,但如果你按照Wiki教程给某些任务添加了 "now",那么这些任务也将无视随机延迟直接启动 -RandomDelay="300" - - -################################## 定义User-Agent(选填) ################################## -## 自定义LXK9301大佬仓库里京东系列js脚本的User-Agent,不懂不知不会User-Agent的请不要随意填写内容,随意填写了出错概不负责 -## 如需使用,请自行解除下一行注释 -# export JD_USER_AGENT="" - - -################################## 定义通知TOKEN(选填) ################################## -## 想通过什么渠道收取通知,就填入对应渠道的值 -## 1. ServerChan,教程:http://sc.ftqq.com/3.version -export PUSH_KEY="" - -## 2. BARK,教程(看BARK_PUSH和BARK_SOUND的说明):https://gitee.com/lxk0301/jd_scripts/blob/master/githubAction.md -export BARK_PUSH="" -export BARK_SOUND="" - -## 3. Telegram,如需使用,TG_BOT_TOKEN和TG_USER_ID必须同时赋值,教程:https://gitee.com/lxk0301/jd_scripts/blob/master/backUp/TG_PUSH.md -export TG_BOT_TOKEN="" -export TG_USER_ID="" - -## 4. 钉钉,教程(看DD_BOT_TOKEN和DD_BOT_SECRET部分):https://gitee.com/lxk0301/jd_scripts/blob/master/githubAction.md -export DD_BOT_TOKEN="" -export DD_BOT_SECRET="" - -## 5. iGot聚合推送,支持多方式推送,填写iGot的推送key。教程:https://gitee.com/lxk0301/jd_scripts/blob/master/githubAction.md -export IGOT_PUSH_KEY="" - -## 6. Push Plus,微信扫码登录后一对一推送或一对多推送,参考文档:http://pushplus.hxtrip.com/ -## 其中PUSH_PLUS_USER是一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码)注:(1、需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送),只填PUSH_PLUS_TOKEN默认为一对一推送 -export PUSH_PLUS_TOKEN="" -export PUSH_PLUS_USER="" - -## 7. 企业微信机器人消息推送 webhook 后面的 key,文档:https://work.weixin.qq.com/api/doc/90000/90136/91770 -export QYWX_KEY="" - -## 8. 企业微信应用消息推送的值,教程:https://note.youdao.com/ynoteshare1/index.html?id=351e08a72378206f9dd64d2281e9b83b&type=note -## 依次填上corpid的值,corpsecret的值,touser的值,agentid,media_id的值,注意用,号隔开,例如:"wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat" -export QYWX_AM="" - - -################################## 定义每日签到的通知形式(选填) ################################## -## js脚本每日签到提供3种通知方式,分别为: -## 关闭通知,那么请在下方填入0 -## 简洁通知,那么请在下方填入1,其效果见:https://github.com/LXK9301/jd_scripts/blob/master/icon/bean_sign_simple.jpg -## 原始通知,那么请在下方填入2,如果不填也默认为2,内容比较长,这也是默认通知方式 -NotifyBeanSign="" - - -################################## 定义每日签到每个接口间的延迟时间(选填) ################################## -## 默认每个签到接口并发无延迟,如需要依次进行每个接口,请自定义延迟时间,单位为毫秒,延迟作用于每个签到接口, 如填入延迟则切换顺序签到(耗时较长) -export JD_BEAN_STOP="" - - -################################## 定义东东农场互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyFruit1="" -MyFruit2="" -MyFruit3="" -MyFruit4="" -MyFruit5="" -MyFruit6="" -MyFruitA="" -MyFruitB="" - -ForOtherFruit1="" -ForOtherFruit2="" -ForOtherFruit3="" -ForOtherFruit4="" -ForOtherFruit5="" -ForOtherFruit6="" - - -################################## 定义东东萌宠互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyPet1="" -MyPet2="" -MyPet3="" -MyPet4="" -MyPet5="" -MyPet6="" -MyPetA="" -MyPetB="" - -ForOtherPet1="" -ForOtherPet2="" -ForOtherPet3="" -ForOtherPet4="" -ForOtherPet5="" -ForOtherPet6="" - - -################################## 定义种豆得豆互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyBean1="" -MyBean2="" -MyBean3="" -MyBean4="" -MyBean5="" -MyBean6="" -MyBeanA="" -MyBeanB="" - -ForOtherBean1="" -ForOtherBean2="" -ForOtherBean3="" -ForOtherBean4="" -ForOtherBean5="" -ForOtherBean6="" - - -################################## 定义京喜工厂互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyDreamFactory1="" -MyDreamFactory2="" -MyDreamFactory3="" -MyDreamFactory4="" -MyDreamFactory5="" -MyDreamFactory6="" -MyDreamFactoryA="" -MyDreamFactoryB="" - -ForOtherDreamFactory1="" -ForOtherDreamFactory2="" -ForOtherDreamFactory3="" -ForOtherDreamFactory4="" -ForOtherDreamFactory5="" -ForOtherDreamFactory6="" - - -################################## 定义东东工厂互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyJdFactory1="" -MyJdFactory2="" -MyJdFactory3="" -MyJdFactory4="" -MyJdFactory5="" -MyJdFactory6="" -MyJdFactoryA="" -MyJdFactoryB="" - -ForOtherJdFactory1="" -ForOtherJdFactory2="" -ForOtherJdFactory3="" -ForOtherJdFactory4="" -ForOtherJdFactory5="" -ForOtherJdFactory6="" - - -################################## 定义京东赚赚互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyJdzz1="" -MyJdzz2="" -MyJdzz3="" -MyJdzz4="" -MyJdzz5="" -MyJdzz6="" -MyJdzzA="" -MyJdzzB="" - -ForOtherJdzz1="" -ForOtherJdzz2="" -ForOtherJdzz3="" -ForOtherJdzz4="" -ForOtherJdzz5="" -ForOtherJdzz6="" - - -################################## 定义疯狂的JOY互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyJoy1="" -MyJoy2="" -MyJoy3="" -MyJoy4="" -MyJoy5="" -MyJoy6="" -MyJoyA="" -MyJoyB="" - -ForOtherJoy1="" -ForOtherJoy2="" -ForOtherJoy3="" -ForOtherJoy4="" -ForOtherJoy5="" -ForOtherJoy6="" - - -################################## 定义京喜农场互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -## 京喜农场助力码为 JSON 格式因此使用单引号,json 格式如下 -## {"smp":"22bdadsfaadsfadse8a","active":"jdnc_1_btorange210113_2","joinnum":"1"} -## 助力码获取可以通过 js jd_get_share_code now 命令获取 -## 注意:京喜农场 种植种子发生变化的时候,互助码也会变!! -MyJxnc1='' -MyJxnc2='' -MyJxnc3='' -MyJxnc4='' -MyJxnc5='' -MyJxnc6='' -MyJxncA='' -MyJxncB='' - -ForOtherJxnc1="" -ForOtherJxnc2="" -ForOtherJxnc3="" -ForOtherJxnc4="" -ForOtherJxnc5="" -ForOtherJxnc6="" - - -################################## 定义京喜农场TOKEN(选填) ################################## -## 如果某个Cookie的账号种植的是app种子,则必须填入有效的TOKEN;而种植非app种子则不需要TOKEN -## TOKEN的形式:{"farm_jstoken":"749a90f871adsfads8ffda7bf3b1576760","timestamp":"1610165423873","phoneid":"42c7e3dadfadsfdsaac-18f0e4f4a0cf"} -## 因TOKEN中带有双引号,因此,变量值两侧必须由一对单引号引起来 -## TOKEN如何获取请阅读以下文件的注释:https://github.com/LXK9301/jd_scripts/blob/master/jd_jxnc.js -TokenJxnc1='' -TokenJxnc2='' -TokenJxnc3='' -TokenJxnc4='' -TokenJxnc5='' -TokenJxnc6='' - - -################################## 定义口袋书店互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyBookShop1="" -MyBookShop2="" -MyBookShop3="" -MyBookShop4="" -MyBookShop5="" -MyBookShop6="" -MyBookShopA="" -MyBookShopB="" - -ForOtherBookShop1="" -ForOtherBookShop2="" -ForOtherBookShop3="" -ForOtherBookShop4="" -ForOtherBookShop5="" -ForOtherBookShop6="" - - -################################## 定义签到领现金互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyCash1="" -MyCash2="" -MyCash3="" -MyCash4="" -MyCash5="" -MyCash6="" -MyCashA="" -MyCashB="" - -ForOtherCash1="" -ForOtherCash2="" -ForOtherCash3="" -ForOtherCash4="" -ForOtherCash5="" -ForOtherCash6="" - - -################################## 定义闪购盲盒互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MySgmh1="" -MySgmh2="" -MySgmh3="" -MySgmh4="" -MySgmh5="" -MySgmh6="" -MySgmhA="" -MySgmhB="" - -ForOtherSgmh1="" -ForOtherSgmh2="" -ForOtherSgmh3="" -ForOtherSgmh4="" -ForOtherSgmh5="" -ForOtherSgmh6="" - - -################################## 定义京东环球挑战赛活动互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -## 仅2021年2月22日(含)前有效 -MyGLOBAL1="" -MyGLOBAL2="" -MyGLOBAL3="" -MyGLOBAL4="" -MyGLOBAL5="" -MyGLOBAL6="" -MyGLOBALA="" -MyGLOBALB="" - -ForOtherGLOBAL1="" -ForOtherGLOBAL2="" -ForOtherGLOBAL3="" -ForOtherGLOBAL4="" -ForOtherGLOBAL5="" -ForOtherGLOBAL6="" - -################################## 定义惊喜财富岛互助(选填) ################################## -## 具体填法及要求详见本文件最下方“互助码填法示例” -MyJdcfd1="" -MyJdcfd2="" -MyJdcfd3="" -MyJdcfd4="" -MyJdcfd5="" -MyJdcfd6="" -MyJdcfdA="" -MyJdcfdB="" - - -ForOtherJdcfd1="" -ForOtherJdcfd2="" -ForOtherJdcfd3="" -ForOtherJdcfd4="" -ForOtherJdcfd5="" -ForOtherJdcfd6="" - -################################## 定义东东超市蓝币兑换数量(选填) ################################## -## 东东超市蓝币兑换,可用值包括: -## 一、0:表示不兑换京豆,这也是js脚本的默认值 -## 二、20:表示兑换20个京豆 -## 三、1000:表示兑换1000个京豆 -## 四、可兑换清单的商品名称,输入能跟唯一识别出来的关键词即可,比如:MARKET_COIN_TO_BEANS="抽纸" -## 注意:有些比较贵的实物商品京东只是展示出来忽悠人的,即使你零点用脚本去抢,也会提示没有或提示已下架 -export MARKET_COIN_TO_BEANS="0" - - -################################## 定义东东超市蓝币成功兑换奖品是否静默运行(选填) ################################## -## 默认 "false" 关闭(即:奖品兑换成功后会发出通知提示),如需要静默运行不发出通知,请改为 "true" -export MARKET_REWARD_NOTIFY="" - - -################################## 定义东东超市是否自动升级商品和货架(选填) ################################## -## 升级顺序:解锁升级商品、升级货架,默认 "true" 自动升级,如需关闭自动升级,请改为 "false" -export SUPERMARKET_UPGRADE="" - - -################################## 定义东东超市是否自动更换商圈(选填) ################################## -## 小于对方300热力值自动更换商圈队伍,默认 "true" 自动更换,如不想更换商圈,请改为 "false" -## 目前已无此功能,申明了也无效 -# export BUSINESS_CIRCLE_JUMP="" - - -################################## 定义东东超市是否自动使用金币去抽奖(选填) ################################## -## 是否用金币去抽奖,默认 "false" 关闭,如需开启,请修改为 "true" -export SUPERMARKET_LOTTERY="" - - -################################## 定义东东超市是否自动参加PK队伍(选填) ################################## -## 是否每次PK活动参加脚本作者创建的PK队伍,"true" 表示参加,"false" 表示不参加,默认为 "true" -export JOIN_PK_TEAM="" - - -################################## 定义东东农场是否静默运行(选填) ################################## -## 默认为 "false",不静默,发送推送通知消息,如不想收到通知,请修改为 "true" -## 如果你不想完全关闭或者完全开启通知,只想在特定的时间发送通知,可以参考下面的“定义东东萌宠是否静默运行”部分,设定几个if判断条件 -export FRUIT_NOTIFY_CONTROL="" - - -################################## 定义东东农场是否使用水滴换豆卡(选填) ################################## -## 如果出现限时活动时100g水换20豆,此时比浇水划算,"true" 表示换豆(不浇水),"false" 表示不换豆(继续浇水),默认是"false" -## 如需切换为换豆(不浇水),请修改为 "true" -export FRUIT_BEAN_CARD="" - - -################################## 定义宠汪汪喂食克数(选填) ################################## -## 你期望的宠汪汪每次喂食克数,只能填入10、20、40、80,默认为10 -## 如实际持有食物量小于所设置的克数,脚本会自动降一档,直到降无可降 -## 具体情况请自行在宠汪汪游戏中去查阅攻略 -export JOY_FEED_COUNT="" - - -################################## 定义宠汪汪兑换京豆数量(选填) ################################## -## 目前的可用值包括:0、20、500、1000,其中0表示为不自动兑换京豆,如不设置,将默认为"20" -## 不同等级可兑换不同数量的京豆,详情请见宠汪汪游戏中兑换京豆选项 -## 500、1000的京豆每天有总量限制,设置了并且你也有足够积分时,也并不代表就一定能抢到 -export JD_JOY_REWARD_NAME="" - - -################################## 定义宠汪汪兑换京豆是否静默运行(选填) ################################## -## 默认为 "false",在成功兑换京豆时将发送推送通知消息(失败不发送),如想要静默不发送通知,请修改为 "true" -export JD_JOY_REWARD_NOTIFY="" - - -################################## 定义宠汪汪是否自动给好友的汪汪喂食(选填) ################################## -## 默认 "false" 不会自动给好友的汪汪喂食,如想自动喂食,请改成 "true" -export JOY_HELP_FEED="" - - -################################## 定义宠汪汪是否自动报名宠物赛跑(选填) ################################## -## 默认 "true" 参加宠物赛跑,如需关闭,请改成 "false" -export JOY_RUN_FLAG="" - - -################################## 定义宠汪汪参加比赛类型(选填) ################################## -## 当JOY_RUN_FLAG不设置或设置为 "true" 时生效 -## 可选值:2,10,50,其他值不可以。其中2代表参加双人PK赛,10代表参加10人突围赛,50代表参加50人挑战赛,不填时默认为2 -## 各个账号间请使用 & 分隔,比如:JOY_TEAM_LEVEL="2&2&50&10" -## 如果你有5个账号但只写了四个数字,那么第5个账号将默认参加2人赛,账号如果更多,与此类似 -export JOY_TEAM_LEVEL="" - - -################################## 定义宠汪汪赛跑获胜后是否推送通知(选填) ################################## -## 控制jd_joy.js脚本宠汪汪赛跑获胜后是否推送通知,"false" 为否(不推送通知消息),"true" 为是(即:发送推送通知消息),默认为 "true" -export JOY_RUN_NOTIFY="" - - -################################## 定义摇钱树是否自动将金果卖出变成金币(选填) ################################## -## 金币有时效,默认为 "false",不卖出金果为金币,如想希望自动卖出,请修改为 "true" -export MONEY_TREE_SELL_FRUIT="" - - -################################## 定义东东萌宠是否静默运行(选填) ################################## -## 默认 "false"(不静默,发送推送通知消息),如想静默请修改为 true -## 每次执行脚本通知太频繁了,改成只在周三和周六中午那一次运行时发送通知提醒 -## 除掉上述提及时间之外,均设置为 true,静默不发通知 -## 特别说明:针对北京时间有效。 -if [ $(date "+%w") -eq 6 ] && [ $(date "+%H") -ge 9 ] && [ $(date "+%H") -lt 14 ]; then - export PET_NOTIFY_CONTROL="false" -elif [ $(date "+%w") -eq 3 ] && [ $(date "+%H") -ge 9 ] && [ $(date "+%H") -lt 14 ]; then - export PET_NOTIFY_CONTROL="false" -else - export PET_NOTIFY_CONTROL="true" -fi - - -################################## 定义京喜工厂控制哪个京东账号不运行此脚本(选填) ################################## -## 输入"1"代表第一个京东账号不运行,多个使用 & 连接,例:"1&3" 代表账号1和账号3不运行京喜工厂脚本,注:输入"0",代表全部账号不运行京喜工厂脚本 -## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 -export DREAMFACTORY_FORBID_ACCOUNT="" - - -################################## 定义东东工厂控制哪个京东账号不运行此脚本(选填) ################################## -## 输入"1"代表第一个京东账号不运行,多个使用 & 连接,例:"1&3" 代表账号1和账号3不运行东东工厂脚本,注:输入"0",代表全部账号不运行东东工厂脚本 -## 如果使用了 “临时屏蔽某个Cookie” TempBlockCookie 功能,编号会发生变化 -export JDFACTORY_FORBID_ACCOUNT="" - - -################################## 定义东东工厂心仪的商品(选填) ################################## -## 只有在满足以下条件时,才自动投入电力:一是存储的电力满足生产商品所需的电力,二是心仪的商品有库存,如果没有输入心仪的商品,那么当前你正在生产的商品视作心仪的商品 -## 如果你看不懂上面的话,请去东东工厂游戏中查阅攻略 -## 心仪的商品请输入商品的全称或能唯一识别出该商品的关键字 -export FACTORAY_WANTPRODUCT_NAME="" - - -################################## 定义京喜农场通知级别(选填) ################################## -## 可用值: 0(不通知); 1(本次获得水滴>0); 2(任务执行); 3(任务执行+未种植种子),默认为"3" -export JXNC_NOTIFY_LEVEL="3" - - -################################## 定义取关参数(选填) ################################## -## jd_unsubscribe这个任务是用来取关每天做任务关注的商品和店铺,默认在每次运行时取关20个商品和20个店铺 -## 如果取关数量不够,可以根据情况增加,还可以设置 jdUnsubscribeStopGoods 和 jdUnsubscribeStopShop -## 商品取关数量 -goodPageSize="" -## 店铺取关数量 -shopPageSize="" -## 遇到此商品不再取关此商品以及它后面的商品,需去商品详情页长按拷贝商品信息 -jdUnsubscribeStopGoods="" -## 遇到此店铺不再取关此店铺以及它后面的店铺,请从头开始输入店铺名称 -jdUnsubscribeStopShop="" - - -################################## 疯狂的JOY(选填) ################################## -## 疯狂的JOY循环助力,"true" 表示循环助力,"false" 表示不循环助力,默认 "false" -export JDJOY_HELPSELF="" - -## 疯狂的JOY京豆兑换,目前最小值为500/1000京豆,默认为 "0" 不开启京豆兑换 -export JDJOY_APPLYJDBEAN="" - -## 疯狂的JOY自动购买什么等级的JOY,如需要使用请自行解除注释 -# export BUY_JOY_LEVEL="" - - -################################## 定义是否自动加购物车(选填) ################################## -## 口袋书店和东东小窝有些任务需要将商品加进购物车才能完成,默认 "false" 不做这些任务,如想做,请设置为 "true" -export PURCHASE_SHOPS="" - -################################## 定义京喜财富岛通知(选填) ################################## -## 输入true为通知,默认 "false",不填则为不通知 -export CFD_NOTIFY_CONTROL="" - - -################################## 定义京小兑是否自动把抽奖卷兑换为兑币,(选填) ################################## -## 输入true为自动兑换,不填则为不兑换 -export JD_JXD_EXCHANGE="" - - -################################## 定义宠汪汪是否开启内部互助,(选填) ################################## -## 输入true为开启内部互助,不填默认关闭 -export JOY_RUN_HELP_MYSELF="" - -################################## Telegram 代理(选填) ################################## -## Telegram 代理的 IP,代理类型为 http,比如你代理是 http://127.0.0.1:1080,则填写 "127.0.0.1" -## 如需使用,请自行解除下一行的注释 -# export TG_PROXY_HOST="" - -## Telegram 代理的端口,代理类型为 http,比如你代理是 http://127.0.0.1:1080,则填写 "1080" -## 如需使用,请自行解除下一行的注释 -# export TG_PROXY_PORT="" - - -################################## 是否添加DIY脚本(选填) ################################## -## 如果你自己会写shell脚本,并且希望在每次git_pull.sh这个脚本运行时,额外运行你的DIY脚本,请赋值为 "true" -## 同时,请务必将你的脚本命名为 extra.sh (只能叫这个文件名),放在 config 目录下 -## 仓库下已经上传extra.sh模板,如果你想使用;可以参考本仓库下 extra.sh 文件;地址:https://raw.githubusercontent.com/dockere/jd-base/master/sample/diy.sh -EnableExtraShell="" - - -################################## 互助码填法示例 ################################## -## **互助码是填在My系列变量中的,ForOther系统变量中只要填入My系列的变量名即可,按注释中的例子拼接,以东东农场为例,如下所示。** -## **实际上东东农场一个账号只能给别人助力3次,我多写的话,只有前几个会被助力。但如果前面的账号获得的助力次数已经达到上限了,那么还是会尝试继续给余下的账号助力,所以多填也是有意义的。** -## **ForOther系列变量必须从1开始编号,依次编下去。** - -# MyFruit1="e6e04602d5e343258873af1651b603ec" # 这是Cookie1这个账号的互助码 -# MyFruit2="52801b06ce2a462f95e1d59d7e856ef4" # 这是Cookie2这个账号的互助码 -# MyFruit3="e2fd1311229146cc9507528d0b054da8" # 这是Cookie3这个账号的互助码 -# MyFruit4="6dc9461f662d490991a31b798f624128" # 这是Cookie4这个账号的互助码 -# MyFruit5="30f29addd75d44e88fb452bbfe9f2110" # 这是Cookie5这个账号的互助码 -# MyFruit6="1d02fc9e0e574b4fa928e84cb1c5e70b" # 这是Cookie6这个账号的互助码 -# MyFruitA="5bc73a365ff74a559bdee785ea97fcc5" # 这是我和别人交换互助,另外一个用户A的互助码 -# MyFruitB="6d402dcfae1043fba7b519e0d6579a6f" # 这是我和别人交换互助,另外一个用户B的互助码 -# MyFruitC="5efc7fdbb8e0436f8694c4c393359576" # 这是我和别人交换互助,另外一个用户C的互助码 - -# ForOtherFruit1="${MyFruit2}@${MyFruitB}@${MyFruit4}" # Cookie1这个账号助力Cookie2的账号的账号、Cookie4的账号以及用户B -# ForOtherFruit2="${MyFruit1}@${MyFruitA}@${MyFruit4}" # Cookie2这个账号助力Cookie1的账号的账号、Cookie4的账号以及用户A -# ForOtherFruit3="${MyFruit1}@${MyFruit2}@${MyFruitC}@${MyFruit4}@${MyFruitA}@${MyFruit6}" # 解释同上,东东农场实际上只能助力3次 -# ForOtherFruit4="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitC}@${MyFruit6}@${MyFruitA}" # 解释同上,东东农场实际上只能助力3次 -# ForOtherFruit5="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitB}@${MyFruit4}@${MyFruit6}@${MyFruitC}@${MyFruitA}" -# ForOtherFruit6="${MyFruit1}@${MyFruit2}@${MyFruit3}@${MyFruitA}@${MyFruit4}@${MyFruit5}@${MyFruitC}" diff --git a/sample/crontab.list.sample b/sample/crontab.list.sample deleted file mode 100644 index 2ca7c064..00000000 --- a/sample/crontab.list.sample +++ /dev/null @@ -1,27 +0,0 @@ -# 更新shell及面板代码 -55 2,13 * * * git_pull >> ${QL_DIR}/log/git_pull.log 2>&1 - -# 更新你需要的仓库的代码 -# diy test作者 test仓库 test路径1|test路径2 黑名单1文件名|黑名单2文件名 -33 * * * * diy whyour hundun "quanx/jx|quanx/jd" tokens >> ${QL_DIR}/log/diy_pull.log 2>&1 -31 * * * * diy monk-coder dust i-chenzhe >> ${QL_DIR}/log/diy_pull.log 2>&1 - -# 重新build面板 -# 30 7 */7 * * rebuild >> ${QL_DIR}/log/rebuild.log 2>&1 - -# 删除 RmLogDaysAgo 指定天数以前的旧日志,本行为不记录日志 -57 13 * * * rm_log >/dev/null 2>&1 - -# 导出所有互助码清单,日志在log/export_sharecodes下 -48 5 * * * export_sharecodes - -# 重启挂机脚本 -# 33 3 * * * js hangup - -# 重置密码 -# 33 6 */7 * * js resetpwd - -# 运行所有脚本(慎用) -# 33 9 */7 * * js runall - -# 其他定时任务 diff --git a/sample/notify.js b/sample/notify.js new file mode 100644 index 00000000..6711e8f0 --- /dev/null +++ b/sample/notify.js @@ -0,0 +1,742 @@ +/* + * @Author: lxk0301 https://gitee.com/lxk0301 + * @Date: 2020-08-19 16:12:40 + * @Last Modified by: whyour + * @Last Modified time: 2021-5-1 15:00:54 + * sendNotify 推送通知功能 + * @param text 通知头 + * @param desp 通知体 + * @param params 某些推送通知方式点击弹窗可跳转, 例:{ url: 'https://abc.com' } + * @param author 作者仓库等信息 例:`本脚本免费使用 By:https://github.com/whyour/qinglong` + */ + +const querystring = require('querystring'); +const $ = new Env(); +const timeout = 15000; //超时时间(单位毫秒) +// =======================================微信server酱通知设置区域=========================================== +//此处填你申请的SCKEY. +//(环境变量名 PUSH_KEY) +let SCKEY = ''; + +// =======================================Bark App通知设置区域=========================================== +//此处填你BarkAPP的信息(IP/设备码,例如:https://api.day.app/XXXXXXXX) +let BARK_PUSH = ''; +//BARK app推送铃声,铃声列表去APP查看复制填写 +let BARK_SOUND = ''; + +// =======================================telegram机器人通知设置区域=========================================== +//此处填你telegram bot 的Token,telegram机器人通知推送必填项.例如:1077xxx4424:AAFjv0FcqxxxxxxgEMGfi22B4yh15R5uw +//(环境变量名 TG_BOT_TOKEN) +let TG_BOT_TOKEN = ''; +//此处填你接收通知消息的telegram用户的id,telegram机器人通知推送必填项.例如:129xxx206 +//(环境变量名 TG_USER_ID) +let TG_USER_ID = ''; +//tg推送HTTP代理设置(不懂可忽略,telegram机器人通知推送功能中非必填) +let TG_PROXY_HOST = ''; //例如:127.0.0.1(环境变量名:TG_PROXY_HOST) +let TG_PROXY_PORT = ''; //例如:1080(环境变量名:TG_PROXY_PORT) +let TG_PROXY_AUTH = ''; //tg代理配置认证参数 +//Telegram api自建的反向代理地址(不懂可忽略,telegram机器人通知推送功能中非必填),默认tg官方api(环境变量名:TG_API_HOST) +let TG_API_HOST = 'api.telegram.org'; +// =======================================钉钉机器人通知设置区域=========================================== +//此处填你钉钉 bot 的webhook,例如:5a544165465465645d0f31dca676e7bd07415asdasd +//(环境变量名 DD_BOT_TOKEN) +let DD_BOT_TOKEN = ''; +//密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串 +let DD_BOT_SECRET = ''; + +// =======================================企业微信机器人通知设置区域=========================================== +//此处填你企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa +//(环境变量名 QYWX_KEY) +let QYWX_KEY = ''; + +// =======================================企业微信应用消息通知设置区域=========================================== +/* + 此处填你企业微信应用消息的值(详见文档 https://work.weixin.qq.com/api/doc/90000/90135/90236) + 环境变量名 QYWX_AM依次填入 corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型) + 注意用,号隔开(英文输入法的逗号),例如:wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat + 可选推送消息类型(推荐使用图文消息(mpnews)): + - 文本卡片消息: 0 (数字零) + - 文本消息: 1 (数字一) + - 图文消息(mpnews): 素材库图片id, 可查看此教程(http://note.youdao.com/s/HMiudGkb)或者(https://note.youdao.com/ynoteshare1/index.html?id=1a0c8aff284ad28cbd011b29b3ad0191&type=note) + */ +let QYWX_AM = ''; + +// =======================================iGot聚合推送通知设置区域=========================================== +//此处填您iGot的信息(推送key,例如:https://push.hellyw.com/XXXXXXXX) +let IGOT_PUSH_KEY = ''; + +// =======================================push+设置区域======================================= +//官方文档:http://www.pushplus.plus/ +//PUSH_PLUS_TOKEN:微信扫码登录后一对一推送或一对多推送下面的token(您的Token),不提供PUSH_PLUS_USER则默认为一对一推送 +//PUSH_PLUS_USER: 一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送) +let PUSH_PLUS_TOKEN = ''; +let PUSH_PLUS_USER = ''; + +//==========================云端环境变量的判断与接收========================= +if (process.env.PUSH_KEY) { + SCKEY = process.env.PUSH_KEY; +} + +if (process.env.QQ_SKEY) { + QQ_SKEY = process.env.QQ_SKEY; +} + +if (process.env.QQ_MODE) { + QQ_MODE = process.env.QQ_MODE; +} + +if (process.env.BARK_PUSH) { + if ( + process.env.BARK_PUSH.indexOf('https') > -1 || + process.env.BARK_PUSH.indexOf('http') > -1 + ) { + //兼容BARK自建用户 + BARK_PUSH = process.env.BARK_PUSH; + } else { + BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}`; + } + if (process.env.BARK_SOUND) { + BARK_SOUND = process.env.BARK_SOUND; + } +} else { + if ( + BARK_PUSH && + BARK_PUSH.indexOf('https') === -1 && + BARK_PUSH.indexOf('http') === -1 + ) { + //兼容BARK本地用户只填写设备码的情况 + BARK_PUSH = `https://api.day.app/${BARK_PUSH}`; + } +} +if (process.env.TG_BOT_TOKEN) { + TG_BOT_TOKEN = process.env.TG_BOT_TOKEN; +} +if (process.env.TG_USER_ID) { + TG_USER_ID = process.env.TG_USER_ID; +} +if (process.env.TG_PROXY_AUTH) TG_PROXY_AUTH = process.env.TG_PROXY_AUTH; +if (process.env.TG_PROXY_HOST) TG_PROXY_HOST = process.env.TG_PROXY_HOST; +if (process.env.TG_PROXY_PORT) TG_PROXY_PORT = process.env.TG_PROXY_PORT; +if (process.env.TG_API_HOST) TG_API_HOST = process.env.TG_API_HOST; + +if (process.env.DD_BOT_TOKEN) { + DD_BOT_TOKEN = process.env.DD_BOT_TOKEN; + if (process.env.DD_BOT_SECRET) { + DD_BOT_SECRET = process.env.DD_BOT_SECRET; + } +} + +if (process.env.QYWX_KEY) { + QYWX_KEY = process.env.QYWX_KEY; +} + +if (process.env.QYWX_AM) { + QYWX_AM = process.env.QYWX_AM; +} + +if (process.env.IGOT_PUSH_KEY) { + IGOT_PUSH_KEY = process.env.IGOT_PUSH_KEY; +} + +if (process.env.PUSH_PLUS_TOKEN) { + PUSH_PLUS_TOKEN = process.env.PUSH_PLUS_TOKEN; +} +if (process.env.PUSH_PLUS_USER) { + PUSH_PLUS_USER = process.env.PUSH_PLUS_USER; +} +//==========================云端环境变量的判断与接收========================= + +/** + * sendNotify 推送通知功能 + * @param text 通知头 + * @param desp 通知体 + * @param params 某些推送通知方式点击弹窗可跳转, 例:{ url: 'https://abc.com' } + * @param author 作者仓库等信息 例:`本脚本免费使用 By:https://github.com/whyour/qinglong` + * @returns {Promise} + */ +async function sendNotify( + text, + desp, + params = {}, + author = '\n\n本脚本免费使用 By:https://github.com/whyour/qinglong', +) { + //提供6种通知 + desp += author; //增加作者信息,防止被贩卖等 + await Promise.all([ + serverNotify(text, desp), //微信server酱 + pushPlusNotify(text, desp), //pushplus(推送加) + ]); + //由于上述两种微信通知需点击进去才能查看到详情,故text(标题内容)携带了账号序号以及昵称信息,方便不点击也可知道是哪个京东哪个活动 + text = text.match(/.*?(?=\s?-)/g) ? text.match(/.*?(?=\s?-)/g)[0] : text; + await Promise.all([ + BarkNotify(text, desp, params), //iOS Bark APP + tgBotNotify(text, desp), //telegram 机器人 + ddBotNotify(text, desp), //钉钉机器人 + qywxBotNotify(text, desp), //企业微信机器人 + qywxamNotify(text, desp), //企业微信应用消息推送 + iGotNotify(text, desp, params), //iGot + ]); +} + +function serverNotify(text, desp, time = 2100) { + return new Promise((resolve) => { + if (SCKEY) { + //微信server酱推送通知一个\n不会换行,需要两个\n才能换行,故做此替换 + desp = desp.replace(/[\n\r]/g, '\n\n'); + const options = { + url: SCKEY.includes('SCT') + ? `https://sctapi.ftqq.com/${SCKEY}.send` + : `https://sc.ftqq.com/${SCKEY}.send`, + body: `text=${text}&desp=${desp}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + timeout, + }; + setTimeout(() => { + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('发送通知调用API失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + //server酱和Server酱·Turbo版的返回json格式不太一样 + if (data.errno === 0 || data.data.errno === 0) { + console.log('server酱发送通知消息成功🎉\n'); + } else if (data.errno === 1024) { + // 一分钟内发送相同的内容会触发 + console.log(`server酱发送通知消息异常: ${data.errmsg}\n`); + } else { + console.log( + `server酱发送通知消息异常\n${JSON.stringify(data)}`, + ); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + }, time); + } else { + resolve(); + } + }); +} + +function CoolPush(text, desp) { + return new Promise((resolve) => { + if (QQ_SKEY) { + let options = { + url: `https://push.xuthus.cc/${QQ_MODE}/${QQ_SKEY}`, + headers: { + 'Content-Type': 'application/json', + }, + }; + + // 已知敏感词 + text = text.replace(/京豆/g, '豆豆'); + desp = desp.replace(/京豆/g, ''); + desp = desp.replace(/🐶/g, ''); + desp = desp.replace(/红包/g, 'H包'); + + switch (QQ_MODE) { + case 'email': + options.json = { + t: text, + c: desp, + }; + break; + default: + options.body = `${text}\n\n${desp}`; + } + + let pushMode = function (t) { + switch (t) { + case 'send': + return '个人'; + case 'group': + return 'QQ群'; + case 'wx': + return '微信'; + case 'ww': + return '企业微信'; + case 'email': + return '邮件'; + default: + return '未知方式'; + } + }; + + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log(`发送${pushMode(QQ_MODE)}通知调用API失败!!\n`); + console.log(err); + } else { + data = JSON.parse(data); + if (data.code === 200) { + console.log(`酷推发送${pushMode(QQ_MODE)}通知消息成功🎉\n`); + } else if (data.code === 400) { + console.log( + `QQ酷推(Cool Push)发送${pushMode(QQ_MODE)}推送失败:${ + data.msg + }\n`, + ); + } else if (data.code === 503) { + console.log(`QQ酷推出错,${data.message}:${data.data}\n`); + } else { + console.log(`酷推推送异常: ${JSON.stringify(data)}`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + +function BarkNotify(text, desp, params = {}) { + return new Promise((resolve) => { + if (BARK_PUSH) { + const options = { + url: `${BARK_PUSH}/${encodeURIComponent(text)}/${encodeURIComponent( + desp, + )}?sound=${BARK_SOUND}&${querystring.stringify(params)}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + timeout, + }; + $.get(options, (err, resp, data) => { + try { + if (err) { + console.log('Bark APP发送通知调用API失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + if (data.code === 200) { + console.log('Bark APP发送通知消息成功🎉\n'); + } else { + console.log(`${data.message}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + } else { + resolve(); + } + }); +} + +function tgBotNotify(text, desp) { + return new Promise((resolve) => { + if (TG_BOT_TOKEN && TG_USER_ID) { + const options = { + url: `https://${TG_API_HOST}/bot${TG_BOT_TOKEN}/sendMessage`, + body: `chat_id=${TG_USER_ID}&text=${text}\n\n${desp}&disable_web_page_preview=true`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + timeout, + }; + if (TG_PROXY_HOST && TG_PROXY_PORT) { + const tunnel = require('tunnel'); + const agent = { + https: tunnel.httpsOverHttp({ + proxy: { + host: TG_PROXY_HOST, + port: TG_PROXY_PORT * 1, + proxyAuth: TG_PROXY_AUTH, + }, + }), + }; + Object.assign(options, { agent }); + } + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('telegram发送通知消息失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + if (data.ok) { + console.log('Telegram发送通知消息成功🎉。\n'); + } else if (data.error_code === 400) { + console.log( + '请主动给bot发送一条消息并检查接收用户ID是否正确。\n', + ); + } else if (data.error_code === 401) { + console.log('Telegram bot token 填写错误。\n'); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} +function ddBotNotify(text, desp) { + return new Promise((resolve) => { + const options = { + url: `https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}`, + json: { + msgtype: 'text', + text: { + content: ` ${text}\n\n${desp}`, + }, + }, + headers: { + 'Content-Type': 'application/json', + }, + timeout, + }; + if (DD_BOT_TOKEN && DD_BOT_SECRET) { + const crypto = require('crypto'); + const dateNow = Date.now(); + const hmac = crypto.createHmac('sha256', DD_BOT_SECRET); + hmac.update(`${dateNow}\n${DD_BOT_SECRET}`); + const result = encodeURIComponent(hmac.digest('base64')); + options.url = `${options.url}×tamp=${dateNow}&sign=${result}`; + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('钉钉发送通知消息失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + if (data.errcode === 0) { + console.log('钉钉发送通知消息成功🎉。\n'); + } else { + console.log(`${data.errmsg}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else if (DD_BOT_TOKEN) { + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('钉钉发送通知消息失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + if (data.errcode === 0) { + console.log('钉钉发送通知消息完成。\n'); + } else { + console.log(`${data.errmsg}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + +function qywxBotNotify(text, desp) { + return new Promise((resolve) => { + const options = { + url: `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${QYWX_KEY}`, + json: { + msgtype: 'text', + text: { + content: ` ${text}\n\n${desp}`, + }, + }, + headers: { + 'Content-Type': 'application/json', + }, + timeout, + }; + if (QYWX_KEY) { + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('企业微信发送通知消息失败!!\n'); + console.log(err); + } else { + data = JSON.parse(data); + if (data.errcode === 0) { + console.log('企业微信发送通知消息成功🎉。\n'); + } else { + console.log(`${data.errmsg}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + +function ChangeUserId(desp) { + const QYWX_AM_AY = QYWX_AM.split(','); + if (QYWX_AM_AY[2]) { + const userIdTmp = QYWX_AM_AY[2].split('|'); + let userId = ''; + for (let i = 0; i < userIdTmp.length; i++) { + const count = '账号' + (i + 1); + const count2 = '签到号 ' + (i + 1); + if (desp.match(count2)) { + userId = userIdTmp[i]; + } + } + if (!userId) userId = QYWX_AM_AY[2]; + return userId; + } else { + return '@all'; + } +} + +function qywxamNotify(text, desp) { + return new Promise((resolve) => { + if (QYWX_AM) { + const QYWX_AM_AY = QYWX_AM.split(','); + const options_accesstoken = { + url: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`, + json: { + corpid: `${QYWX_AM_AY[0]}`, + corpsecret: `${QYWX_AM_AY[1]}`, + }, + headers: { + 'Content-Type': 'application/json', + }, + timeout, + }; + $.post(options_accesstoken, (err, resp, data) => { + html = desp.replace(/\n/g, '
'); + var json = JSON.parse(data); + accesstoken = json.access_token; + let options; + + switch (QYWX_AM_AY[4]) { + case '0': + options = { + msgtype: 'textcard', + textcard: { + title: `${text}`, + description: `${desp}`, + url: 'https://github.com/whyour/qinglong', + btntxt: '更多', + }, + }; + break; + + case '1': + options = { + msgtype: 'text', + text: { + content: `${text}\n\n${desp}`, + }, + }; + break; + + default: + options = { + msgtype: 'mpnews', + mpnews: { + articles: [ + { + title: `${text}`, + thumb_media_id: `${QYWX_AM_AY[4]}`, + author: `智能助手`, + content_source_url: ``, + content: `${html}`, + digest: `${desp}`, + }, + ], + }, + }; + } + if (!QYWX_AM_AY[4]) { + //如不提供第四个参数,则默认进行文本消息类型推送 + options = { + msgtype: 'text', + text: { + content: `${text}\n\n${desp}`, + }, + }; + } + options = { + url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${accesstoken}`, + json: { + touser: `${ChangeUserId(desp)}`, + agentid: `${QYWX_AM_AY[3]}`, + safe: '0', + ...options, + }, + headers: { + 'Content-Type': 'application/json', + }, + }; + + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log( + '成员ID:' + + ChangeUserId(desp) + + '企业微信应用消息发送通知消息失败!!\n', + ); + console.log(err); + } else { + data = JSON.parse(data); + if (data.errcode === 0) { + console.log( + '成员ID:' + + ChangeUserId(desp) + + '企业微信应用消息发送通知消息成功🎉。\n', + ); + } else { + console.log(`${data.errmsg}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + }); + } else { + resolve(); + } + }); +} + +function iGotNotify(text, desp, params = {}) { + return new Promise((resolve) => { + if (IGOT_PUSH_KEY) { + // 校验传入的IGOT_PUSH_KEY是否有效 + const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$'); + if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) { + console.log('您所提供的IGOT_PUSH_KEY无效\n'); + resolve(); + return; + } + const options = { + url: `https://push.hellyw.com/${IGOT_PUSH_KEY.toLowerCase()}`, + body: `title=${text}&content=${desp}&${querystring.stringify(params)}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + timeout, + }; + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log('发送通知调用API失败!!\n'); + console.log(err); + } else { + if (typeof data === 'string') data = JSON.parse(data); + if (data.ret === 0) { + console.log('iGot发送通知消息成功🎉\n'); + } else { + console.log(`iGot发送通知消息失败:${data.errMsg}\n`); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + +function pushPlusNotify(text, desp) { + return new Promise((resolve) => { + if (PUSH_PLUS_TOKEN) { + desp = desp.replace(/[\n\r]/g, '
'); // 默认为html, 不支持plaintext + const body = { + token: `${PUSH_PLUS_TOKEN}`, + title: `${text}`, + content: `${desp}`, + topic: `${PUSH_PLUS_USER}`, + }; + const options = { + url: `http://www.pushplus.plus/send`, + body: JSON.stringify(body), + headers: { + 'Content-Type': ' application/json', + }, + timeout, + }; + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log( + `push+发送${ + PUSH_PLUS_USER ? '一对多' : '一对一' + }通知消息失败!!\n`, + ); + console.log(err); + } else { + data = JSON.parse(data); + if (data.code === 200) { + console.log( + `push+发送${ + PUSH_PLUS_USER ? '一对多' : '一对一' + }通知消息完成。\n`, + ); + } else { + console.log( + `push+发送${ + PUSH_PLUS_USER ? '一对多' : '一对一' + }通知消息失败:${data.msg}\n`, + ); + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(data); + } + }); + } else { + resolve(); + } + }); +} + +module.exports = { + sendNotify, + BARK_PUSH, +}; + +// 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)} diff --git a/sample/notify.py b/sample/notify.py new file mode 100644 index 00000000..3106710e --- /dev/null +++ b/sample/notify.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +# _*_ coding:utf-8 _*_ + +import sys +import os +cur_path = os.path.abspath(os.path.dirname(__file__)) +root_path = os.path.split(cur_path)[0] +sys.path.append(root_path) +import requests +import json +import traceback +import time +import hmac +import hashlib +import base64 +import urllib.parse +from requests.adapters import HTTPAdapter +from urllib3.util import Retry +import re + +# 通知服务 +BARK = '' # bark服务,此参数如果以http或者https开头则判定为自建bark服务; secrets可填; +SCKEY = '' # Server酱的SCKEY; secrets可填 +TG_BOT_TOKEN = '' # tg机器人的TG_BOT_TOKEN; secrets可填 +TG_USER_ID = '' # tg机器人的TG_USER_ID; secrets可填 +TG_PROXY_IP = '' # tg机器人的TG_PROXY_IP; secrets可填 +TG_PROXY_PORT = '' # tg机器人的TG_PROXY_PORT; secrets可填 +DD_BOT_ACCESS_TOKEN = '' # 钉钉机器人的DD_BOT_ACCESS_TOKEN; secrets可填 +DD_BOT_SECRET = '' # 钉钉机器人的DD_BOT_SECRET; secrets可填 +QYWX_APP = '' # 企业微信应用的QYWX_APP; secrets可填 参考http://note.youdao.com/s/HMiudGkb + +notify_mode = [] + +# GitHub action运行需要填写对应的secrets +if "BARK" in os.environ and os.environ["BARK"]: + BARK = os.environ["BARK"] +if "SCKEY" in os.environ and os.environ["SCKEY"]: + SCKEY = os.environ["SCKEY"] +if "TG_BOT_TOKEN" in os.environ and os.environ["TG_BOT_TOKEN"] and "TG_USER_ID" in os.environ and os.environ["TG_USER_ID"]: + TG_BOT_TOKEN = os.environ["TG_BOT_TOKEN"] + TG_USER_ID = os.environ["TG_USER_ID"] +if "DD_BOT_ACCESS_TOKEN" in os.environ and os.environ["DD_BOT_ACCESS_TOKEN"] and "DD_BOT_SECRET" in os.environ and os.environ["DD_BOT_SECRET"]: + DD_BOT_ACCESS_TOKEN = os.environ["DD_BOT_ACCESS_TOKEN"] + DD_BOT_SECRET = os.environ["DD_BOT_SECRET"] +if "QYWX_APP" in os.environ and os.environ["QYWX_APP"]: + QYWX_APP = os.environ["QYWX_APP"] + +if BARK: + notify_mode.append('bark') + print("BARK 推送打开") +if SCKEY: + notify_mode.append('sc_key') + print("Server酱 推送打开") +if TG_BOT_TOKEN and TG_USER_ID: + notify_mode.append('telegram_bot') + print("Telegram 推送打开") +if DD_BOT_ACCESS_TOKEN and DD_BOT_SECRET: + notify_mode.append('dingding_bot') + print("钉钉机器人 推送打开") +if QYWX_APP: + notify_mode.append('qywxapp_bot') + print("企业微信应用 推送打开") + +def bark(title, content): + print("\n") + if not BARK: + print("bark服务的bark_token未设置!!\n取消推送") + return + print("bark服务启动") + url = None + if BARK.startswith('http'): + url = f"""{BARK}/{title}/{content}""" + else: + url = f"""https://api.day.app/{BARK}/{title}/{content}""" + response = requests.get(url).json() + if response['code'] == 200: + print('推送成功!') + else: + print('推送失败!') + +def serverJ(title, content): + print("\n") + if not SCKEY: + print("server酱服务的SCKEY未设置!!\n取消推送") + return + print("serverJ服务启动") + data = { + "text": title, + "desp": content.replace("\n", "\n\n") + } + response = requests.post(f"https://sc.ftqq.com/{SCKEY}.send", data=data).json() + if response['errno'] == 0: + print('推送成功!') + else: + print('推送失败!') + +def telegram_bot(title, content): + print("\n") + bot_token = TG_BOT_TOKEN + user_id = TG_USER_ID + if not bot_token or not user_id: + print("tg服务的bot_token或者user_id未设置!!\n取消推送") + return + print("tg服务启动") + url=f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage" + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + payload = {'chat_id': str(TG_USER_ID), 'text': f'{title}\n\n{content}', 'disable_web_page_preview': 'true'} + proxies = None + if TG_PROXY_IP and TG_PROXY_PORT: + proxyStr = "http://{}:{}".format(TG_PROXY_IP, TG_PROXY_PORT) + proxies = {"http": proxyStr, "https": proxyStr} + response = requests.post(url=url, headers=headers, params=payload, proxies=proxies).json() + if response['ok']: + print('推送成功!') + else: + print('推送失败!') + +def dingding_bot(title, content): + timestamp = str(round(time.time() * 1000)) # 时间戳 + secret_enc = DD_BOT_SECRET.encode('utf-8') + string_to_sign = '{}\n{}'.format(timestamp, 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).digest() + sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) # 签名 + print('开始使用 钉钉机器人 推送消息...', end='') + url = f'https://oapi.dingtalk.com/robot/send?access_token={DD_BOT_ACCESS_TOKEN}×tamp={timestamp}&sign={sign}' + headers = {'Content-Type': 'application/json;charset=utf-8'} + data = { + 'msgtype': 'text', + 'text': {'content': f'{title}\n\n{content}'} + } + response = requests.post(url=url, data=json.dumps(data), headers=headers, timeout=15).json() + if not response['errcode']: + print('推送成功!') + else: + print('推送失败!') + +def qywxapp_bot(title, content): + print("\n") + if not QYWX_APP: + print("企业微信应用的QYWX_APP未设置!!\n取消推送") + return + print("企业微信应用启动") + qywx_app_params = QYWX_APP.split(',') + url='https://qyapi.weixin.qq.com/cgi-bin/gettoken' + headers= { + 'Content-Type': 'application/json', + } + payload = { + 'corpid': qywx_app_params[0], + 'corpsecret': qywx_app_params[1], + } + response = requests.post(url=url, headers=headers, data=json.dumps(payload), timeout=15).json() + accesstoken = response["access_token"] + html = content.replace("\n", "
") + + options = None + if not qywx_app_params[4]: + options = { + 'msgtype': 'text', + 'text': { + content: f'{title}\n\n${content}' + } + } + elif qywx_app_params[4] == '0': + options = { + 'msgtype': 'textcard', + 'textcard': { + title: f'{title}', + description: f'{content}', + btntxt: '更多' + } + } + elif qywx_app_params[4] == '1': + options = { + 'msgtype': 'text', + 'text': { + content: f'{title}\n\n${content}' + } + } + else: + options = { + 'msgtype': 'mpnews', + 'mpnews': { + 'articles': [ + { + 'title': f'{title}', + 'thumb_media_id': f'{qywx_app_params[4]}', + 'author': '智能助手', + 'content_source_url': '', + 'content': f'{html}', + 'digest': f'{content}' + } + ] + } + } + + url=f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={accesstoken}" + data = { + 'touser': f'{change_user_id(content)}', + 'agentid': f'{qywx_app_params[3]}', + 'safe': '0' + } + data.update(options) + headers = { + 'Content-Type': 'application/json', + } + response = requests.post(url=url, headers=headers, data=json.dumps(data)).json() + + if response['errcode'] == 0: + print('推送成功!') + else: + print('推送失败!') + +def change_user_id(desp): + qywx_app_params = QYWX_APP.split(',') + if qywx_app_params[2]: + userIdTmp = qywx_app_params[2].split("|") + userId = "" + for i in range(len(userIdTmp)): + count1 = f"账号{i + 1}" + count2 = f"签到号{i + 1}" + if re.search(count1, desp) or re.search(count2, desp): + userId = userIdTmp[i] + if not userId: + userId = qywx_app_params[2] + return userId + else: + return "@all" + +def send(title, content): + """ + 使用 bark, telegram bot, dingding bot, serverJ 发送手机推送 + :param title: + :param content: + :return: + """ + for i in notify_mode: + if i == 'bark': + if BARK: + bark(title=title, content=content) + else: + print('未启用 bark') + continue + if i == 'sc_key': + if SCKEY: + serverJ(title=title, content=content) + else: + print('未启用 Server酱') + continue + elif i == 'dingding_bot': + if DD_BOT_ACCESS_TOKEN and DD_BOT_SECRET: + dingding_bot(title=title, content=content) + else: + print('未启用 钉钉机器人') + continue + elif i == 'telegram_bot': + if TG_BOT_TOKEN and TG_USER_ID: + telegram_bot(title=title, content=content) + else: + print('未启用 telegram机器人') + continue + elif i == 'qywxapp_bot': + if QYWX_APP: + qywxapp_bot(title=title, content=content) + else: + print('未启用 企业微信应用推送') + continue + else: + print('此类推送方式不存在') + +def main(): + send('title', 'content') + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/sample/package.json b/sample/package.json new file mode 100644 index 00000000..b80b416a --- /dev/null +++ b/sample/package.json @@ -0,0 +1,16 @@ +{ + "name": "dependence", + "author": "", + "license": "ISC", + "dependencies": { + "crypto-js": "^4.0.0", + "download": "^8.0.0", + "got": "^11.5.1", + "http-server": "^0.12.3", + "qrcode-terminal": "^0.12.0", + "request": "^2.88.2", + "tough-cookie": "^4.0.0", + "tunnel": "0.0.6", + "ws": "^7.4.3" + } +} \ No newline at end of file diff --git a/sample/requirements.txt b/sample/requirements.txt new file mode 100644 index 00000000..663bd1f6 --- /dev/null +++ b/sample/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file diff --git a/shell/api.sh b/shell/api.sh index b633ff40..cd3be60a 100755 --- a/shell/api.sh +++ b/shell/api.sh @@ -1,70 +1,117 @@ #!/usr/bin/env bash get_token() { - token=$(cat $AuthConf | jq -r .token) -} - -get_json_value() { - local json=$1 - local key=$2 - - if [[ -z "$3" ]]; then - local num=1 - else - local num=$3 - fi - - local value=$(echo "${json}" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'${key}'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p) - - echo ${value} + token=$(cat $file_auth_user | jq -r .token) } add_cron_api() { - local currentTimeStamp=$(date +%s) - if [ $# -eq 1 ]; then - local schedule=$(echo "$1" | awk -F ":" '{print $1}') - local command=$(echo "$1" | awk -F ":" '{print $2}') - local name=$(echo "$1" | awk -F ":" '{print $3}') - else - local schedule=$1 - local command=$2 - local name=$3 - fi + local currentTimeStamp=$(date +%s) + if [ $# -eq 1 ]; then + local schedule=$(echo "$1" | awk -F ":" '{print $1}') + local command=$(echo "$1" | awk -F ":" '{print $2}') + local name=$(echo "$1" | awk -F ":" '{print $3}') + else + local schedule=$1 + local command=$2 + local name=$3 + fi - local api=$(curl "http://localhost:5600/api/crons?t=$currentTimeStamp" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $token" \ - -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ - -H "Content-Type: application/json;charset=UTF-8" \ - -H "Origin: http://localhost:5700" \ - -H "Referer: http://localhost:5700/crontab" \ - -H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" \ - --data-raw "{\"name\":\"$name\",\"command\":\"$command\",\"schedule\":\"$schedule\"}" \ - --compressed) - code=$(echo $api | jq -r .code) - if [[ $code == 200 ]]; then - echo -e "$name 添加成功" - else - echo -e "$name 添加失败" - fi + local api=$( + curl -s "http://localhost:5600/api/crons?t=$currentTimeStamp" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $token" \ + -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ + -H "Content-Type: application/json;charset=UTF-8" \ + -H "Origin: http://localhost:5700" \ + -H "Referer: http://localhost:5700/crontab" \ + -H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" \ + --data-raw "{\"name\":\"$name\",\"command\":\"$command\",\"schedule\":\"$schedule\"}" \ + --compressed + ) + code=$(echo $api | jq -r .code) + message=$(echo $api | jq -r .message) + if [[ $code == 200 ]]; then + echo -e "$name -> 添加成功" + else + echo -e "$name -> 添加失败(${message})" + fi +} + +update_cron_api() { + local currentTimeStamp=$(date +%s) + if [ $# -eq 1 ]; then + local schedule=$(echo "$1" | awk -F ":" '{print $1}') + local command=$(echo "$1" | awk -F ":" '{print $2}') + local name=$(echo "$1" | awk -F ":" '{print $3}') + local id=$(echo "$1" | awk -F ":" '{print $4}') + else + local schedule=$1 + local command=$2 + local name=$3 + local id=$4 + fi + + local api=$( + curl -s "http://localhost:5600/api/crons?t=$currentTimeStamp" \ + -X 'PUT' \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $token" \ + -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ + -H "Content-Type: application/json;charset=UTF-8" \ + -H "Origin: http://localhost:5700" \ + -H "Referer: http://localhost:5700/crontab" \ + -H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" \ + --data-raw "{\"name\":\"$name\",\"command\":\"$command\",\"schedule\":\"$schedule\",\"_id\":\"$id\"}" \ + --compressed + ) + code=$(echo $api | jq -r .code) + message=$(echo $api | jq -r .message) + if [[ $code == 200 ]]; then + echo -e "$name -> 更新成功" + else + echo -e "$name -> 更新失败(${message})" + fi } del_cron_api() { - local id=$1 - local currentTimeStamp=$(date +%s) - local api=$(curl "http://localhost:5600/api/crons/$id?t=$currentTimeStamp" \ - -X 'DELETE' \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $token" \ - -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ - -H "Content-Type: application/json;charset=UTF-8" \ - -H "Origin: http://localhost:5700" \ - -H "Referer: http://localhost:5700/crontab" \ - -H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7") - code=$(echo $api | jq -r .code) - if [[ $code == 200 ]]; then - echo -e "$name 删除成功" - else - echo -e "$name 删除失败" - fi + local ids=$1 + local currentTimeStamp=$(date +%s) + local api=$( + curl -s "http://localhost:5600/api/crons?t=$currentTimeStamp" \ + -X 'DELETE' \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $token" \ + -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ + -H "Content-Type: application/json;charset=UTF-8" \ + -H "Origin: http://localhost:5700" \ + -H "Referer: http://localhost:5700/crontab" \ + -H "Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" \ + --data-raw "[$ids]" \ + --compressed + ) + code=$(echo $api | jq -r .code) + message=$(echo $api | jq -r .message) + if [[ $code == 200 ]]; then + echo -e "删除成功" + else + echo -e "删除失败(${message})" + fi +} + +get_user_info() { + local currentTimeStamp=$(date +%s) + local api=$( + curl -s "http://localhost:5700/api/user?t=$currentTimeStamp" \ + -H 'Accept: */*' \ + -H "Authorization: Bearer $token" \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36' \ + -H 'Referer: http://localhost:5700/crontab' \ + -H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7' \ + --compressed + ) + code=$(echo $api | jq -r .code) + if [[ $code != 200 ]]; then + echo -e "请先登录!" + exit 0 + fi } diff --git a/shell/bot.sh b/shell/bot.sh new file mode 100644 index 00000000..fd46b2d6 --- /dev/null +++ b/shell/bot.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +## 导入通用变量与函数 +dir_shell=/ql/shell +. $dir_shell/share.sh +repo_path="${dir_repo}/SuMaiKaDe_jddockerbot" + +echo -e "1、安装bot依赖...\n" +apk --no-cache add -f zlib-dev gcc jpeg-dev python3-dev musl-dev freetype-dev +echo + +echo -e "2、下载bot所需文件...\n" +git clone -b master https://ghproxy.com/https://github.com/SuMaiKaDe/jddockerbot.git $repo_path +cp -rf "$repo_path/jbot" $dir_root +cp -f "$repo_path/config/bot.json" "$dir_root/config" +echo + +echo -e "3、安装python3依赖...\n" +pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple +cp -f "$repo_path/jbot/requirements.txt" "$dir_root" +pip3 --default-timeout=100 install -r requirements.txt --no-cache-dir +echo + +echo -e "4、启动bot程序...\n" +cd $dir_root +ps -ef | grep "python3 -m jbot" | grep -v grep | awk '{print $1}' | xargs kill -9 2>/dev/null +nohup python3 -m jbot & +echo + +exit 0 diff --git a/shell/code.sh b/shell/code.sh new file mode 100755 index 00000000..474ff259 --- /dev/null +++ b/shell/code.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash + +## 导入通用变量与函数 +dir_shell=/ql/shell +. $dir_shell/share.sh + +## 生成pt_pin清单 +gen_pt_pin_array() { + local tmp1 tmp2 i pt_pin_temp + for ((user_num = 1; user_num <= $user_sum; user_num++)); do + tmp1=Cookie$user_num + tmp2=${!tmp1} + i=$(($user_num - 1)) + pt_pin_temp=$(echo $tmp2 | perl -pe "{s|.*pt_pin=([^; ]+)(?=;?).*|\1|; s|%|\\\x|g}") + [[ $pt_pin_temp == *\\x* ]] && pt_pin[i]=$(printf $pt_pin_temp) || pt_pin[i]=$pt_pin_temp + done +} + +## 导出互助码的通用程序,$1:去掉后缀的脚本名称,$2:config.sh中的后缀,$3:活动中文名称 +export_codes_sub() { + local task_name=$1 + local config_name=$2 + local chinese_name=$3 + local config_name_my=My$config_name + local config_name_for_other=ForOther$config_name + local i j k m n pt_pin_in_log code tmp_grep tmp_my_code tmp_for_other user_num random_num_list + if cd $dir_log/$task_name &>/dev/null && [[ $(ls) ]]; then + ## 寻找所有互助码以及对应的pt_pin + i=0 + pt_pin_in_log=() + code=() + pt_pin_and_code=$(ls -r *.log | xargs awk -v var="的$chinese_name好友互助码" 'BEGIN{FS="[( )】]+"; OFS="&"} $3~var {print $2,$4}') + for line in $pt_pin_and_code; do + pt_pin_in_log[i]=$(echo $line | awk -F "&" '{print $1}') + code[i]=$(echo $line | awk -F "&" '{print $2}') + let i++ + done + + ## 输出My系列变量 + if [[ ${#code[*]} -gt 0 ]]; then + for ((m = 0; m < ${#pt_pin[*]}; m++)); do + tmp_my_code="" + j=$((m + 1)) + for ((n = 0; n < ${#code[*]}; n++)); do + if [[ ${pt_pin[m]} == ${pt_pin_in_log[n]} ]]; then + tmp_my_code=${code[n]} + break + fi + done + echo "$config_name_my$j='$tmp_my_code'" + done + else + echo "## 从日志中未找到任何互助码" + fi + + ## 输出ForOther系列变量 + if [[ ${#code[*]} -gt 0 ]]; then + echo + case $HelpType in + 0) ## 全部一致 + tmp_for_other="" + for ((m = 0; m < ${#pt_pin[*]}; m++)); do + j=$((m + 1)) + tmp_for_other="$tmp_for_other@\${$config_name_my$j}" + done + echo "${config_name_for_other}1=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" + for ((m = 1; m < ${#pt_pin[*]}; m++)); do + j=$((m + 1)) + echo "$config_name_for_other$j=\"\${${config_name_for_other}1}\"" + done + ;; + + 1) ## 均等助力 + for ((m = 0; m < ${#pt_pin[*]}; m++)); do + tmp_for_other="" + j=$((m + 1)) + for ((n = $m; n < $(($user_sum + $m)); n++)); do + [[ $m -eq $n ]] && continue + if [[ $((n + 1)) -le $user_sum ]]; then + k=$((n + 1)) + else + k=$((n + 1 - $user_sum)) + fi + tmp_for_other="$tmp_for_other@\${$config_name_my$k}" + done + echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" + done + ;; + + 2) ## 本套脚本内账号间随机顺序助力 + for ((m = 0; m < ${#pt_pin[*]}; m++)); do + tmp_for_other="" + random_num_list=$(seq $user_sum | sort -R) + j=$((m + 1)) + for n in $random_num_list; do + [[ $j -eq $n ]] && continue + tmp_for_other="$tmp_for_other@\${$config_name_my$n}" + done + echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" + done + ;; + + *) ## 按编号优先 + for ((m = 0; m < ${#pt_pin[*]}; m++)); do + tmp_for_other="" + j=$((m + 1)) + for ((n = 0; n < ${#pt_pin[*]}; n++)); do + [[ $m -eq $n ]] && continue + k=$((n + 1)) + tmp_for_other="$tmp_for_other@\${$config_name_my$k}" + done + echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" + done + ;; + esac + fi + else + echo "## 未运行过 $task_name.js 脚本,未产生日志" + fi +} + +## 汇总输出 +export_all_codes() { + gen_pt_pin_array + echo -e "\n# 从日志提取互助码,编号和配置文件中Cookie编号完全对应,如果为空就是所有日志中都没有。\n\n# 即使某个MyXxx变量未赋值,也可以将其变量名填在ForOtherXxx中,jtask脚本会自动过滤空值。\n" + echo -n "# 你选择的互助码模板为:" + case $HelpType in + 0) + echo "所有账号助力码全部一致。" + ;; + 1) + echo "所有账号机会均等助力。" + ;; + 2) + echo "本套脚本内账号间随机顺序助力。" + ;; + *) + echo "按账号编号优先。" + ;; + esac + for ((i = 0; i < ${#name_js[*]}; i++)); do + echo -e "\n## ${name_chinese[i]}:" + export_codes_sub "${name_js[i]}" "${name_config[i]}" "${name_chinese[i]}" + done +} + +## 执行并写入日志 +log_time=$(date "+%Y-%m-%d-%H-%M-%S") +log_path="$dir_code/$log_time.log" +make_dir "$dir_code" +export_all_codes | perl -pe "{s|京东种豆|种豆|; s|crazyJoy任务|疯狂的JOY|}" | tee $log_path diff --git a/shell/export_sharecodes.sh b/shell/export_sharecodes.sh deleted file mode 100755 index 7bb51cd1..00000000 --- a/shell/export_sharecodes.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env bash - -## 路径、环境判断 -ShellDir=${QL_DIR:-$(cd $(dirname $0); pwd)} -LogDir=${ShellDir}/log -ConfigDir=${ShellDir}/config -FileConf=${ConfigDir}/config.sh -CookieConf=${ConfigDir}/cookie.sh -[[ ${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} ]] && Opt="P" || Opt="E" -Tips="从日志中未找到任何互助码" - -## 所有有互助码的活动,只需要把脚本名称去掉前缀 jd_ 后列在 Name1 中,将其中文名称列在 Name2 中,对应 config.sh 中互助码后缀列在 Name3 中即可。 -## Name1、Name2 和 Name3 中的三个名称必须一一对应。 -Name1=(fruit pet plantBean dreamFactory jdfactory crazy_joy jdzz jxnc bookshop cash sgmh cfd global) -Name2=(东东农场 东东萌宠 京东种豆得豆 京喜工厂 东东工厂 crazyJoy任务 京东赚赚 京喜农场 口袋书店 签到领现金 闪购盲盒 京喜财富岛 环球挑战赛) -Name3=(Fruit Pet Bean DreamFactory JdFactory Joy Jdzz Jxnc BookShop Cash Sgmh Cfd Global) - - -## 导入 config.sh -function Import_Conf { - if [ -f ${FileConf} ] - then - . ${CookieConf} - . ${FileConf} - if [ ! -s ${CookieConf} ]; then - echo -e "请先在Cookie管理中添加一条Cookie...\n" - exit 1 - fi - else - echo -e "配置文件 ${FileConf} 不存在,请先按教程配置好该文件...\n" - exit 1 - fi -} - - -## 用户数量 UserSum -function Count_UserSum { - UserSum=$(awk '{print NR}' ${CookieConf} | tail -n1) -} - - -## 导出互助码的通用程序 -function Cat_Scodes { - if [ -d ${LogDir}/jd_$1 ] && [[ $(ls ${LogDir}/jd_$1) != "" ]]; then - cd ${LogDir}/jd_$1 - - ## 导出助力码变量(My) - for log in $(ls -r); do - case $# in - 2) - codes=$(cat ${log} | grep -${Opt} "开始【京东账号|您的(好友)?助力码为" | uniq | perl -0777 -pe "{s|\*||g; s|开始||g; s|\n您的(好友)?助力码为(:)?:?|:|g; s|,.+||g}" | sed -r "s/【京东账号/My$2/;s/】.*?:/='/;s/】.*?/='/;s/$/'/;s/\(每次运行都变化,不影响\)//") - ;; - 3) - codes=$(grep -${Opt} $3 ${log} | uniq | sed -r "s/【京东账号/My$2/;s/(.*?】/='/;s/$/'/") - ;; - esac - if [[ ${codes} ]]; then - ## 添加判断,若未找到该用户互助码,则设置为空值 - for ((user_num=1;user_num<=${UserSum};user_num++)); do - echo -e "${codes}" | grep -${Opt}q "My$2${user_num}=" - if [ $? -eq 1 ]; then - if [ $user_num == 1 ]; then - codes=$(echo "${codes}" | sed -r "1i My${2}1=''") - else - codes=$(echo "${codes}" | sed -r "/My$2$(expr ${user_num} - 1)=/a\My$2${user_num}=''") - fi - fi - done - break - fi - done - - ## 导出为他人助力变量(ForOther) - if [[ ${codes} ]]; then - help_code="" - for ((user_num=1;user_num<=${UserSum};user_num++)); do - echo -e "${codes}" | grep -${Opt}q "My$2${user_num}=''" - if [ $? -eq 1 ]; then - help_code=${help_code}"\${My"$2${user_num}"}@" - fi - done - ## 生成互助规则模板 - for_other_codes="" - case $HelpType in - 0) ### 统一优先级助力模板 - new_code=$(echo ${help_code} | sed "s/@$//") - for ((user_num=1;user_num<=${UserSum};user_num++)); do - if [ $user_num == 1 ]; then - for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" - else - for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\"\${ForOther"${2}1"}\"\n" - fi - done - ;; - 1) ### 均匀助力模板 - for ((user_num=1;user_num<=${UserSum};user_num++)); do - echo ${help_code} | grep "\${My"$2${user_num}"}@" > /dev/null - if [ $? -eq 0 ]; then - left_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $1}') - right_str=$(echo ${help_code} | sed "s/\${My$2${user_num}}@/ /g" | awk '{print $2}') - mark="\${My$2${user_num}}@" - else - left_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $1}')${mark} - right_str=$(echo ${help_code} | sed "s/${mark}/ /g" | awk '{print $2}') - fi - new_code=$(echo ${right_str}${left_str} | sed "s/@$//") - for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" - done - ;; - *) ### 普通优先级助力模板 - for ((user_num=1;user_num<=${UserSum};user_num++)); do - new_code=$(echo ${help_code} | sed "s/\${My"$2${user_num}"}@//;s/@$//") - for_other_codes=${for_other_codes}"ForOther"$2${user_num}"=\""${new_code}"\"\n" - done - ;; - esac - echo -e "${codes}\n\n${for_other_codes}" | sed s/[[:space:]]//g - else - echo ${Tips} - fi - else - echo "未运行过 jd_$1 脚本,未产生日志" - fi -} - - -## 汇总 -function Cat_All { - echo -e "\n从最后一个日志提取互助码,受日志内容影响,仅供参考。" - for ((i=0; i<${#Name1[*]}; i++)); do - echo -e "\n${Name2[i]}:" - [[ $(Cat_Scodes "${Name1[i]}" "${Name3[i]}" "的${Name2[i]}好友互助码") == ${Tips} ]] && Cat_Scodes "${Name1[i]}" "${Name3[i]}" || Cat_Scodes "${Name1[i]}" "${Name3[i]}" "的${Name2[i]}好友互助码" - done -} - - -## 执行并写入日志 -LogTime=$(date "+%Y-%m-%d-%H-%M-%S") -LogFile="${LogDir}/export_sharecodes/${LogTime}.log" -[ ! -d "${LogDir}/export_sharecodes" ] && mkdir -p ${LogDir}/export_sharecodes -Import_Conf && Count_UserSum && Cat_All | perl -pe "{s|京东种豆|种豆|; s|crazyJoy任务|疯狂的JOY|}" | tee ${LogFile} diff --git a/shell/git_diy.sh b/shell/git_diy.sh deleted file mode 100755 index 7c335f83..00000000 --- a/shell/git_diy.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env bash -#author:spark thanks to: https://github.com/sparkssssssss/scripts - -ShellDir=${QL_DIR:-$( - cd $(dirname $0) - pwd -)} -[[ $QL_DIR ]] && ShellJs=js -ConfigDir=$ShellDir/config -ListCronCurrent=$ConfigDir/crontab.list -AuthConf=$ConfigDir/auth.json - -declare -A BlackListDict -url=$1 -path=$2 -blackword=$3 - -if [[ $# -lt 2 ]] || [[ $# -gt 4 ]]; then - echo 'Desc: 用户拉取指定用户的指定仓储' - echo 'Usage: diy ' - - echo 'repourl 仓储地址' - echo 'path 需要下载脚本的指定目录,多个目录 | 分割 path1 | path2' - echo 'blacklist 需要排除的脚本名,多个名称 | 分割 blacklist1 | blacklist2' - exit 0 -fi - -diyscriptsdir=/ql/diyscripts -mkdir -p ${diyscriptsdir} - -urlTmp=${url%*/} -repoTmp=${urlTmp##*/} -repo=${repoTmp%.*} -tmp=${url%/*} -authorTmp1=${tmp##*/} -authorTmp2=${authorTmp1##*:} -author=${authorTmp2##*.} - -if [ ! -d "$diyscriptsdir/${author}_${repo}" ]; then - echo -e "${author}本地仓库不存在,从远程拉取ing..." - cd ${diyscriptsdir} && git clone $url ${author}_${repo} - gitpullstatus=$? - [ $gitpullstatus -eq 0 ] && echo -e "${author}本地仓库拉取完毕" - [ $gitpullstatus -ne 0 ] && echo -e "${author}本地仓库拉取失败,请检查!" && exit 0 -else - cd ${diyscriptsdir}/${author}_${repo} - branch=$(git symbolic-ref --short -q HEAD) - git fetch --all - git reset --hard origin/$branch - git pull - gitpullstatus=$? -fi - -rand() { - min=$1 - max=$(($2 - $min + 1)) - num=$(cat /proc/sys/kernel/random/uuid | cksum | awk -F ' ' '{print $1}') - echo $(($num % $max + $min)) -} - -addnewcron() { - addname="" - cd ${diyscriptsdir}/${author}_${repo} - express=$(find . -name "*.js") - if [ $path ]; then - express=$(find . -name "*.js" | egrep $path) - fi - if [ $blackword ]; then - express=$(find . -name "*.js" | egrep -v $blackword | egrep $path) - fi - for js in $express; do - base=$(basename $js) - croname=$(echo "${author}_$base" | awk -F\. '{print $1}') - script_date=$(cat $js | grep ^[0-9] | awk '{print $1,$2,$3,$4,$5}' | egrep -v "[a-zA-Z]|:|\." | sort | uniq | head -n 1) - [ -z "${script_date}" ] && script_date=$(cat $js | grep -Eo "([0-9]+|\*|[0-9]+[,-].*) ([0-9]+|\*|[0-9]+[,-].*) ([0-9]+|\*|[0-9]+[,-].*) ([0-9]+|\*|[0-9]+[,-].*) ([0-9]+|\*|[0-9][,-].*)" | sort | uniq | head -n 1) - [ -z "${script_date}" ] && cron_min=$(rand 1 59) && cron_hour=$(rand 7 9) && script_date="${cron_min} ${cron_hour} * * *" - local oldCron=$(grep -c -w "$croname" "$ListCronCurrent") - if [[ oldCron -eq 0 ]]; then - local name=$(cat "$js" | grep -E "new Env\(" | perl -pe "s|(^.+)new Env\(\'*\"*(.+?)'*\"*\).+|\2|") - add_cron_api "$script_date" "js $croname" "$name" - addname="${addname}\n${croname}" - echo -e "添加了新的脚本${croname}." - fi - if [ ! -f "/ql/scripts/${author}_$base" ]; then - \cp $js /ql/scripts/${author}_$base - else - change=$(diff $js /ql/scripts/${author}_$base) - [ -n "${change}" ] && \cp $js /ql/scripts/${author}_$base && echo -e "${author}_$base 脚本更新了." - fi - done - [ "$addname" != "" ] && notify "新增 ${author} 自定义脚本" "${addname}" - -} - -delcron() { - delname="" - cronfiles=$(grep "$author" /ql/config/crontab.list | grep -v "^#" | perl -pe "s|.*ID=(.*) js (${author}_.*)\.*|\1:\2|") - for filename in $cronfiles; do - local id=$(echo "$1" | awk -F ":" '{print $1}') - local name=$(echo "$1" | awk -F ":" '{print $2}') - hasFile=$(cd ${diyscriptsdir}/${author}_${repo} && find . -name "$filename.js" | wc -l) - if [[ $hasFile != 0 ]]; then - del_cron_api "$id" - echo -e "删除失效脚本${name}." - delname="${delname}\n${author}_${filename}" - fi - done - [ "$delname" != "" ] && notify "删除 ${author} 失效脚本" "${delname}" -} - -. $ShellDir/shell/api.sh -get_token - -if [[ ${gitpullstatus} -eq 0 ]]; then - addnewcron - delcron -else - echo -e "$author 仓库更新失败了." - notify "自定义仓库更新失败" "$author" -fi - -exit 0 diff --git a/shell/git_pull.sh b/shell/git_pull.sh deleted file mode 100755 index 8083ad0f..00000000 --- a/shell/git_pull.sh +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/env bash - -ShellDir=${QL_DIR:-$( - cd $(dirname $0) - pwd -)} -[[ $QL_DIR ]] && ShellJs=js -LogDir=$ShellDir/log -DbDir=$ShellDir/db -ManualLogDir=$ShellDir/manual_log -ScriptsDir=$ShellDir/scripts -ConfigDir=$ShellDir/config -FileConf=$ConfigDir/config.sh -CookieConf=$ConfigDir/cookie.sh -AuthConf=$ConfigDir/auth.json -ExtraShell=$ConfigDir/extra.sh -FileConfSample=$ShellDir/sample/config.sh.sample -ListCronSample=$ShellDir/sample/crontab.list.sample -ListCronCurrent=$ConfigDir/crontab.list -ListCronRemote=$ScriptsDir/docker/crontab_list.sh -ListCurrentTask=$LogDir/task.list -ListRemoteTask=$LogDir/js.list -ListJsAdd=$LogDir/js-add.list -ListJsDrop=$LogDir/js-drop.list -ContentVersion=$ShellDir/version -ContentNewTask=$ShellDir/new_task -ContentDropTask=$ShellDir/drop_task -SendVersion=$ShellDir/send_version -isTermux=$ANDROID_RUNTIME_ROOT$ANDROID_ROOT -ShellURL=https://ghproxy.com/https://github.com/whyour/qinglong -ScriptsURL=https://ghproxy.com/https://github.com/gossh520/jd_scripts - -Import_Conf() { - if [ ! -s $FileConf ]; then - echo -e "复制一份 $FileConfSample 示例配置文件\n\n" - cp -fv $FileConfSample $FileConf - fi - if [ ! -s $ListCronCurrent ]; then - echo -e "复制一份 $ListCronSample 基础定时任务\n\n" - cp -fv $ListCronSample $ListCronCurrent - fi - [ -f $CookieConf ] && . $CookieConf - [ -f $FileConf ] && . $FileConf -} - -# 更新shell -Git_Pull_Shell() { - echo -e "更新shell...\n" - cd $ShellDir - git remote set-url origin $ShellURL - git fetch --all - ExitStatusShell=$? - git reset --hard origin/master - git pull -} - -Git_Pull_Shell_Next() { - if [[ $ExitStatusShell -eq 0 ]]; then - echo -e "更新shell成功...\n" - [ ! -d $ShellDir/node_modules ] && Npm_Install panel - [ -f $ShellDir/package.json ] && PanelDependNew=$(cat $ShellDir/package.json) - [[ "$PanelDependOld" != "$PanelDependNew" ]] && cd $ShellDir && Npm_Install panel - cp -f $FileConfSample $ConfigDir/config.sh.sample - Notify_Version - else - echo -e "更新shell失败,请检查原因...\n" - fi -} - -## npm install -Npm_Install() { - echo -e "检测到 $1 的依赖包有变化,运行 npm install...\n" - Npm_InstallSub - if [ $? -ne 0 ]; then - echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules 后再次尝试一遍..." - rm -rf node_modules - fi - echo - - if [ ! -d node_modules ]; then - echo -e "运行 npm install...\n" - Npm_InstallSub - if [ $? -ne 0 ]; then - echo -e "\nnpm install 运行不成功,自动删除 $1/node_modules...\n" - echo -e "请进入 $1 目录后手动运行 npm install...\n" - echo -e "3...\n" - sleep 1 - echo -e "2...\n" - sleep 1 - echo -e "1...\n" - sleep 1 - rm -rf node_modules - fi - fi -} - -## npm install 子程序,判断是否为安卓,判断是否安装有yarn -function Npm_InstallSub() { - if [ -n "$isTermux" ]; then - npm install --production --no-save --no-bin-links || npm install --production --no-bin-links --no-save --registry=https://registry.npm.taobao.org - elif ! type yarn >/dev/null 2>&1; then - npm install --production --no-save || npm install --production --no-save --registry=https://registry.npm.taobao.org - else - echo -e "检测到本机安装了 yarn,使用 yarn 替代 npm...\n" - yarn install --production --network-timeout 1000000000 || yarn install --production --registry=https://registry.npm.taobao.org --network-timeout 1000000000 - fi -} - -## 检测配置文件版本 -Notify_Version() { - ## 识别出两个文件的版本号 - VerConfSample=$(grep " Version: " $FileConfSample | perl -pe "s|.+v((\d+\.?){3})|\1|") - [ -f $FileConf ] && VerConf=$(grep " Version: " $FileConf | perl -pe "s|.+v((\d+\.?){3})|\1|") - - ## 删除旧的发送记录文件 - [ -f "$SendVersion" ] && [[ $(cat $SendVersion) != $VerConfSample ]] && rm -f $SendVersion - - ## 识别出更新日期和更新内容 - UpdateDate=$(grep " Date: " $FileConfSample | awk -F ": " '{print $2}') - UpdateContent=$(grep " Update Content: " $FileConfSample | awk -F ": " '{print $2}') - - ## 如果是今天,并且版本号不一致,则发送通知 - if [ -f $FileConf ] && [[ "$VerConf" != "$VerConfSample" ]] && [[ $UpdateDate == $(date "+%Y-%m-%d") ]]; then - if [ ! -f $SendVersion ]; then - notify "检测到配置文件config.sh.sample有更新" "更新日期: $UpdateDate\n当前版本: $VerConf\n新的版本: $VerConfSample\n更新内容: $UpdateContent\n更新说明: 如需使用新功能请对照config.sh.sample,将相关新参数手动增加到你自己的config.sh中,否则请无视本消息。本消息只在该新版本配置文件更新当天发送一次。" - fi - else - [ -f $ContentVersion ] && rm -f $ContentVersion - [ -f $SendVersion ] && rm -f $SendVersion - fi -} - -## 每天次数随机,更新时间随机,更新秒数随机,至少6次,至多12次,大部分为8-10次,符合正态分布。 -Random_Pull_Cron() { - if [[ $(date "+%-H") -le 2 ]]; then - RanMin=$(($RANDOM % 60)) - RanSleep=$(($RANDOM % 56)) - RanHourArray[0]=$(($RANDOM % 3)) - for ((i = 1; i < 14; i++)); do - j=$(($i - 1)) - tmp=$(($RANDOM % 3 + ${RanHourArray[j]} + 2)) - [[ $tmp -lt 24 ]] && RanHourArray[i]=$tmp || break - done - - RanHour=${RanHourArray[0]} - for ((i = 1; i < ${#RanHourArray[*]}; i++)); do - RanHour="$RanHour,${RanHourArray[i]}" - done - - perl -i -pe "s|.+(git_pull? .+git_pull\.log.*)|$RanMin $RanHour \* \* \* sleep $RanSleep && \1|" $ListCronCurrent - crontab $ListCronCurrent - fi -} - -## 克隆scripts -Git_Clone_Scripts() { - git clone -b master $ScriptsURL $ScriptsDir - ExitStatusScripts=$? -} - -## 更新scripts -Git_Pull_Scripts() { - if [ -d $ScriptsDir/.git ]; then - echo -e "更新scripts...\n" - cd $ScriptsDir - git remote set-url origin $ScriptsURL - git fetch --all - ExitStatusScripts=$? - git reset --hard origin/master - git pull - else - Git_Clone_Scripts - fi -} - -Git_Pull_Scripts_Next() { - if [[ $ExitStatusScripts -eq 0 ]]; then - echo -e "更新scripts成功...\n" - [ ! -d $ScriptsDir/node_modules ] && Npm_Install scripts - [ -f $ScriptsDir/package.json ] && ScriptsDependNew=$(cat $ScriptsDir/package.json) - [[ "$ScriptsDependOld" != "$ScriptsDependNew" ]] && cd $ScriptsDir && Npm_Install scripts - Diff_Cron - if [ -s $ListJsDrop ]; then - Output_ListJs $ListJsDrop "失效" - Del_Cron - fi - if [ -s $ListJsAdd ]; then - Output_ListJs $ListJsAdd "新" - Add_Cron - fi - else - echo -e "更新scripts失败,请检查原因...\n" - fi -} - -Diff_Cron() { - cat $ListCronRemote | grep -E "node.+j[drx]_\w+\.js" | perl -pe "s|.+(j[drx]_\w+)\.js.+|\1|" | sort -u >$ListRemoteTask - cat $ListCronCurrent | grep -E "$ShellJs j[drx]_\w+" | perl -pe "s|.*ID=(.*) $ShellJs (j[drx]_\w+)\.*|\2|" | sort -u >$ListCurrentTask - if [ -s $ListCurrentTask ]; then - grep -vwf $ListCurrentTask $ListRemoteTask >$ListJsAdd - else - cp -f $ListRemoteTask $ListJsAdd - fi - if [ -s $ListRemoteTask ]; then - grep -vwf $ListRemoteTask $ListCurrentTask >$ListJsDrop - else - cp -f $ListCurrentTask $ListJsDrop - fi -} - -Del_Cron() { - if [ $AutoDelCron == true ] && [ -s $ListJsDrop ]; then - echo -e "开始尝试自动删除定时任务如下:\n" - cat $ListJsDrop - echo - JsDrop=$(cat $ListJsDrop) - for Cron in $JsDrop; do - local id=$(cat $ListCronCurrent | grep -E "js $Cron$" | perl -pe "s|.*ID=(.*) js $Cron$|\1|") - del_cron_api "$id" - done - crontab $ListCronCurrent - echo -e "成功删除失效的脚本与定时任务\n" - notify "删除 lxk0301 失效脚本" "$JsDrop" - fi -} - -Add_Cron() { - if [ $AutoAddCron == true ] && [ -s $ListJsAdd ]; then - echo -e "开始尝试自动添加定时任务\n" - JsAdd=$(cat $ListJsAdd) - for Cron in $JsAdd; do - if [[ $Cron == jd_bean_sign ]]; then - local name=$(cat "$ScriptsDir/$Cron.js" | grep -E "new Env\(" | perl -pe "s|(^.+)new Env\(\'*\"*(.+?)'*\"*\).+|\2|") - add_cron_api "4 0,9 * * *" "$ShellJs $Cron" "$name" - else - local name=$(cat "$ScriptsDir/$Cron.js" | grep -E "new Env\(" | perl -pe "s|(^.+)new Env\(\'*\"*(.+?)'*\"*\).+|\2|") - local param=$(cat $ListCronRemote | grep -E "\/$Cron\." | perl -pe "s|(^.+) node */scripts/(j[drx]_\w+)\.js.+|\1\:$ShellJs \2|") - add_cron_api "$param:$name" - fi - done - - if [ $? -eq 0 ]; then - crontab $ListCronCurrent - echo -e "成功添加新的定时任务...\n" - notify "新增lxk0301脚本" "$JsAdd" - else - echo -e "添加新的定时任务出错,请手动添加...\n" - notify "尝试自动添加lxk0301以下新的定时任务出错,请手动添加:" "$JsAdd" - fi - fi -} - -## 输出定时任务变化 -Output_ListJs() { - local list=$1 - local type=$2 - if [ -s $list ]; then - echo -e "检测到有$type的定时任务:\n" - cat $list - echo - fi -} - -################################################################################################################################# -echo -e "\n--------------------------------------------------------------\n" -echo -n "系统时间:" -echo $(date "+%Y-%m-%d %H:%M:%S") -if [ "${TZ}" = "UTC" ]; then - echo -n "北京时间:" - echo $(date -d "8 hour" "+%Y-%m-%d %H:%M:%S") -fi -echo -e "\nJS脚本目录:$ScriptsDir\n" -echo -e "--------------------------------------------------------------\n" - -. $ShellDir/shell/api.sh -get_token - -Import_Conf - -# 更新shell -[ -f $ShellDir/package.json ] && PanelDependOld=$(cat $ShellDir/package.json) -Git_Pull_Shell -Git_Pull_Shell_Next - -## 更新scripts -[ -f $ScriptsDir/package.json ] && ScriptsDependOld=$(cat $ScriptsDir/package.json) -Git_Pull_Scripts -Git_Pull_Scripts_Next - -## 调用用户自定义的extra.sh -if [[ $EnableExtraShell == true ]]; then - if [ -f $ExtraShell ]; then - . $ExtraShell - else - echo -e "$ExtraShell 文件不存在,跳过执行DIY脚本...\n" - fi -fi diff --git a/shell/js.sh b/shell/js.sh deleted file mode 100755 index e369810e..00000000 --- a/shell/js.sh +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env bash - -## 路径 -ShellDir=${QL_DIR:-$(cd $(dirname $0); pwd)} -[[ ${QL_DIR} ]] && ShellJS=js -ScriptsDir=${ShellDir}/scripts -ConfigDir=${ShellDir}/config -FileConf=${ConfigDir}/config.sh -CookieConf=${ConfigDir}/cookie.sh -FileConfSample=${ShellDir}/sample/config.sh.sample -LogDir=${ShellDir}/log -ListScripts=($(cd ${ScriptsDir}; ls *.js | grep -E "j[drx]_")) -ListCron=${ConfigDir}/crontab.list -ListCronLxk=${ScriptsDir}/docker/crontab_list.sh -ListJs=${LogDir}/js.list - -## 导入config.sh -function Import_Conf { - if [ -f ${FileConf} ] - then - . ${CookieConf} - . ${FileConf} - if [[ ! -s ${CookieConf} ]]; then - echo -e "请先在Cookie管理中添加一条Cookie...\n" - exit 1 - fi - else - echo -e "配置文件 ${FileConf} 不存在,请先按教程配置好该文件...\n" - exit 1 - fi -} - -## 更新crontab -function Detect_Cron { - if [[ $(cat ${ListCron}) != $(crontab -l) ]]; then - crontab ${ListCron} - fi -} - -## 用户数量UserSum -function Count_UserSum { - UserSum=0 - for line in `cat $CookieConf` - do - ((UserSum++)) - eval Cookie${UserSum}="\"${line}\"" - done -} - -## 组合Cookie和互助码子程序 -function Combin_Sub { - CombinAll="" - if [[ ${AutoHelpOther} == true ]] && [[ $1 == ForOther* ]]; then - - ForOtherAll="" - MyName=$(echo $1 | perl -pe "s|ForOther|My|") - - for ((m=1; m<=${UserSum}; m++)); do - TmpA=${MyName}$m - TmpB=${!TmpA} - ForOtherAll="${ForOtherAll}@${TmpB}" - done - - for ((n=1; n<=${UserSum}; n++)); do - for num in ${TempBlockCookie}; do - [[ $n -eq $num ]] && continue 2 - done - CombinAll="${CombinAll}&${ForOtherAll}" - done - - else - for ((i=1; i<=${UserSum}; i++)); do - for num in ${TempBlockCookie}; do - [[ $i -eq $num ]] && continue 2 - done - Tmp1=$1$i - Tmp2=${!Tmp1} - CombinAll="${CombinAll}&${Tmp2}" - done - fi - - echo ${CombinAll} | perl -pe "{s|^&||; s|^@+||; s|&@|&|g; s|@+&|&|g; s|@+|@|g; s|@+$||}" -} - -## 组合Cookie、Token与互助码 -function Combin_All { - export JD_COOKIE=$(Combin_Sub Cookie) - export FRUITSHARECODES=$(Combin_Sub ForOtherFruit) - export PETSHARECODES=$(Combin_Sub ForOtherPet) - export PLANT_BEAN_SHARECODES=$(Combin_Sub ForOtherBean) - export DREAM_FACTORY_SHARE_CODES=$(Combin_Sub ForOtherDreamFactory) - export DDFACTORY_SHARECODES=$(Combin_Sub ForOtherJdFactory) - export JDZZ_SHARECODES=$(Combin_Sub ForOtherJdzz) - export JDJOY_SHARECODES=$(Combin_Sub ForOtherJoy) - export JXNC_SHARECODES=$(Combin_Sub ForOtherJxnc) - export JXNCTOKENS=$(Combin_Sub TokenJxnc) - export BOOKSHOP_SHARECODES=$(Combin_Sub ForOtherBookShop) - export JD_CASH_SHARECODES=$(Combin_Sub ForOtherCash) - export JDSGMH_SHARECODES=$(Combin_Sub ForOtherSgmh) - export JDCFD_SHARECODES=$(Combin_Sub ForOtherCfd) - export JDGLOBAL_SHARECODES=$(Combin_Sub ForOtherGlobal) -} - -## 转换JD_BEAN_SIGN_STOP_NOTIFY或JD_BEAN_SIGN_NOTIFY_SIMPLE -function Trans_JD_BEAN_SIGN_NOTIFY { - case ${NotifyBeanSign} in - 0) - export JD_BEAN_SIGN_STOP_NOTIFY="true" - ;; - 1) - export JD_BEAN_SIGN_NOTIFY_SIMPLE="true" - ;; - esac -} - -## 转换UN_SUBSCRIBES -function Trans_UN_SUBSCRIBES { - export UN_SUBSCRIBES="${goodPageSize}\n${shopPageSize}\n${jdUnsubscribeStopGoods}\n${jdUnsubscribeStopShop}" -} - -## 申明全部变量 -function Set_Env { - Count_UserSum - Combin_All - Trans_JD_BEAN_SIGN_NOTIFY - Trans_UN_SUBSCRIBES -} - -## 随机延迟 -function Random_Delay { - if [[ -n ${RandomDelay} ]] && [[ ${RandomDelay} -gt 0 ]]; then - CurMin=$(date "+%-M") - if [[ ${CurMin} -gt 2 && ${CurMin} -lt 30 ]] || [[ ${CurMin} -gt 31 && ${CurMin} -lt 59 ]]; then - CurDelay=$((${RANDOM} % ${RandomDelay} + 1)) - echo -e "\n命令未添加 \"now\",随机延迟 ${CurDelay} 秒后再执行任务,如需立即终止,请按 CTRL+C...\n" - sleep ${CurDelay} - fi - fi -} - -## 使用说明 -function Help { - echo -e "本脚本的用法为:" - echo -e "1. ${ShellJS} xxx # 如果设置了随机延迟并且当时时间不在0-2、30-31、59分内,将随机延迟一定秒数" - echo -e "2. ${ShellJS} xxx now # 无论是否设置了随机延迟,均立即运行" - echo -e "3. ${ShellJS} runall # 运行所有非挂机脚本,非常耗时" - echo -e "4. ${ShellJS} hangup # 重启挂机程序" - echo -e "5. ${ShellJS} resetpwd # 重置控制面板用户名和密码" - echo -e "\n针对用法1、用法2中的\"xxx\",可以不输入后缀\".js\",另外,如果前缀是\"jd_\"的话前缀也可以省略。" - echo -e "当前有以下脚本可以运行(仅列出以jd_、jr_、jx_开头的脚本):" - cd ${ScriptsDir} - for ((i=0; i<${#ListScripts[*]}; i++)); do - Name=$(grep "new Env" ${ListScripts[i]} | awk -F "'|\"" '{print $2}') - echo -e "$(($i + 1)).${Name}:${ListScripts[i]}" - done -} - -## nohup -function Run_Nohup { - if [[ $(ps -ef | grep "${js}" | grep -v "grep") != "" ]]; then - ps -ef | grep "${js}" | grep -v "grep" | awk '{print $2}' | xargs kill -9 - fi - [ ! -d ${LogDir}/${js} ] && mkdir -p ${LogDir}/${js} - LogTime=$(date "+%Y-%m-%d-%H-%M-%S") - LogFile="${LogDir}/${js}/${LogTime}.log" - nohup node ${js}.js > ${LogFile} & -} - -## 运行挂机脚本 -function Run_HangUp { - HangUpJs="jd_crazy_joy_coin" - cd ${ScriptsDir} - for js in ${HangUpJs}; do - Import_Conf ${js} && Set_Env - if type pm2 >/dev/null 2>&1; then - pm2 stop ${js}.js 2>/dev/null - pm2 flush - pm2 start -a ${js}.js --watch "${ScriptsDir}/${js}.js" --name="${js}" - else - Run_Nohup >/dev/null 2>&1 - fi - done -} - -## 重置密码 -function Reset_Pwd { - cp -f ${ShellDir}/sample/auth.json ${ConfigDir}/auth.json - echo -e "控制面板重置成功,用户名:admin,密码:adminadmin\n" -} - -## 一次性运行所有脚本 -function Run_All { - if [ ! -f ${ListJs} ]; then - cat ${ListCronLxk} | grep -E "j[drx]_\w+\.js" | perl -pe "s|.+(j[drx]_\w+)\.js.+|\1|" | sort -u > ${ListJs} - fi - echo -e "\n==================== 开始运行所有非挂机脚本 ====================\n" - echo -e "请注意:本过程将非常非常耗时,一个账号可能长达几小时,账号越多耗时越长,如果是手动运行,退出终端也将终止运行。\n" - echo -e "倒计时5秒...\n" - for ((sec=5; sec>0; sec--)); do - echo -e "$sec...\n" - sleep 1 - done - for file in $(cat ${ListJs}); do - echo -e "==================== 运行 $file.js 脚本 ====================\n" - ${ShellJS} $file now - done -} - -## 正常运行单个脚本 -function Run_Normal { - Import_Conf $1 && Detect_Cron && Set_Env - - FileNameTmp1=$(echo $1 | perl -pe "s|\.js||") - FileNameTmp2=$(echo $1 | perl -pe "{s|jd_||; s|\.js||; s|^|jd_|}") - SeekDir="${ScriptsDir} ${ScriptsDir}/backUp ${ConfigDir}" - FileName="" - WhichDir="" - - for dir in ${SeekDir} - do - if [ -f ${dir}/${FileNameTmp1}.js ]; then - FileName=${FileNameTmp1} - WhichDir=${dir} - break - elif [ -f ${dir}/${FileNameTmp2}.js ]; then - FileName=${FileNameTmp2} - WhichDir=${dir} - break - fi - done - - if [ -n "${FileName}" ] && [ -n "${WhichDir}" ] - then - [ $# -eq 1 ] && Random_Delay - LogTime=$(date "+%Y-%m-%d-%H-%M-%S") - LogFile="${LogDir}/${FileName}/${LogTime}.log" - [ ! -d ${LogDir}/${FileName} ] && mkdir -p ${LogDir}/${FileName} - cd ${WhichDir} - node ${FileName}.js 2>&1 | tee ${LogFile} - else - echo -e "\n在${ScriptsDir}、${ScriptsDir}/backUp、${ConfigDir}三个目录下均未检测到 $1 脚本的存在,请确认...\n" - Help - fi -} - -## 命令检测 -case $# in - 0) - echo - Help - ;; - 1) - case $1 in - hangup) - Run_HangUp - ;; - resetpwd) - Reset_Pwd - ;; - runall) - Run_All - ;; - *) - Run_Normal $1 - ;; - esac - ;; - 2) - case $2 in - now) - Run_Normal $1 $2 - ;; - *) - echo -e "\n命令输入错误...\n" - Help - ;; - esac - ;; - *) - echo -e "\n命令过多...\n" - Help - ;; -esac \ No newline at end of file diff --git a/shell/notify.js b/shell/notify.js index c7027b1f..b70fe673 100644 --- a/shell/notify.js +++ b/shell/notify.js @@ -1,4 +1,4 @@ -const notify = require('/ql/scripts/sendNotify'); +const notify = require('/ql/scripts/sendNotify.js'); const title = process.argv[2]; const content = process.argv[3]; diff --git a/shell/notify.sh b/shell/notify.sh index cf05ccd0..ae669f70 100755 --- a/shell/notify.sh +++ b/shell/notify.sh @@ -1,7 +1,6 @@ #!/bin/bash #author:spark thanks to: https://github.com/sparkssssssss/scripts -. /ql/config/cookie.sh . /ql/config/config.sh title=$(echo $1|sed 's/-/_/g') msg=$(echo -e $2) diff --git a/shell/rebuild.sh b/shell/rebuild.sh deleted file mode 100755 index 8dea6af0..00000000 --- a/shell/rebuild.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -ShellDir=${QL_DIR:-$(cd $(dirname $0); pwd)} - -echo -e "更新shell...\n" -cd ${ShellDir} -git fetch --all -git pull -echo -e "更新shell完成...\n" - -echo -e "重新build...\n" -yarn install --network-timeout 1000000000 || yarn install --registry=https://registry.npm.taobao.org --network-timeout 1000000000 -yarn build -yarn build-back -yarn cache clean -echo -e "重新build完成...\n" - -echo -e "重启服务...\n" - -PIDS=`ps -ef|grep "app.js"|grep -v grep` -if [ "$PIDS" != "" ]; then - pm2 restart panel -else - pm2 start ${QL_DIR}/build/app.js -n panel -fi - -nginx -s reload - -echo -e "重启服务完成...\n" \ No newline at end of file diff --git a/shell/rm_log.sh b/shell/rm_log.sh deleted file mode 100755 index 6cb63d74..00000000 --- a/shell/rm_log.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -## 判断环境 -ShellDir=${QL_DIR:-$(cd $(dirname $0); pwd)} -LogDir=${ShellDir}/log - -## 导入配置文件 -. ${ShellDir}/config/cookie.sh -. ${ShellDir}/config/config.sh - -## 删除运行js脚本的旧日志 -function Rm_JsLog { - LogFileList=$(ls -l ${LogDir}/*/*.log | awk '{print $9}') - for log in ${LogFileList} - do - LogDate=$(echo ${log} | awk -F "/" '{print $NF}' | cut -c1-10) #文件名比文件属性获得的日期要可靠 - if [[ $(uname -s) == Darwin ]] - then - DiffTime=$(($(date +%s) - $(date -j -f "%Y-%m-%d" "${LogDate}" +%s))) - else - DiffTime=$(($(date +%s) - $(date +%s -d "${LogDate}"))) - fi - [ ${DiffTime} -gt $((${RmLogDaysAgo} * 86400)) ] && rm -vf ${log} - done -} - -## 删除git_pull.sh的运行日志 -function Rm_GitPullLog { - if [[ $(uname -s) == Darwin ]] - then - DateDelLog=$(date -v-${RmLogDaysAgo}d "+%Y-%m-%d") - else - Stmp=$(($(date "+%s") - 86400 * ${RmLogDaysAgo})) - DateDelLog=$(date -d "@${Stmp}" "+%Y-%m-%d") - fi - LineEndGitPull=$[$(cat ${LogDir}/git_pull.log | grep -n "${DateDelLog} " | head -1 | awk -F ":" '{print $1}') - 3] - [ ${LineEndGitPull} -gt 0 ] && perl -i -ne "{print unless 1 .. ${LineEndGitPull} }" ${LogDir}/git_pull.log -} - -## 删除空文件夹 -function Rm_EmptyDir { - cd ${LogDir} - for dir in $(ls) - do - if [ -d ${dir} ] && [[ $(ls ${dir}) == "" ]]; then - rm -rf ${dir} - fi - done -} - -## 运行 -if [ -n "${RmLogDaysAgo}" ]; then - echo -e "查找旧日志文件中...\n" - Rm_JsLog - Rm_GitPullLog - Rm_EmptyDir - echo -e "删除旧日志执行完毕\n" -fi diff --git a/shell/rmlog.sh b/shell/rmlog.sh new file mode 100755 index 00000000..d64359f8 --- /dev/null +++ b/shell/rmlog.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +## 导入通用变量与函数 +dir_shell=/ql/shell +. $dir_shell/share.sh + +days=$1 + +## 删除运行js脚本的旧日志 +remove_js_log() { + local log_full_path_list=$(ls -l $dir_log/*/*.log | awk '{print $9}') + local diff_time + for log in $log_full_path_list; do + local log_date=$(echo $log | awk -F "/" '{print $NF}' | cut -c1-10) #文件名比文件属性获得的日期要可靠 + if [[ $is_macos -eq 1 ]]; then + diff_time=$(($(date +%s) - $(date -j -f "%Y-%m-%d" "$log_date" +%s))) + else + diff_time=$(($(date +%s) - $(date +%s -d "$log_date"))) + fi + [[ $diff_time -gt $((${days} * 86400)) ]] && rm -vf $log + done +} + +## 删除空文件夹 +remove_empty_dir() { + cd $dir_log + for dir in $(ls); do + if [ -d $dir ] && [[ -z $(ls $dir) ]]; then + rm -rf $dir + fi + done +} + +## 运行 +if [[ ${days} ]]; then + echo -e "查找旧日志文件中...\n" + remove_js_log + remove_empty_dir + echo -e "删除旧日志执行完毕\n" +fi diff --git a/shell/share.sh b/shell/share.sh new file mode 100755 index 00000000..ea310b9b --- /dev/null +++ b/shell/share.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash + +## 目录 +dir_root=/ql +dir_shell=$dir_root/shell +dir_sample=$dir_root/sample +dir_config=$dir_root/config +dir_scripts=$dir_root/scripts +dir_repo=$dir_root/repo +dir_raw=$dir_root/raw +dir_log=$dir_root/log +dir_db=$dir_root/db +dir_manual_log=$dir_root/manual_log +dir_list_tmp=$dir_log/.tmp +dir_code=$dir_log/code +dir_update_log=$dir_log/update + +## 文件 +file_config_sample=$dir_sample/config.sample.sh +file_cookie=$dir_config/cookie.sh +file_sharecode=$dir_config/sharecode.sh +file_config_user=$dir_config/config.sh +file_auth_sample=$dir_sample/auth.sample.json +file_auth_user=$dir_config/auth.json +file_extra_shell=$dir_config/extra.sh +file_notify_js_sample=$dir_sample/notify.js +file_notify_py_sample=$dir_sample/notify.py +file_notify_py=$dir_scripts/notify.py +file_notify_js=$dir_scripts/sendNotify.js + +## 清单文件 +list_crontab_user=$dir_config/crontab.list +list_crontab_sample=$dir_sample/crontab.sample.list +list_own_scripts=$dir_list_tmp/own_scripts.list +list_own_user=$dir_list_tmp/own_user.list +list_own_add=$dir_list_tmp/own_add.list +list_own_drop=$dir_list_tmp/own_drop.list + +## 软连接及其原始文件对应关系 +link_name=( + task + ql + notify +) +original_name=( + task.sh + update.sh + notify.sh +) + +## 导入配置文件 +import_config() { + [ -f $file_config_user ] && . $file_config_user + user_sum=0 + for line in $(cat $file_cookie); do + let user_sum+=1 + eval Cookie${user_sum}="\"$line\"" + done + + command_timeout_time=${CommandTimeoutTime:-"1h"} +} + +## 创建目录,$1:目录的绝对路径 +make_dir() { + local dir=$1 + if [[ ! -d $dir ]]; then + mkdir -p $dir + fi +} + +## 检测termux +detect_termux() { + if [[ ${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} ]] || [[ $PATH == *com.termux* ]]; then + is_termux=1 + else + is_termux=0 + fi +} + +## 检测macos +detect_macos() { + [[ $(uname -s) == Darwin ]] && is_macos=1 || is_macos=0 +} + +## 生成随机数,$1:用来求余的数字 +gen_random_num() { + local divi=$1 + echo $((${RANDOM} % $divi)) +} + +## 创建软连接的子函数,$1:软连接文件路径,$2:要连接的对象 +link_shell_sub() { + local link_path="$1" + local original_path="$2" + if [ ! -L $link_path ] || [[ $(readlink -f $link_path) != $original_path ]]; then + rm -f $link_path 2>/dev/null + ln -sf $original_path $link_path + fi +} + +## 创建软连接 +link_shell() { + if [[ $is_termux -eq 1 ]]; then + local path="/data/data/com.termux/files/usr/bin/" + elif [[ $PATH == */usr/local/bin* ]] && [ -d /usr/local/bin ]; then + local path="/usr/local/bin/" + else + local path="" + echo -e "脚本功能受限,请自行添加命令的软连接...\n" + fi + if [[ $path ]]; then + for ((i = 0; i < ${#link_name[*]}; i++)); do + link_shell_sub "$path${link_name[i]}" "$dir_shell/${original_name[i]}" + done + fi +} + +## 定义各命令 +define_cmd() { + local cmd_prefix cmd_suffix + if type task >/dev/null 2>&1; then + cmd_suffix="" + if [ -x "$dir_shell/task.sh" ]; then + cmd_prefix="" + else + cmd_prefix="bash " + fi + else + cmd_suffix=".sh" + if [ -x "$dir_shell/task.sh" ]; then + cmd_prefix="$dir_shell/" + else + cmd_prefix="bash $dir_shell/" + fi + fi + for ((i = 0; i < ${#link_name[*]}; i++)); do + export cmd_${link_name[i]}="${cmd_prefix}${link_name[i]}${cmd_suffix}" + done +} + +## 修复配置文件 +fix_config() { + make_dir $dir_config + make_dir $dir_log + make_dir $dir_db + make_dir $dir_manual_log + make_dir $dir_scripts + make_dir $dir_list_tmp + make_dir $dir_repo + make_dir $dir_raw + make_dir $dir_update_log + + if [ ! -s $file_config_user ]; then + echo -e "复制一份 $file_config_sample 为 $file_config_user,随后请按注释编辑你的配置文件:$file_config_user\n" + cp -fv $file_config_sample $file_config_user + echo + fi + + if [ ! -f $file_cookie ]; then + echo -e "检测到config配置目录下不存在cookie.sh,创建一个空文件用于初始化...\n" + touch $file_cookie + echo + fi + + if [ ! -s $file_auth_user ]; then + echo -e "复制一份 $file_auth_sample 为 $file_auth_user\n" + cp -fv $file_auth_sample $file_auth_user + echo + fi + + if [ ! -s $file_notify_py ]; then + echo -e "复制一份 $file_notify_py_sample 为 $file_notify_py\n" + cp -fv $file_notify_py_sample $file_notify_py + echo + fi + + if [ ! -s $file_notify_js ]; then + echo -e "复制一份 $file_notify_js_sample 为 $file_notify_js\n" + cp -fv $file_notify_js_sample $file_notify_js + echo + fi + + if [ -s /etc/nginx/conf.d/default.conf ]; then + echo -e "检测到默认nginx配置文件,删除...\n" + rm -f /etc/nginx/conf.d/default.conf + echo + fi +} + +## npm install 子程序,判断是否为安卓,判断是否安装有yarn +npm_install_sub() { + if [ $is_termux -eq 1 ]; then + npm install --production --no-save --no-bin-links --registry=https://registry.npm.taobao.org || npm install --production --no-bin-links --no-save + elif ! type yarn >/dev/null 2>&1; then + npm install --production --no-save --registry=https://registry.npm.taobao.org || npm install --production --no-save + else + echo -e "检测到本机安装了 yarn,使用 yarn 替代 npm...\n" + yarn install --production --network-timeout 1000000000 --registry=https://registry.npm.taobao.org || yarn install --production --network-timeout 1000000000 + fi +} + +## npm install,$1:package.json文件所在路径 +npm_install_1() { + local dir_current=$(pwd) + local dir_work=$1 + + cd $dir_work + echo -e "运行 npm install...\n" + npm_install_sub + [[ $? -ne 0 ]] && echo -e "\nnpm install 运行不成功,请进入 $dir_work 目录后手动运行 npm install...\n" + cd $dir_current +} + +npm_install_2() { + local dir_current=$(pwd) + local dir_work=$1 + + cd $dir_work + echo -e "检测到 $dir_work 的依赖包有变化,运行 npm install...\n" + npm_install_sub + if [[ $? -ne 0 ]]; then + echo -e "\n安装 $dir_work 的依赖包运行不成功,再次尝试一遍...\n" + npm_install_1 $dir_work + fi + cd $dir_current +} + +## 比对两个文件,$1比$2新时,将$1复制为$2 +diff_and_copy() { + local copy_source=$1 + local copy_to=$2 + if [ ! -s $copy_to ] || [[ $(diff $copy_source $copy_to) ]]; then + cp -f $copy_source $copy_to + fi +} + +## 更新依赖 +update_depend() { + local dir_current=$(pwd) + + if [ ! -s $dir_scripts/package.json ] || [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then + cp -f $dir_sample/package.json $dir_scripts/package.json + npm_install_2 $dir_scripts + fi + + if [ ! -s $dir_scripts/requirements.txt ] || [[ $(diff $dir_sample/requirements.txt $dir_scripts/requirements.txt) ]]; then + cp -f $dir_sample/requirements.txt $dir_scripts/requirements.txt + cd $dir_scripts + pip3 install -r $dir_scripts/requirements.txt + fi + + cd $dir_current +} + +## 导入配置文件,检测平台,创建软连接,识别命令,修复配置文件 +detect_termux +detect_macos +define_cmd +fix_config +import_config diff --git a/shell/task.sh b/shell/task.sh new file mode 100755 index 00000000..482488be --- /dev/null +++ b/shell/task.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash + +## 导入通用变量与函数 +dir_shell=/ql/shell +. $dir_shell/share.sh + +## 组合Cookie和互助码子程序,$1:要组合的内容 +combine_sub() { + local what_combine=$1 + local combined_all="" + local tmp1 tmp2 + for ((i = 1; i <= $user_sum; i++)); do + local tmp1=$what_combine$i + local tmp2=${!tmp1} + combined_all="$combined_all&$tmp2" + done + echo $combined_all | perl -pe "{s|^&||; s|^@+||; s|&@|&|g; s|@+&|&|g; s|@+|@|g; s|@+$||}" +} + +## 正常依次运行时,组合所有账号的Cookie与互助码 +combine_all() { + for ((i = 0; i < ${#env_name[*]}; i++)); do + export ${env_name[i]}=$(combine_sub ${var_name[i]}) + done +} + +## 并发运行时,直接申明每个账号的Cookie与互助码,$1:用户Cookie编号 +combine_one() { + local user_num=$1 + for ((i = 0; i < ${#env_name[*]}; i++)); do + local tmp=${var_name[i]}$user_num + export ${env_name[i]}=${!tmp} + done +} + +## 选择python3还是node +define_program() { + local p1=$1 + if [[ $p1 == *.js ]]; then + which_program="node" + elif [[ $p1 == *.py ]]; then + which_program="python3" + else + which_program="" + fi +} + +random_delay() { + local random_delay_max=$RandomDelay + if [[ $random_delay_max ]] && [[ $random_delay_max -gt 0 ]]; then + local current_min=$(date "+%-M") + if [[ $current_min -gt 2 && $current_min -lt 30 ]] || [[ $current_min -gt 31 && $current_min -lt 59 ]]; then + delay_second=$(($(gen_random_num $random_delay_max) + 1)) + echo -e "\n命令未添加 \"now\",随机延迟 $delay_second 秒后再执行任务,如需立即终止,请按 CTRL+C...\n" + sleep $delay_second + fi + fi +} + +## scripts目录下所有可运行脚本数组 +gen_array_scripts() { + local dir_current=$(pwd) + local i="-1" + cd $dir_scripts + for file in $(ls); do + if [ -f $file ] && [[ $file == *.js && $file != sendNotify.js ]]; then + let i++ + array_scripts[i]=$(echo "$file" | perl -pe "s|$dir_scripts/||g") + array_scripts_name[i]=$(grep "new Env" $file | awk -F "'|\"" '{print $2}' | head -1) + [[ -z ${array_scripts_name[i]} ]] && array_scripts_name[i]="<未识别出活动名称>" + fi + done + cd $dir_current +} + +## 使用说明 +usage() { + define_cmd + gen_array_scripts + echo -e "task命令运行本程序自动添加进crontab的脚本,需要输入脚本的绝对路径或去掉 “$dir_scripts/” 目录后的相对路径(定时任务中请写作相对路径),用法为:" + echo -e "1.$cmd_task # 依次执行,如果设置了随机延迟并且当时时间不在0-2、30-31、59分内,将随机延迟一定秒数" + echo -e "2.$cmd_task now # 依次执行,无论是否设置了随机延迟,均立即运行,前台会输出日志,同时记录在日志文件中" + echo -e "3.$cmd_task conc # 并发执行,无论是否设置了随机延迟,均立即运行,前台不产生日志,直接记录在日志文件中" + if [[ ${#array_scripts[*]} -gt 0 ]]; then + echo -e "\n当前有以下脚本可以运行:" + for ((i = 0; i < ${#array_scripts[*]}; i++)); do + echo -e "$(($i + 1)). ${array_scripts_name[i]}:${array_scripts[i]}" + done + else + echo -e "\n暂无脚本可以执行" + fi +} + +## run nohup,$1:文件名,不含路径,带后缀 +run_nohup() { + local file_name=$1 + nohup node $file_name &>$log_path & +} + +## 正常运行单个脚本,$1:传入参数 +run_normal() { + local p1=$1 + cd $dir_scripts + define_program "$p1" + if [[ $p1 == *.js ]]; then + if [[ $AutoHelpOther == true ]] && [[ $(ls $dir_code) ]]; then + local latest_log=$(ls -r $dir_code | head -1) + . $dir_code/$latest_log + fi + if [[ $# -eq 1 ]]; then + random_delay + fi + fi + combine_all + log_time=$(date "+%Y-%m-%d-%H-%M-%S") + log_dir="$dir_log/${p1%%.*}" + log_path="$log_dir/$log_time.log" + make_dir "$log_dir" + timeout $command_timeout_time $which_program $p1 2>&1 | tee $log_path +} + +## 并发执行,因为是并发,所以日志只能直接记录在日志文件中(日志文件以Cookie编号结尾),前台执行并发跑时不会输出日志 +## 并发执行时,设定的 RandomDelay 不会生效,即所有任务立即执行 +run_concurrent() { + local p1=$1 + cd $dir_scripts + define_program "$p1" + log_dir="$dir_log/${p1%%.*}" + make_dir $log_dir + log_time=$(date "+%Y-%m-%d-%H-%M-%S.%N") + echo -e "\n各账号间已经在后台开始并发执行,前台不输入日志,日志直接写入文件中。\n" + for ((user_num = 1; user_num <= $user_sum; user_num++)); do + combine_one $user_num + log_path="$log_dir/${log_time}_${user_num}.log" + timeout $command_timeout_time $which_program $p1 &>$log_path & + done +} + +## 运行其他命令 +run_else() { + local log_time=$(date "+%Y-%m-%d-%H-%M-%S") + local log_dir="$dir_log/$1" + local log_path="$log_dir/$log_time.log" + make_dir "$log_dir" + timeout $command_timeout_time "$@" 2>&1 | tee $log_path +} + +## 命令检测 +main() { + case $# in + 0) + echo + usage + ;; + 1) + run_normal $1 + ;; + 2) + case $2 in + now) + run_normal $1 $2 + ;; + conc) + run_concurrent $1 $2 + ;; + *) + run_else "$@" + ;; + esac + ;; + *) + run_else "$@" + ;; + esac +} + +main "$@" + +exit 0 diff --git a/shell/update.sh b/shell/update.sh new file mode 100755 index 00000000..0cca5f39 --- /dev/null +++ b/shell/update.sh @@ -0,0 +1,400 @@ +#!/usr/bin/env bash + +# 导入通用变量与函数 +dir_shell=/ql/shell +. $dir_shell/share.sh +. $dir_shell/api.sh + +send_mark=$dir_shell/send_mark + +get_token + +## 重置仓库remote url,docker专用,$1:要重置的目录,$2:要重置为的网址 +reset_romote_url() { + local dir_current=$(pwd) + local dir_work=$1 + local url=$2 + + if [ -d "$dir_work/.git" ]; then + cd $dir_work + git remote set-url origin $url >/dev/null + git reset --hard >/dev/null + cd $dir_current + fi +} + +## 克隆脚本,$1:仓库地址,$2:仓库保存路径,$3:分支(可省略) +git_clone_scripts() { + local url=$1 + local dir=$2 + local branch=$3 + [[ $branch ]] && local cmd="-b $branch " + echo -e "开始克隆仓库 $url 到 $dir\n" + git clone $cmd $url $dir + exit_status=$? +} + +## 更新脚本,$1:仓库保存路径 +git_pull_scripts() { + local dir_current=$(pwd) + local dir_work=$1 + cd $dir_work + echo -e "开始更新仓库:$dir_work\n" + git fetch --all + exit_status=$? + git reset --hard + git pull + cd $dir_current +} + +## 检测cron的差异,$1:脚本清单文件路径,$2:cron任务清单文件路径,$3:增加任务清单文件路径,$4:删除任务清单文件路径 +diff_cron() { + local list_scripts="$1" + local list_task="$2" + local list_add="$3" + local list_drop="$4" + if [ -s $list_task ]; then + grep -vwf $list_task $list_scripts >$list_add + elif [ ! -s $list_task ] && [ -s $list_scripts ]; then + cp -f $list_scripts $list_add + fi + if [ -s $list_scripts ]; then + grep -vwf $list_scripts $list_task >$list_drop + else + cp -f $list_task $list_drop + fi +} + +## 检测配置文件版本 +detect_config_version() { + ## 识别出两个文件的版本号 + ver_config_sample=$(grep " Version: " $file_config_sample | perl -pe "s|.+v((\d+\.?){3})|\1|") + [ -f $file_config_user ] && ver_config_user=$(grep " Version: " $file_config_user | perl -pe "s|.+v((\d+\.?){3})|\1|") + + ## 删除旧的发送记录文件 + [ -f $send_mark ] && [[ $(cat $send_mark) != $ver_config_sample ]] && rm -f $send_mark + + ## 识别出更新日期和更新内容 + update_date=$(grep " Date: " $file_config_sample | awk -F ": " '{print $2}') + update_content=$(grep " Update Content: " $file_config_sample | awk -F ": " '{print $2}') + + ## 如果是今天,并且版本号不一致,则发送通知 + if [ -f $file_config_user ] && [[ $ver_config_user != $ver_config_sample ]] && [[ $update_date == $(date "+%Y-%m-%d") ]]; then + if [ ! -f $send_mark ]; then + local notify_title="配置文件更新通知" + local notify_content="更新日期: $update_date\n用户版本: $ver_config_user\n新的版本: $ver_config_sample\n更新内容: $update_content\n更新说明: 如需使用新功能请对照config.sample.sh,将相关新参数手动增加到你自己的config.sh中,否则请无视本消息。本消息只在该新版本配置文件更新当天发送一次。\n" + echo -e $notify_content + notify "$notify_title" "$notify_content" + [[ $? -eq 0 ]] && echo $ver_config_sample >$send_mark + fi + else + [ -f $send_mark ] && rm -f $send_mark + fi +} + +## 输出是否有新的或失效的定时任务,$1:新的或失效的任务清单文件路径,$2:新/失效 +output_list_add_drop() { + local list=$1 + local type=$2 + if [ -s $list ]; then + echo -e "检测到有$type的定时任务:\n" + cat $list + echo + fi +} + +## 自动删除失效的脚本与定时任务,需要:1.AutoDelCron 设置为 true;2.正常更新js脚本,没有报错;3.存在失效任务 +## $1:失效任务清单文件路径 +del_cron() { + local list_drop=$1 + local author=$2 + local detail="" + local ids="" + echo -e "开始尝试自动删除失效的定时任务...\n" + for cron in $(cat $list_drop); do + local id=$(cat $list_crontab_user | grep -E "$cmd_task $cron$" | perl -pe "s|.*ID=(.*) $cmd_task $cron$|\1|" | xargs | sed 's/ /","/g') + if [[ $ids ]]; then + ids="$ids,\"$id\"" + else + ids="\"$id\"" + fi + cron_file="$dir_scripts/${cron}" + if [[ -f $cron_file ]]; then + cron_name=$(grep "new Env" $cron_file | awk -F "'|\"" '{print $2}' | head -1) + fi + [[ -z $cron_name ]] && cron_name="$cron" + detail="${detail}\n${cron_name}" + rm -f $cron_file + done + result=$(del_cron_api "$ids") + detail="${result}\n\n${detail}" + notify "删除失效任务通知" "$detail" +} + +## 自动增加定时任务,需要:1.AutoAddCron 设置为 true;2.正常更新js脚本,没有报错;3.存在新任务;4.crontab.list存在并且不为空 +## $1:新任务清单文件路径 +add_cron() { + local list_add=$1 + local author=$2 + echo -e "开始尝试自动添加定时任务...\n" + local detail="" + cd $dir_scripts + for file in $(cat $list_add); do + local file_name=${file/${author}\_/} + if [ -f $file ]; then + cron_line=$( + perl -ne "{ + print if /.*([\d\*]*[\*-\/,\d]*[\d\*] ){4,5}[\d\*]*[\*-\/,\d]*[\d\*]( |,|\").*$file_name/ + }" $file | + perl -pe "{ + s|[^\d\*]*(([\d\*]*[\*-\/,\d]*[\d\*] ){4,5}[\d\*]*[\*-\/,\d]*[\d\*])( \|,\|\").*/?$file_name.*|\1|g; + s| | |g + }" | sort -u | head -1 + ) + cron_name=$(grep "new Env" $file | awk -F "'|\"" '{print $2}' | head -1) + [[ -z $cron_name ]] && cron_name="$file_name" + [[ -z $cron_line ]] && cron_line="0 6 * * *" + result=$(add_cron_api "$cron_line:$cmd_task $file:$cron_name") + echo -e "$result" + detail="${detail}\n${result}" + fi + done + notify "新增任务通知" "$detail" +} + +## 更新仓库 +update_repo() { + echo -e "--------------------------------------------------------------\n" + local url="$1" + local path="$2" + local blackword="$3" + local dependence="$4" + local urlTmp="${url%*/}" + local repoTmp="${urlTmp##*/}" + local repo="${repoTmp%.*}" + local tmp="${url%/*}" + local authorTmp1="${tmp##*/}" + local authorTmp2="${authorTmp1##*:}" + local author="${authorTmp2##*.}" + + local repo_path="${dir_repo}/${author}_${repo}" + if [ -d ${repo_path}/.git ]; then + reset_romote_url ${repo_path} ${url} + git_pull_scripts ${repo_path} + else + git_clone_scripts ${url} ${repo_path} + fi + if [[ $exit_status -eq 0 ]]; then + echo -e "\n更新${repo_path}成功...\n" + else + echo -e "\n更新${repo_path}失败,请检查原因...\n" + fi + + diff_scripts $repo_path $author $path $blackword $dependence +} + +## 更新所有 raw 文件 +update_raw() { + echo -e "--------------------------------------------------------------\n" + local raw_url="$1" + raw_file_name=$(echo ${raw_url} | awk -F "/" '{print $NF}') + echo -e "开始下载:${raw_url} \n\n保存路径:$dir_raw/${raw_file_name}\n" + wget -q --no-check-certificate -O "$dir_raw/${raw_file_name}.new" ${raw_url} + if [[ $? -eq 0 ]]; then + mv "$dir_raw/${raw_file_name}.new" "$dir_raw/${raw_file_name}" + echo -e "下载 ${raw_file_name} 成功...\n" + cd $dir_raw + local filename="raw_${raw_file_name}" + local cron_id=$(cat $list_crontab_user | grep -E "$cmd_task $filename$" | perl -pe "s|.*ID=(.*) $cmd_task $filename$|\1|") + cp -f $raw_file_name $dir_scripts/${filename} + cron_line=$( + perl -ne "{ + print if /.*([\d\*]*[\*-\/,\d]*[\d\*] ){4,5}[\d\*]*[\*-\/,\d]*[\d\*]( |,|\").*$raw_file_name/ + }" $raw_file_name | + perl -pe "{ + s|[^\d\*]*(([\d\*]*[\*-\/,\d]*[\d\*] ){4,5}[\d\*]*[\*-\/,\d]*[\d\*])( \|,\|\").*/?$raw_file_name.*|\1|g; + s| | |g + }" | sort -u | head -1 + ) + cron_name=$(grep "new Env" $raw_file_name | awk -F "'|\"" '{print $2}' | head -1) + [[ -z $cron_name ]] && cron_name="$raw_file_name" + [[ -z $cron_line ]] && cron_line="0 6 * * *" + if [[ -z $cron_id ]]; then + result=$(add_cron_api "$cron_line:$cmd_task $filename:$cron_name") + echo -e "$result" + notify "新增任务通知" "\n$result" + # update_cron_api "$cron_line:$cmd_task $filename:$cron_name:$cron_id" + fi + else + echo -e "下载 ${raw_file_name} 失败,保留之前正常下载的版本...\n" + [ -f "$dir_raw/${raw_file_name}.new" ] && rm -f "$dir_raw/${raw_file_name}.new" + fi + +} + +## 调用用户自定义的extra.sh +run_extra_shell() { + if [[ ${EnableExtraShell} == true ]]; then + if [ -f $file_extra_shell ]; then + echo -e "--------------------------------------------------------------\n" + . $file_extra_shell + else + echo -e "$file_extra_shell文件不存在,跳过执行...\n" + fi + fi +} + +## 脚本用法 +usage() { + echo -e "本脚本用法:" + echo -e "1. $cmd_update update # 更新青龙,并且运行extra.sh" + echo -e "2. $cmd_update restart # 重新启动青龙并编译,不会运行extra.sh" + echo -e "3. $cmd_update raw # 更新单个脚本文件" + echo -e "4. $cmd_update repo # 更新单个仓库的脚本" + echo -e "5. $cmd_update rmlog # 删除旧日志" + echo -e "6. $cmd_update code # 获取互助码" + echo -e "6. $cmd_update bot # 启动tg-bot" +} + +## 更新qinglong +update_qinglong() { + echo -e "--------------------------------------------------------------\n" + git_pull_scripts $dir_root + if [[ $exit_status -eq 0 ]]; then + echo -e "\n更新$dir_root成功...\n" + cp -f $file_config_sample $dir_config/config.sample.sh + detect_config_version + update_depend + else + echo -e "\n更新$dir_root失败,请检查原因...\n" + fi +} + +## 对比脚本 +diff_scripts() { + gen_list_repo $1 $2 $3 $4 $5 + diff_cron $list_own_scripts $list_own_user $list_own_add $list_own_drop + + if [ -s $list_own_drop ]; then + output_list_add_drop $list_own_drop "失效" + if [[ ${AutoDelCron} == true ]]; then + del_cron $list_own_drop $2 + fi + fi + if [ -s $list_own_add ]; then + output_list_add_drop $list_own_add "新" + if [[ ${AutoAddCron} == true ]]; then + add_cron $list_own_add $2 + fi + fi +} + +## 生成脚本的路径清单文件 +gen_list_repo() { + local dir_current=$(pwd) + local repo_path="$1" + local author="$2" + local path="$3" + local blackword="$4" + local dependence="$5" + rm -f $dir_list_tmp/own*.list >/dev/null 2>&1 + + cd ${repo_path} + files=$(find . -name "*.js" | sed 's/^..//') + if [[ $path ]]; then + files=$(find . -name "*.js" | sed 's/^..//' | egrep $path) + fi + if [[ $blackword ]]; then + files=$(find . -name "*.js" | sed 's/^..//' | egrep -v $blackword | egrep $path) + fi + if [[ $dependence ]]; then + find . -name "*.js" | sed 's/^..//' | egrep $dependence | xargs -i cp {} $dir_scripts + fi + for file in ${files}; do + filename=$(basename $file) + cp -f $file $dir_scripts/${author}_${filename} + echo ${author}_${filename} >>$list_own_scripts + done + grep -E "$cmd_task $author" $list_crontab_user | perl -pe "s|.*ID=(.*) $cmd_task ($author_.*)\.*|\2|" | awk -F " " '{print $1}' | sort -u >$list_own_user + cd $dir_current +} + +## 重新编译qinglong +restart_qinglong() { + update_qinglong + if [[ $exit_status -eq 0 ]]; then + echo -e "重新编译青龙...\n" + yarn install --network-timeout 1000000000 || yarn install --registry=https://registry.npm.taobao.org --network-timeout 1000000000 + yarn build + yarn build-back + yarn cache clean + echo -e "重新编译青龙完成...\n" + + echo -e "重启青龙面板...\n" + pm2 reload panel 2>/dev/null || pm2 start $dir_root/build/app.js -n panel + nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf + echo -e "重启面板完成...\n" + + echo -e "重启定时任务...\n" + pm2 reload schedule 2>/dev/null || pm2 start $dir_root/build/schedule.js -n schedule + echo -e "重启定时完成...\n" + fi +} + +main() { + local p1=$1 + local p2=$2 + local p3=$3 + local p4=$4 + local p5=$5 + log_time=$(date "+%Y-%m-%d-%H-%M-%S") + log_path="$dir_log/update/${log_time}_$p1.log" + case $p1 in + update) + update_qinglong | tee $log_path + run_extra_shell | tee -a $log_path + ;; + restart) + restart_qinglong | tee $log_path + ;; + repo) + get_user_info + local name=$(echo "${p2##*/}" | awk -F "." '{print $1}') + log_path="$dir_log/update/${log_time}_$name.log" + if [[ -n $p2 ]]; then + update_repo "$p2" "$p3" "$p4" "$p5" | tee $log_path + else + echo -e "命令输入错误...\n" + usage + fi + ;; + raw) + get_user_info + local name=$(echo "${p2##*/}" | awk -F "." '{print $1}') + log_path="$dir_log/update/${log_time}_$name.log" + if [[ -n $p2 ]]; then + update_raw "$p2" | tee $log_path + else + echo -e "命令输入错误...\n" + usage + fi + ;; + rmlog) + . $dir_shell/rmlog.sh "$p2" | tee $log_path + ;; + code) + . $dir_shell/code.sh + ;; + bot) + . $dir_shell/bot.sh + ;; + *) + echo -e "命令输入错误...\n" + usage + ;; + esac +} + +main "$@" + +exit 0 diff --git a/src/layouts/index.less b/src/layouts/index.less index e0f8d31d..7329aca7 100644 --- a/src/layouts/index.less +++ b/src/layouts/index.less @@ -23,7 +23,7 @@ body { } } -.cookie-wrapper { +.session-wrapper { th { white-space: nowrap; } diff --git a/src/pages/cookie/index.tsx b/src/pages/cookie/index.tsx index 71ccfaeb..7f2ff7f6 100644 --- a/src/pages/cookie/index.tsx +++ b/src/pages/cookie/index.tsx @@ -105,17 +105,17 @@ const Config = () => { return {index + 1} ; }, }, - { - title: '用户名', - dataIndex: 'pin', - key: 'pin', - align: 'center' as const, - render: (text: string, record: any) => { - const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/); - const val = (match && match[1]) || '未匹配用户名'; - return {decodeUrl(val)}; - }, - }, + // { + // title: '用户名', + // dataIndex: 'pin', + // key: 'pin', + // align: 'center' as const, + // render: (text: string, record: any) => { + // const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/); + // const val = (match && match[1]) || '未匹配用户名'; + // return {decodeUrl(val)}; + // }, + // }, { title: '昵称', dataIndex: 'nickname', @@ -231,11 +231,11 @@ const Config = () => { } }; - useEffect(() => { - if (value && loading) { - asyncUpdateStatus(); - } - }, [value]); + // useEffect(() => { + // if (value && loading) { + // asyncUpdateStatus(); + // } + // }, [value]); const asyncUpdateStatus = async () => { for (let i = 0; i < value.length; i++) { @@ -362,7 +362,7 @@ const Config = () => { }); }; - const handleCancel = (cookies: any[]) => { + const handleCancel = (cookies?: any[]) => { setIsModalVisible(false); if (cookies && cookies.length > 0) { handleCookies(cookies); @@ -370,10 +370,10 @@ const Config = () => { }; const handleCookies = (cookies: any[]) => { + const result = [...value]; for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i]; const index = value.findIndex((x) => x._id === cookie._id); - const result = [...value]; if (index === -1) { result.push(cookie); } else { @@ -381,9 +381,8 @@ const Config = () => { ...cookie, }); } - setValue(result); - refreshStatus(cookie, index); } + setValue(result); }; const components = { @@ -432,9 +431,8 @@ const Config = () => { return ( addCookie()}> 添加Cookie @@ -463,9 +461,9 @@ const Config = () => { dataSource={value} rowKey="value" size="middle" - bordered scroll={{ x: 768 }} components={components} + loading={loading} onRow={(record, index) => { return { index, diff --git a/src/pages/cookie/modal.tsx b/src/pages/cookie/modal.tsx index b8731a48..a735e0ee 100644 --- a/src/pages/cookie/modal.tsx +++ b/src/pages/cookie/modal.tsx @@ -10,7 +10,7 @@ const CookieModal = ({ }: { cookie?: any; visible: boolean; - handleCancel: (needUpdate?: boolean) => void; + handleCancel: (cks?: any[]) => void; }) => { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); @@ -46,7 +46,7 @@ const CookieModal = ({ }); } setLoading(false); - handleCancel(data); + handleCancel(cookie ? [data] : data); }; useEffect(() => { @@ -92,7 +92,7 @@ const CookieModal = ({ diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx index 005f1c4f..67a3a406 100644 --- a/src/pages/crontab/index.tsx +++ b/src/pages/crontab/index.tsx @@ -23,6 +23,7 @@ import { EditOutlined, StopOutlined, DeleteOutlined, + PauseCircleOutlined, } from '@ant-design/icons'; import config from '@/utils/config'; import { PageContainer } from '@ant-design/pro-layout'; @@ -39,6 +40,20 @@ enum CrontabStatus { 'disabled', } +enum OperationName { + '启用', + '禁用', + '运行', + '停止', +} + +enum OperationPath { + 'enable', + 'disable', + 'run', + 'stop', +} + const Crontab = () => { const columns = [ { @@ -108,15 +123,28 @@ const Crontab = () => { align: 'center' as const, render: (text: string, record: any, index: number) => ( - - { - runCron(record, index); - }} - > - - - + {record.status !== CrontabStatus.running && ( + + { + runCron(record, index); + }} + > + + + + )} + {record.status === CrontabStatus.running && ( + + { + stopCron(record, index); + }} + > + + + + )} { @@ -142,6 +170,7 @@ const Crontab = () => { const [searchText, setSearchText] = useState(''); const [isLogModalVisible, setIsLogModalVisible] = useState(false); const [logCron, setLogCron] = useState(); + const [selectedRowIds, setSelectedRowIds] = useState([]); const getCrons = () => { setLoading(true); @@ -177,7 +206,7 @@ const Crontab = () => { ), onOk() { request - .delete(`${config.apiPrefix}crons/${record._id}`) + .delete(`${config.apiPrefix}crons`, { data: [record._id] }) .then((data: any) => { if (data.code === 200) { notification.success({ @@ -213,7 +242,7 @@ const Crontab = () => { ), onOk() { request - .get(`${config.apiPrefix}crons/${record._id}/run`) + .put(`${config.apiPrefix}crons/run`, { data: [record._id] }) .then((data: any) => { if (data.code === 200) { const result = [...value]; @@ -235,6 +264,42 @@ const Crontab = () => { }); }; + const stopCron = (record: any, index: number) => { + Modal.confirm({ + title: '确认停止', + content: ( + <> + 确认停止定时任务{' '} + + {record.name} + {' '} + 吗 + + ), + onOk() { + request + .put(`${config.apiPrefix}crons/stop`, { data: [record._id] }) + .then((data: any) => { + if (data.code === 200) { + const result = [...value]; + result.splice(index, 1, { + ...record, + status: CrontabStatus.idle, + }); + setValue(result); + } else { + notification.error({ + message: data, + }); + } + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + }; + const enabledOrDisabledCron = (record: any, index: number) => { Modal.confirm({ title: `确认${ @@ -252,21 +317,16 @@ const Crontab = () => { ), onOk() { request - .get( - `${config.apiPrefix}crons/${record._id}/${ + .put( + `${config.apiPrefix}crons/${ record.status === CrontabStatus.disabled ? 'enable' : 'disable' }`, { - data: { _id: record._id }, + data: [record._id], }, ) .then((data: any) => { if (data.code === 200) { - notification.success({ - message: `${ - record.status === CrontabStatus.disabled ? '启用' : '禁用' - }成功`, - }); const newStatus = record.status === CrontabStatus.disabled ? CrontabStatus.idle @@ -296,30 +356,28 @@ const Crontab = () => { }> = ({ record, index }) => ( action(key, record, index)}> }> 编辑 + + ) : ( + + ) + } + > + {record.status === CrontabStatus.disabled ? '启用' : '禁用'} + {record.isSystem !== 1 && ( - <> - - ) : ( - - ) - } - > - {record.status === CrontabStatus.disabled ? '启用' : '禁用'} - - }> - 删除 - - + }> + 删除 + )} } @@ -385,8 +443,78 @@ const Crontab = () => { .finally(() => setLoading(false)); }; + const onSelectChange = (selectedIds: any[]) => { + setSelectedRowIds(selectedIds); + }; + + const rowSelection = { + selectedRowIds, + onChange: onSelectChange, + selections: [ + Table.SELECTION_ALL, + Table.SELECTION_INVERT, + Table.SELECTION_NONE, + ], + }; + + const delCrons = () => { + Modal.confirm({ + title: '确认删除', + content: <>确认删除选中的定时任务吗, + onOk() { + request + .delete(`${config.apiPrefix}crons`, { data: selectedRowIds }) + .then((data: any) => { + if (data.code === 200) { + notification.success({ + message: '批量删除成功', + }); + setSelectedRowIds([]); + getCrons(); + } else { + notification.error({ + message: data, + }); + } + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + }; + + const operateCrons = (operationStatus: number) => { + Modal.confirm({ + title: `确认${OperationName[operationStatus]}`, + content: <>确认{OperationName[operationStatus]}选中的定时任务吗, + onOk() { + request + .put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, { + data: selectedRowIds, + }) + .then((data: any) => { + if (data.code === 200) { + notification.success({ + message: `批量${OperationName[operationStatus]}成功`, + }); + getCrons(); + } else { + notification.error({ + message: data, + }); + } + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + }; + useEffect(() => { if (logCron) { + localStorage.setItem('logCron', logCron._id); setIsLogModalVisible(true); } }, [logCron]); @@ -411,7 +539,6 @@ const Crontab = () => { { marginLeft, }, }} + style={{ + height: '100vh', + }} > + {selectedRowIds.length > 0 && ( + + )} { dataSource={value} rowKey="_id" size="middle" - bordered scroll={{ x: 768 }} + loading={loading} + rowSelection={rowSelection} /> { - const log = data.data as string; - setValue(log || '暂无日志'); - if (log && !log.includes('执行结束')) { - setTimeout(() => { - getCronLog(); - }, 2000); + if (localStorage.getItem('logCron') === cron._id) { + const log = data.data as string; + setValue(log || '暂无日志'); + if (log && !log.includes('执行结束')) { + setTimeout(() => { + getCronLog(); + }, 2000); + } } }) .finally(() => { diff --git a/src/pages/crontab/modal.tsx b/src/pages/crontab/modal.tsx index ea6ee681..6163afb7 100644 --- a/src/pages/crontab/modal.tsx +++ b/src/pages/crontab/modal.tsx @@ -68,19 +68,19 @@ const CronModal = ({ >
- + - - + + { - if (cronParse.parseString(value).expressions.length > 0) { + if (cronParse.parseExpression(value).hasNext()) { return Promise.resolve(); } else { return Promise.reject('Cron表达式格式有误'); @@ -89,7 +89,7 @@ const CronModal = ({ }, ]} > - + diff --git a/src/pages/diff/index.tsx b/src/pages/diff/index.tsx index 7906726e..6801c5f1 100644 --- a/src/pages/diff/index.tsx +++ b/src/pages/diff/index.tsx @@ -87,7 +87,7 @@ const Crontab = () => { newValue={sample} splitView={true} leftTitle="config.sh" - rightTitle="config.sh.sample" + rightTitle="config.sample.sh" disableWordDiff={true} /> {/*