/* B站直播 需要青龙环境 需要安装 wxmnode #大运会抢票 cron: 0,10 * * * * */ //涵 process.env.dyh = "111"; // process.env.PUSH_KEY = 'SCT46897TQDkm3LUOZdiklUhe1wyLhhfa' let DD_BOT_TOKEN, SCKEY; 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.PUSH_KEY) { SCKEY = process.env.PUSH_KEY; } const $ = new Env("大运会抢票"); let envSplitor = ['@', '\n'] let httpResult, httpReq, httpResp let ckName = 'dyh' let userCookie = ($.isNode() ? process.env[ckName] : $.getdata(ckName)) || ''; let userList = [] let userIdx = 0 let userCount = 0 let baseUrl = 'http://423n669m71.no-ip.org:9000/api/endpoints/2'; let apiKey = "ptr_oQzrYfeDBfuGSbaK2qOM83zN94RyEZfO8v+DdUJwccA=" let keys = ['篮球', "羽毛球", "排球"] /////////////////////////////////////////////////////////////////// class UserInfo { constructor(str) { //console.log(str) this.index = ++userIdx, this.idx = `账号[${this.index}] `; // let cookJson=$.str2json(str,"; "); // this.ck=cookJson.consistent_code; this.ck = str; this.apiKey = apiKey; this.exception_num = 0; this.current_index = 0; this.message = ""; } async testCk () { let url = `https://tickets.2021chengdu.com/cyy_gatewayapi/user/buyer/v3/user_audiences?src=H5&channelId=&terminalSrc=H5&offset=0&length=50&lang=zh` let body = {}; let boydStr = JSON.stringify(body); let urlObject = popu(url, "", this.ck) await httpRequest('get', urlObject) let result = httpResult; if (result.statusCode != 200) { $.logAndNotify(`凭证已过期,${result.comments}`); return false; } return true; } //获取指定比赛 async getRace (name) { let url = "https://tickets.2021chengdu.com/cyy_gatewayapi/home/pub/v3/show_list/search_by_front?src=WEB&channelId=&terminalSrc=WEB&offset=0&length=20&cityId=3101&bizFrontendCategoryId=648c3321b9f9f200018d1de8&lang=zh" let body = {}; let boydStr = JSON.stringify(body); let urlObject = popu(url, "", this.ck) await httpRequest('get', urlObject) let result = httpResult; if (result.statusCode != 200) { $.logAndNotify(`获取${name}赛事列表失败,${result.comments}`); return undefined; } var item = result.data.searchData.find(item => item.showName == name); if (!item) { console.log(`赛事${name}还未开始`); return undefined; } url = `https://tickets.2021chengdu.com/cyy_gatewayapi/show/pub/v3/show_dynamic_data/${item.showId}?src=WEB&channelId=&terminalSrc=WEB&lang=zh` urlObject = popu(url, "", this.ck) await httpRequest('get', urlObject) result = httpResult; if (result.statusCode != 200) { $.logAndNotify(`获取${name}赛事状态失败,${result.comments}`); return undefined; } else if (result.data.showDetailStatus != "ON_SALE") { $.logAndNotify(`获取${name}赛事状态,${JSON.stringify(result.data)}`); return undefined; } $.logAndNotify(`${name}`); return item; } //获取周末可购买场次 async getChangci (name, item) { let url = `https://tickets.2021chengdu.com/cyy_gatewayapi/show/pub/v3/show/${item.showId}/sessions_static_data?src=WEB&channelId=&terminalSrc=WEB&lang=zh` let static_datas = this.GetJsonDatas(`获取${name}赛事场次列表失败`, url); if (static_datas == undefined) { return undefined; } url = `https://tickets.2021chengdu.com/cyy_gatewayapi/show/pub/v3/show/${item.showId}/sessions_dynamic_data?src=WEB&channelId=&terminalSrc=WEB&lang=zh` let dynamic_datas = this.GetJsonDatas(`获取${name}赛事场次列表失败`, url); if (dynamic_datas == undefined) { return undefined; } dynamic_datas = dynamic_datas.data.sessionVOs; static_datas = static_datas.data.sessionVOs; let goodItems = []; //可以购买的场次 for (let index = 0; index < static_datas.length; index++) { const element = static_datas[index]; let day = (new Date(element.beginDateTime)).getDay(); //判断是否还有票 let exitsItem = dynamic_datas.find(item => item.bizShowSessionId == element.bizShowSessionId && item.sessionStatus == 'ON_SALE'); if (!exitsItem) { //周末 if (day == 0 || day == 6) { goodItems.splice(0, 0, exitsItem); } else goodItems.push(element); } } //可以购买的票价 for (let index = 0; index < goodItems.length; index++) { const element = goodItems[index]; //场次的票价 url = `https://tickets.2021chengdu.com/cyy_gatewayapi/show/pub/v3/show/${item.showId}/show_session/${element.bizShowSessionId}/seat_plans_static_data?src=WEB&channelId=&terminalSrc=WEB&lang=zh` static_datas = this.GetJsonDatas(`获取${name}赛票价列表失败`, url); if (static_datas == undefined) { return undefined; } url = `https://tickets.2021chengdu.com/cyy_gatewayapi/show/pub/v3/show/${item.showId}/show_session/${element.bizShowSessionId}/seat_plans_dynamic_data?src=WEB&channelId=&terminalSrc=WEB&lang=zh` static_datas = this.GetJsonDatas(`获取${name}赛票价列表失败`, url); if (static_datas == undefined) { return undefined; } dynamic_datas = dynamic_datas.data.seatPlans; static_datas = static_datas.data.seatPlans; for (let j = 0; j < static_datas.length; j++) { const price = static_datas[j]; let exitsItem = dynamic_datas.find(item => item.seatPlanId == element.seatPlanId && item.canBuyCount > 0); if (exitsItem == undefined) continue; $.logAndNotify(`开始抢购 ${name}赛事,场次:${element.sessionName} 票价:${price.originalPrice}`); } } } async GetJsonDatas (title, url) { let urlObject = popu(url, "", this.ck) await httpRequest('get', urlObject) let static_datas = httpResult; if (static_datas.statusCode != 200) { $.logAndNotify(`${title},${result.comments}`); return undefined; } return static_datas; } async task () { try { console.log(`\n=========== ${this.idx} 开始大运会 ===========\n`) // let isLive = await this.testCk(); // if (!isLive) { // return; // } for (let index = 0; index < keys.length; index++) { const key = keys[index]; await this.getRace(key); } } catch (e) { console.log(e) } finally { return Promise.resolve(1); } } } !(async () => { if (typeof $request !== "undefined") { await GetRewrite() } else { if (!(await checkEnv())) return; if (userList.length > 0) { let taskall = [] for (let user of userList) { taskall.push(user.task()) } await Promise.all(taskall); } $.showmsg(); } })() .catch((e) => console.log(e)) .finally(() => $.done()) async function checkEnv () { if (userCookie) { let splitor = envSplitor[0]; for (let sp of envSplitor) { if (userCookie.indexOf(sp) > -1) { splitor = sp; break; } } for (let userCookies of userCookie.split(splitor)) { if (userCookies) userList.push(new UserInfo(userCookies)) } userCount = userList.length } else { } console.log(`找到[${ckName}] 变量 ${userCount}个账号`) return true } //////////////////////////////////////////////////////////////////// function popu (url, body = '', ck) { //console.log(ck) /?upuid\u003d10314864 let host = url.replace('//', '/').split('/')[1] let urlObject = { url: url, headers: { "Host": host, "Connection": "keep-alive", "Accept": "application/json, text/plain, */*", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.51", "Referer": "https://tickets.2021chengdu.com/", "referrerPolicy": "no-referrer-when-downgrade", "Accept-Encoding": "gzip, deflate", "mode": "cors", "credentials": "include", "Content-Type": "application/json;charset:utf-8", "X-Requested-With": "XMLHttpRequest", "Accept-Language": "zh-CN,zh", // "access-token": ck, }, timeout: 5000, } if (body) { urlObject.body = body } return urlObject; } function popudocket (url, body = '', ck) { //console.log(ck) /?upuid\u003d10314864 let host = url.replace('//', '/').split('/')[1] let urlObject = { url: url, headers: { "Host": host, "Connection": "keep-alive", "Accept": "*/*", "User-Agent": "Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/4425 MMWEBSDK/20221206 Mobile Safari/537.36 MMWEBID/4883 MicroMessenger/8.0.32.2300(0x2800205D) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64", "Referer": "http://423n669m71.no-ip.org:9000/", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/json;charset:utf-8", "Accept-Language": "zh-CN,zh", "X-API-Key": ck, }, timeout: 5000, } if (body) { urlObject.body = body } return urlObject; } async function httpRequest (method, url) { //console.log(url) httpResult = null, httpReq = null, httpResp = null; return new Promise((resolve) => { $.send(method, url, async (err, req, resp) => { try { httpReq = req; httpResp = resp; if (err) { } else { if (resp.body) { if (typeof resp.body == "object") { httpResult = resp.body; } else { try { httpResult = JSON.parse(resp.body); } catch (e) { httpResult = resp.body; } } } } } catch (e) { console.log(e); } finally { resolve(); } }); }); } //////////////////////////////////////////////////////////////////// function Env (name, env) { return new class { constructor(name, env) { this.name = name this.notifyStr = '' this.startTime = (new Date).getTime() Object.assign(this, env) console.log(`${this.name} 开始运行:\n`) } 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 } getdata (t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, i, "") : e } catch (t) { e = "" } } return e } setdata (t, e) { let s = !1; if (/^@/.test(e)) { const [, i, r] = /^@(.*?)\.(.*?)$/.exec(e), o = this.getval(i), h = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(h); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), i) } catch (e) { const o = {}; this.lodash_set(o, r, t), s = this.setval(JSON.stringify(o), i) } } else { s = this.setval(t, e); } return s } 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, e) { return this.isSurge() || this.isLoon() ? $persistentStore.write(t, e) : this.isQuanX() ? $prefs.setValueForKey(t, e) : this.isNode() ? (this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0) : this.data && this.data[e] || null } send (m, t, e = (() => { })) { if (m != 'get' && m != 'post' && m != 'put' && m != 'delete') { console.log(`无效的http方法:${m}`); return; } if (m == 'get' && t.headers) { delete t.headers["Content-Type"]; delete t.headers["Content-Length"]; } else if (t.body && t.headers) { if (!t.headers["Content-Type"]) t.headers["Content-Type"] = "application/x-www-form-urlencoded"; } if (this.isSurge() || this.isLoon()) { if (this.isSurge() && this.isNeedRewrite) { t.headers = t.headers || {}; Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 }); } let conf = { method: m, url: t.url, headers: t.headers, timeout: t.timeout, data: t.body }; if (m == 'get') delete conf.data $axios(conf).then(t => { const { status: i, request: q, headers: r, data: o } = t; e(null, q, { statusCode: i, headers: r, body: o }); }).catch(err => console.log(err)) } else if (this.isQuanX()) { t.method = m.toUpperCase(), this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: i, request: q, headers: r, body: o } = t; e(null, q, { statusCode: i, headers: r, body: o }) }, t => e(t)) } else if (this.isNode()) { this.got = this.got ? this.got : require("got"); const { url: s, ...i } = t; this.instance = this.got.extend({ followRedirect: false }); this.instance[m](s, i).then(t => { const { statusCode: i, request: q, headers: r, body: o } = t; e(null, q, { statusCode: i, headers: r, body: o }) }, t => { const { message: s, response: i } = t; e(s, i, i && i.body) }) } } time (t) { let e = { "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 s in e) new RegExp("(" + s + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? e[s] : ("00" + e[s]).substr(("" + e[s]).length))); return t } async showmsg () { if (!this.notifyStr) return; let notifyBody = this.notifyStr if ($.isNode()) { var notify = require('./sendNotify'); console.log('\n============== 推送 ==============') // await notify.sendNotify(this.name, notifyBody); await sendNotify(this.name, notifyBody); } else { this.msg(notifyBody); } } logAndNotify (str) { console.log(str) this.notifyStr += str this.notifyStr += ' ' } msg (e = t, s = "", i = "", r) { const o = t => { if (!t) return t; if ("string" == typeof t) return this.isLoon() ? t : this.isQuanX() ? { "open-url": t } : this.isSurge() ? { url: t } : void 0; if ("object" == typeof t) { if (this.isLoon()) { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } if (this.isQuanX()) { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl; return { "open-url": e, "media-url": s } } if (this.isSurge()) { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } } }; this.isMute || (this.isSurge() || this.isLoon() ? $notification.post(e, s, i, o(r)) : this.isQuanX() && $notify(e, s, i, o(r))); let h = ["", "============== 系统通知 =============="]; h.push(e), s && h.push(s), i && h.push(i), console.log(h.join("\n")) } getMin (a, b) { return ((a < b) ? a : b) } getMax (a, b) { return ((a < b) ? b : a) } padStr (num, length, padding = '0') { let numStr = String(num) let numPad = (length > numStr.length) ? (length - numStr.length) : 0 let retStr = '' for (let i = 0; i < numPad; i++) { retStr += padding } retStr += numStr return retStr; } json2str (obj, c, encodeUrl = false) { let ret = [] for (let keys of Object.keys(obj).sort()) { let v = obj[keys] if (v && encodeUrl) v = encodeURIComponent(v) ret.push(keys + '=' + v) } return ret.join(c); } str2json (str, split = '&', decodeUrl = false) { let ret = {} for (let item of str.split(split)) { if (!item) continue; let idx = item.indexOf('=') if (idx == -1) continue; let k = item.substr(0, idx) let v = item.substr(idx + 1) if (decodeUrl) v = decodeURIComponent(v) ret[k] = v } return ret; } randomString (len, charset = 'abcdef0123456789') { let str = ''; for (let i = 0; i < len; i++) { str += charset.charAt(Math.floor(Math.random() * charset.length)); } return str; } randomList (a) { let idx = Math.floor(Math.random() * a.length) return a[idx] } wait (t) { return new Promise(e => setTimeout(e, t)) } done (t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; console.log(`\n${this.name} 运行结束,共运行了 ${s} 秒!`) if (this.isSurge() || this.isQuanX() || this.isLoon()) $done(t) } }(name, env) } async function sendNotify (text, desp, params = {}, author = '\n\n仅供用于学习') { //提供6种通知 //由于上述两种微信通知需点击进去才能查看到详情,故text(标题内容)携带了账号序号以及昵称信息,方便不点击也可知道是哪个京东哪个活动 text = text.match(/.*?(?=\s?-)/g) ? text.match(/.*?(?=\s?-)/g)[0] : text; await Promise.all([ wxmmmm(text, desp), ddBotNotify(text, desp),//钉钉机器人 ]) } async function serverNotify (text, desp, time = 2100) { 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' } } await httpRequest('post', options) data = httpResult; 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)}`) } } else { console.log('\n\n您未提供server酱的SCKEY,取消微信推送消息通知🚫\n'); resolve() } } async function ddBotNotify (text, desp, time = 2100) { if (DD_BOT_TOKEN) { //微信server酱推送通知一个\n不会换行,需要两个\n才能换行,故做此替换 desp = desp.replace(/[\n\r]/g, '\n\n'); 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' } } await httpRequest('post', options) data = httpResult; if (data.errcode === 0) { console.log('钉钉发送通知消息成功🎉。\n') } else { console.log(`${data.errmsg}\n`) } } else { console.log('您未提供钉钉机器人推送所需的DD_BOT_TOKEN或者DD_BOT_SECRET,取消钉钉推送消息通知🚫\n'); } } async function getToken () { const params = { grant_type: 'client_credential', appid: 'wx78d0fd108ab26b67', // 你的appid 1 secret: '86e3801eb4518af21082e640e4f29473', // 你的secret 2 }; let pp = $.json2str(params, "&"); const options = { url: `https://api.weixin.qq.com/cgi-bin/token?${pp}`, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } await httpRequest('get', options) res = httpResult; return res.access_token; } async function templateMessageSend (title, context) { const token = await getToken(); const url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' + token; const params = { "touser": 'oTHpRuGEWxCYqCPMOidO7vWXjrww', // 用户openid 3 "template_id": 'K5YpydwHeNbr6R1dUna4rYs2xhjvZmqR8KrkRFqhFwY', // 模板id 4 // url: 'http://www.baidu.com', "topcolor": '#FF0000', "data": { "title": { "value": title + "JD" }, "content": { "value": context }, }, }; const options = { url: url, body: JSON.stringify(params), headers: { 'Content-Type': 'application/json;charset=utf-8' } } await httpRequest('post', options) res = httpResult; console.log('res: ', res); } async function wxmmmm (title, context) { const wxmpy = require('wxmnode'); let res = await wxmpy.sendMsgToUser("45741299", "871333", title, context, "") console.log('res: ', res); }