diff --git a/jd_try_notify.js b/jd_try_notify.js new file mode 100644 index 0000000..1c79483 --- /dev/null +++ b/jd_try_notify.js @@ -0,0 +1,686 @@ +/* +cron "22 15 * * *" jd_try_notify.js + */ +const $ = new Env('京东试用待领取通知') +const jdCookieNode = $.isNode() ? require('./jdCookie.js') : ''; +const notify = $.isNode() ? require('./sendNotify') : ''; +let trialActivityIdList = [] +let trialActivityTitleList = [] +let notifyMsg = '' +let size = 1; +$.isPush = true; +$.isLimit = false; +$.isForbidden = false; +$.wrong = false; +$.giveupNum = 0; +$.successNum = 0; +$.completeNum = 0; +$.getNum = 0; +$.try = true; +$.sentNum = 0; +$.notifyMsg = '' + +let cookiesArr = []; +if ($.isNode()) { + Object.keys(jdCookieNode).forEach((item) => { + cookiesArr.push(jdCookieNode[item]) + }) + if (process.env.JD_DEBUG && process.env.JD_DEBUG === 'false') + console.log = () => { + }; +} else { + cookiesArr = [$.getdata('CookieJD'), $.getdata('CookieJD2'), ...jsonParse($.getdata('CookiesJD') || "[]").map(item => item.cookie)].filter(item => !!item); +} + +!(async () => { + if (!cookiesArr[0]) { + $.msg($.name, '【提示】请先获取京东账号一cookie\n直接使用NobyDa的京东签到获取', 'https://bean.m.jd.com/', { + "open-url": "https://bean.m.jd.com/" + }) + return + } + for(let i = 0; i < $.cookiesArr.length; i++){ + if (cookiesArr[i]) { + $.cookie = cookiesArr[i]; + $.UserName = decodeURIComponent($.cookie.match(/pt_pin=(.+?);/) && $.cookie.match(/pt_pin=(.+?);/)[1]) + $.index = i + 1; + $.isLogin = true; + console.log(`\n开始【京东账号${$.index}】${$.UserName}\n`); + let data = await try_list() + try { + list = data.data.list + for (let j = 0; j < list.length; j++) { + item = list[j] + if (item.leftTime) { + if (new Date().getTime() < item.endTime + 60 * 60 * 24 * 1000 * 2) { + let title=item.trialName.length>15?item.trialName.substr(0,30)+'...':item.trialName + $.notifyMsg += `【账号】${$.index}.${$.UserName} 可免费领取-${title}\n入口:京东-我的-更多工具-新品试用\n`; + } else { + console.log("开始领取两天后不再推") + } + } + } + } catch (e) { + } + await $.wait(5000); + } + } + console.log($.notifyMsg) + await notify.sendNotify($.name, $.notifyMsg); +})().catch((e) => { + console.error(`❗️ ${$.name} 运行错误!\n${e}`) +}).finally(() => $.done()) + +async function try_list() { + return new Promise((resolve, reject) => { + console.log(`拉取申请成功列表...`) + let option = taskurl_xh() + $.post(option, (err, resp, data) => { + try { + if (err) { + console.log('err', err) + } else { + data = JSON.parse(data); + } + } catch (e) { + reject(`⚠️ ${arguments.callee.name.toString()} API返回结果解析出错\n${e}\n${JSON.stringify(data)}`) + } finally { + resolve(data) + } + }) + }) +} + +function taskurl_xh() { + return { + "url": "https://api.m.jd.com/client.action", + 'headers': { + 'authority': 'api.m.jd.com', + 'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"', + 'accept': 'application/json, text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'sec-ch-ua-mobile': '?0', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'sec-ch-ua-platform': '"Windows"', + 'origin': 'https://prodev.m.jd.com', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin', + 'referer': 'https://prodev.m.jd.com/', + 'accept-language': 'zh-CN,zh;q=0.9', + "cookie": $.cookie, + }, + "body": "appid=newtry&functionId=try_MyTrials&uuid=1618382783803957689511&clientVersion=&client=wh5&osVersion=&area=&networkType=&body=%7B%22page%22%3A1%2C%22selected%22%3A2%2C%22previewTime%22%3A%22%22%7D", + } +} + +function jsonParse(str) { + if (typeof str == "string") { + try { + return JSON.parse(str); + } catch (e) { + console.log(e); + $.msg($.name, '', '请勿随意在BoxJs输入框修改内容\n建议通过脚本去获取cookie') + return []; + } + } +} + +function Env(name, opts) { + class Http { + constructor(env) { + this.env = env + } + + send(opts, method = 'GET') { + opts = typeof opts === 'string' ? { + url: opts + } : opts + let sender = this.get + if (method === 'POST') { + sender = this.post + } + return new Promise((resolve, reject) => { + sender.call(this, opts, (err, resp, body) => { + if (err) reject(err) + else resolve(resp) + }) + }) + } + + get(opts) { + return this.send.call(this.env, opts) + } + + post(opts) { + return this.send.call(this.env, opts, 'POST') + } + } + + return new (class { + constructor(name, opts) { + this.name = name + this.http = new Http(this) + this.data = null + this.dataFile = 'box.dat' + this.logs = [] + this.isMute = false + this.isNeedRewrite = false + this.logSeparator = '\n' + this.startTime = new Date().getTime() + Object.assign(this, opts) + this.log('', `🔔${this.name}, 开始!`) + } + + 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 + } + + toObj(str, defaultValue = null) { + try { + return JSON.parse(str) + } catch { + return defaultValue + } + } + + toStr(obj, defaultValue = null) { + try { + return JSON.stringify(obj) + } catch { + return defaultValue + } + } + + getjson(key, defaultValue) { + let json = defaultValue + const val = this.getdata(key) + if (val) { + try { + json = JSON.parse(this.getdata(key)) + } catch { + } + } + return json + } + + setjson(val, key) { + try { + return this.setdata(JSON.stringify(val), key) + } catch { + return false + } + } + + getScript(url) { + return new Promise((resolve) => { + this.get({ + url + }, (err, resp, body) => resolve(body)) + }) + } + + runScript(script, runOpts) { + return new Promise((resolve) => { + let httpapi = this.getdata('@chavy_boxjs_userCfgs.httpapi') + httpapi = httpapi ? httpapi.replace(/\n/g, '').trim() : httpapi + let httpapi_timeout = this.getdata('@chavy_boxjs_userCfgs.httpapi_timeout') + httpapi_timeout = httpapi_timeout ? httpapi_timeout * 1 : 20 + httpapi_timeout = runOpts && runOpts.timeout ? runOpts.timeout : httpapi_timeout + const [key, addr] = httpapi.split('@') + const opts = { + url: `http://${addr}/v1/scripting/evaluate`, + body: { + script_text: script, + mock_type: 'cron', + timeout: httpapi_timeout + }, + headers: { + 'X-Key': key, + 'Accept': '*/*' + } + } + this.post(opts, (err, resp, body) => resolve(body)) + }).catch((e) => this.logErr(e)) + } + + loaddata() { + if (this.isNode()) { + this.fs = this.fs ? this.fs : require('fs') + this.path = this.path ? this.path : require('path') + const curDirDataFilePath = this.path.resolve(this.dataFile) + const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile) + const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath) + const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath) + if (isCurDirDataFile || isRootDirDataFile) { + const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath + try { + return JSON.parse(this.fs.readFileSync(datPath)) + } catch (e) { + return {} + } + } else return {} + } else return {} + } + + writedata() { + if (this.isNode()) { + this.fs = this.fs ? this.fs : require('fs') + this.path = this.path ? this.path : require('path') + const curDirDataFilePath = this.path.resolve(this.dataFile) + const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile) + const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath) + const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath) + const jsondata = JSON.stringify(this.data) + if (isCurDirDataFile) { + this.fs.writeFileSync(curDirDataFilePath, jsondata) + } else if (isRootDirDataFile) { + this.fs.writeFileSync(rootDirDataFilePath, jsondata) + } else { + this.fs.writeFileSync(curDirDataFilePath, jsondata) + } + } + } + + lodash_get(source, path, defaultValue = undefined) { + const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.') + let result = source + for (const p of paths) { + result = Object(result)[p] + if (result === undefined) { + return defaultValue + } + } + return result + } + + lodash_set(obj, path, value) { + if (Object(obj) !== obj) return obj + if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [] + path.slice(0, -1).reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[ + path[path.length - 1] + ] = value + return obj + } + + getdata(key) { + let val = this.getval(key) + // 如果以 @ + if (/^@/.test(key)) { + const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key) + const objval = objkey ? this.getval(objkey) : '' + if (objval) { + try { + const objedval = JSON.parse(objval) + val = objedval ? this.lodash_get(objedval, paths, '') : val + } catch (e) { + val = '' + } + } + } + return val + } + + setdata(val, key) { + let issuc = false + if (/^@/.test(key)) { + const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key) + const objdat = this.getval(objkey) + const objval = objkey ? (objdat === 'null' ? null : objdat || '{}') : '{}' + try { + const objedval = JSON.parse(objval) + this.lodash_set(objedval, paths, val) + issuc = this.setval(JSON.stringify(objedval), objkey) + } catch (e) { + const objedval = {} + this.lodash_set(objedval, paths, val) + issuc = this.setval(JSON.stringify(objedval), objkey) + } + } else { + issuc = this.setval(val, key) + } + return issuc + } + + getval(key) { + if (this.isSurge() || this.isLoon()) { + return $persistentStore.read(key) + } else if (this.isQuanX()) { + return $prefs.valueForKey(key) + } else if (this.isNode()) { + this.data = this.loaddata() + return this.data[key] + } else { + return (this.data && this.data[key]) || null + } + } + + setval(val, key) { + if (this.isSurge() || this.isLoon()) { + return $persistentStore.write(val, key) + } else if (this.isQuanX()) { + return $prefs.setValueForKey(val, key) + } else if (this.isNode()) { + this.data = this.loaddata() + this.data[key] = val + this.writedata() + return true + } else { + return (this.data && this.data[key]) || null + } + } + + initGotEnv(opts) { + 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() + if (opts) { + opts.headers = opts.headers ? opts.headers : {} + if (undefined === opts.headers.Cookie && undefined === opts.cookieJar) { + opts.cookieJar = this.ckjar + } + } + } + + get(opts, callback = () => { + }) { + if (opts.headers) { + delete opts.headers['Content-Type'] + delete opts.headers['Content-Length'] + } + if (this.isSurge() || this.isLoon()) { + if (this.isSurge() && this.isNeedRewrite) { + opts.headers = opts.headers || {} + Object.assign(opts.headers, { + 'X-Surge-Skip-Scripting': false + }) + } + $httpClient.get(opts, (err, resp, body) => { + if (!err && resp) { + resp.body = body + resp.statusCode = resp.status + } + callback(err, resp, body) + }) + } else if (this.isQuanX()) { + if (this.isNeedRewrite) { + opts.opts = opts.opts || {} + Object.assign(opts.opts, { + hints: false + }) + } + $task.fetch(opts).then( + (resp) => { + const { + statusCode: status, + statusCode, + headers, + body + } = resp + callback(null, { + status, + statusCode, + headers, + body + }, body) + }, + (err) => callback(err) + ) + } else if (this.isNode()) { + this.initGotEnv(opts) + this.got(opts).on('redirect', (resp, nextOpts) => { + try { + if (resp.headers['set-cookie']) { + const ck = resp.headers['set-cookie'].map(this.cktough.Cookie.parse).toString() + if (ck) { + this.ckjar.setCookieSync(ck, null) + } + nextOpts.cookieJar = this.ckjar + } + } catch (e) { + this.logErr(e) + } + // this.ckjar.setCookieSync(resp.headers['set-cookie'].map(Cookie.parse).toString()) + }).then( + (resp) => { + const { + statusCode: status, + statusCode, + headers, + body + } = resp + callback(null, { + status, + statusCode, + headers, + body + }, body) + }, + (err) => { + const { + message: error, + response: resp + } = err + callback(error, resp, resp && resp.body) + } + ) + } + } + + post(opts, callback = () => { + }) { + // 如果指定了请求体, 但没指定`Content-Type`, 则自动生成 + if (opts.body && opts.headers && !opts.headers['Content-Type']) { + opts.headers['Content-Type'] = 'application/x-www-form-urlencoded' + } + if (opts.headers) delete opts.headers['Content-Length'] + if (this.isSurge() || this.isLoon()) { + if (this.isSurge() && this.isNeedRewrite) { + opts.headers = opts.headers || {} + Object.assign(opts.headers, { + 'X-Surge-Skip-Scripting': false + }) + } + $httpClient.post(opts, (err, resp, body) => { + if (!err && resp) { + resp.body = body + resp.statusCode = resp.status + } + callback(err, resp, body) + }) + } else if (this.isQuanX()) { + opts.method = 'POST' + if (this.isNeedRewrite) { + opts.opts = opts.opts || {} + Object.assign(opts.opts, { + hints: false + }) + } + $task.fetch(opts).then( + (resp) => { + const { + statusCode: status, + statusCode, + headers, + body + } = resp + callback(null, { + status, + statusCode, + headers, + body + }, body) + }, + (err) => callback(err) + ) + } else if (this.isNode()) { + this.initGotEnv(opts) + const { + url, + ..._opts + } = opts + this.got.post(url, _opts).then( + (resp) => { + const { + statusCode: status, + statusCode, + headers, + body + } = resp + callback(null, { + status, + statusCode, + headers, + body + }, body) + }, + (err) => { + const { + message: error, + response: resp + } = err + callback(error, resp, resp && resp.body) + } + ) + } + } + + /** + * + * 示例:$.time('yyyy-MM-dd qq HH:mm:ss.S') + * :$.time('yyyyMMddHHmmssS') + * y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒 + * 其中y可选0-4位占位符、S可选0-1位占位符,其余可选0-2位占位符 + * @param {*} fmt 格式化参数 + * + */ + time(fmt) { + let o = { + '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() + } + if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (new Date().getFullYear() + '').substr(4 - RegExp.$1.length)) + for (let k in o) + if (new RegExp('(' + k + ')').test(fmt)) + fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) + return fmt + } + + /** + * 系统通知 + * + * > 通知参数: 同时支持 QuanX 和 Loon 两种格式, EnvJs根据运行环境自动转换, Surge 环境不支持多媒体通知 + * + * 示例: + * $.msg(title, subt, desc, 'twitter://') + * $.msg(title, subt, desc, { 'open-url': 'twitter://', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) + * $.msg(title, subt, desc, { 'open-url': 'https://bing.com', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) + * + * @param {*} title 标题 + * @param {*} subt 副标题 + * @param {*} desc 通知详情 + * @param {*} opts 通知参数 + * + */ + msg(title = name, subt = '', desc = '', opts) { + const toEnvOpts = (rawopts) => { + if (!rawopts) return rawopts + if (typeof rawopts === 'string') { + if (this.isLoon()) return rawopts + else if (this.isQuanX()) return { + 'open-url': rawopts + } + else if (this.isSurge()) return { + url: rawopts + } + else return undefined + } else if (typeof rawopts === 'object') { + if (this.isLoon()) { + let openUrl = rawopts.openUrl || rawopts.url || rawopts['open-url'] + let mediaUrl = rawopts.mediaUrl || rawopts['media-url'] + return { + openUrl, + mediaUrl + } + } else if (this.isQuanX()) { + let openUrl = rawopts['open-url'] || rawopts.url || rawopts.openUrl + let mediaUrl = rawopts['media-url'] || rawopts.mediaUrl + return { + 'open-url': openUrl, + 'media-url': mediaUrl + } + } else if (this.isSurge()) { + let openUrl = rawopts.url || rawopts.openUrl || rawopts['open-url'] + return { + url: openUrl + } + } + } else { + return undefined + } + } + if (!this.isMute) { + if (this.isSurge() || this.isLoon()) { + $notification.post(title, subt, desc, toEnvOpts(opts)) + } else if (this.isQuanX()) { + $notify(title, subt, desc, toEnvOpts(opts)) + } + } + if (!this.isMuteLog) { + let logs = ['', '==============📣系统通知📣=============='] + logs.push(title) + subt ? logs.push(subt) : '' + desc ? logs.push(desc) : '' + console.log(logs.join('\n')) + this.logs = this.logs.concat(logs) + } + } + + log(...logs) { + if (logs.length > 0) { + this.logs = [...this.logs, ...logs] + } + console.log(logs.join(this.logSeparator)) + } + + logErr(err, msg) { + const isPrintSack = !this.isSurge() && !this.isQuanX() && !this.isLoon() + if (!isPrintSack) { + this.log('', `❗️${this.name}, 错误!`, err) + } else { + this.log('', `❗️${this.name}, 错误!`, err.stack) + } + } + + wait(time) { + return new Promise((resolve) => setTimeout(resolve, time)) + } + + done(val = {}) { + const endTime = new Date().getTime() + const costTime = (endTime - this.startTime) / 1000 + this.log('', `🔔${this.name}, 结束! 🕛 ${costTime} 秒`) + this.log() + if (this.isSurge() || this.isQuanX() || this.isLoon()) { + $done(val) + } + } + })(name, opts) +}