commit 5d03218cd929f9a26711d5619ffc2f4af5295109 Author: jqtmviyu Date: Mon Mar 27 18:23:56 2023 +0800 first comit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab0ba92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.toml \ No newline at end of file diff --git a/app.js b/app.js new file mode 100755 index 0000000..04da6bf --- /dev/null +++ b/app.js @@ -0,0 +1,35 @@ +const schedule = require('node-schedule') +const { request } = require('./js/request') +const { getNowTime, configPaths, getConfig, isExpire } = require('./js/tools') +const { login } = require('./js/login') + +const checkin = async () => { + // 遍历配置文件 + const paths = await configPaths() + for (const path of paths) { + // 读取配置文件, 并转成对象 + const config = await getConfig(path) + // 判断是否过期 + if (isExpire(config)) { + const newCnfig = await login(config) + await request(newCnfig.checkinUrl, newCnfig.headers) + } else { + await request(config.checkinUrl, config.headers) + } + } + console.log('全部签到完成!') +} + +const main = () => { + // 定时任务 + schedule.scheduleJob('0 0 7 * * *', () => { + // 随机10分钟 + setTimeout(() => { + checkin() + }, Math.random() * 10 * 60 * 1000) + }) + // .invoke() +} + +console.log(`开始执行任务-${getNowTime()}`) +main() diff --git a/config/test.toml.example b/config/test.toml.example new file mode 100644 index 0000000..433ba73 --- /dev/null +++ b/config/test.toml.example @@ -0,0 +1,19 @@ +fileName = "test" +loginUrl = "https://test/auth/login" +checkinUrl = "https://test/user/checkin" +email = "xxx@gmail.com" +password = "xxx" +expire_in = "xxx" + +[headers] +Cookie = "uid=xxx; email=xxx%40gmail.com; key=xxx; ip=xxx; expire_in=xxx" +Host = "test" +Origin = "https://test" +Referer = "https://test/user" +sec-ch-ua = '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"' +sec-ch-ua-platform = "macOS" +User-Agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" +expire_in = "xxx" +Accept = "*/*" +Accept-Encoding = "gzip, deflate, br" +Connection = "keep-alive" \ No newline at end of file diff --git a/js/login.js b/js/login.js new file mode 100644 index 0000000..0bb2ab8 --- /dev/null +++ b/js/login.js @@ -0,0 +1,72 @@ +const axios = require('axios') +const qs = require('qs') +const cookie = require('cookie') +const fs = require('node:fs/promises') +const path = require('node:path') +const toml = require('@iarna/toml') + +// 登录成功后, 更新config文件, 同时返回新的config对象 +const login = async oldConfig => { + console.log('登录过期, 重新登录中...') + const { loginUrl, email, password } = oldConfig + const data = qs.stringify({ email, passwd: password }) + const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'sec-ch-ua': oldConfig.headers['sec-ch-ua'], + 'sec-ch-ua-platform': oldConfig.headers['sec-ch-ua-platform'], + 'User-Agent': oldConfig.headers['User-Agent'], + } + let res + res = await axios({ + url: loginUrl, + method: 'post', + headers, + data, + maxRedirects: 0, + timeout: 10000, + }) + console.log('res', res) + if (res.status == 200) { + console.log('res.data.msg', res.data.msg) + let cookieObj = {} + const setCookie = res.headers['set-cookie'] // array + setCookie.forEach(item => { + cookieObj = Object.assign(cookieObj, cookie.parse(item)) + }) + const cookieStr = qs.stringify( + { + lang: 'zh-cn', + udi: cookieObj.uid, + email: cookieObj.email, + key: cookieObj.key, + ip: cookieObj.ip, + expire_in: cookieObj['expire_in'], + }, + { delimiter: '; ' } + ) + const newConfig = JSON.parse(JSON.stringify(oldConfig)) + newConfig.headers.Cookie = cookieStr + newConfig['expire_in'] = cookieObj['expire_in'] + // 如果 headers里还有expire_in, 也要更新 + if (newConfig.headers.expire_in) { + newConfig.headers['expire_in'] = cookieObj['expire_in'] + } + console.log('newConfig', newConfig) + updateConfig(newConfig) // 异步更新 + return newConfig + } +} + +// 更新config文件 +const updateConfig = async newConfig => { + const filePath = path.resolve( + __dirname, + '../config', + newConfig.fileName + (newConfig.fileName.endsWith('.toml') ? '' : '.toml') + ) + console.log('filePath', filePath) + await fs.writeFile(filePath, toml.stringify(newConfig), 'utf8') + console.log(`更新 ${newConfig.fileName} 文件成功`) +} + +module.exports = { login } diff --git a/js/request.js b/js/request.js new file mode 100644 index 0000000..4b4396b --- /dev/null +++ b/js/request.js @@ -0,0 +1,27 @@ +const axios = require('axios') +const { getNowTime } = require('./tools') + +const request = async (url, headers) => { + console.log(`\n\n------${getNowTime()} - ${url}:开始签到------\n`) + try { + const res = await axios({ + url, + method: 'post', + headers, + timeout: 10000, + }) + if (res?.data?.ret === 0) { + console.log(`------ ${getNowTime()} 签到成功 ------\n`) + console.log(res.data.msg) + } else { + console.log(`------ ${getNowTime()} 签到失败 ------\n`) + console.log(JSON.stringify(res)) + } + } catch (error) { + console.log(error?.message) + } finally{ + return new Promise(resolve => resolve()) + } +} + +module.exports = { request } diff --git a/js/tools.js b/js/tools.js new file mode 100644 index 0000000..8f8adba --- /dev/null +++ b/js/tools.js @@ -0,0 +1,48 @@ +const fs = require('node:fs/promises') +const path = require('node:path') +const TOML = require('@iarna/toml') + +const getNowTime = () => { + return new Date()['toLocaleDateString']() +} + +// 监听文件变化 + +// 遍历配置文件目录下有多少个配置文件, 返回数组 +const configPaths = async () => { + try { + let list = [] + const files = await fs.readdir(path.resolve(__dirname, '../config')) + files.forEach(file => { + if (file.endsWith('.toml')) { + list.push(path.join(__dirname, '../config', file)) + } + }) + return list + } catch (error) { + console.log('遍历配置文件失败:\n', error) + } +} + +// 异步读取toml文件并返回对象 +const getConfig = async path => { + try { + const tomlData = await fs.readFile(path, 'utf8') + const parsedData = TOML.parse(tomlData) + return parsedData + } catch (err) { + console.log('读取toml失败:\n', err) + } +} + +// 判断是否登录过期 +const isExpire = config => { + const now = Math.floor(Date.now() / 1000) + if (+config.expire_in < now) { + return true + } else { + return false + } +} + +module.exports= {isExpire, configPaths, getConfig, getNowTime} diff --git a/package.json b/package.json new file mode 100644 index 0000000..83a84ed --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "autoCheckin", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "node ./app.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@iarna/toml": "^2.2.5", + "axios": "^1.3.4", + "cookie": "^0.5.0", + "node-schedule": "^2.1.1", + "qs": "^6.11.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3cc525c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,166 @@ +lockfileVersion: 5.4 + +specifiers: + '@iarna/toml': ^2.2.5 + axios: ^1.3.4 + cookie: ^0.5.0 + node-schedule: ^2.1.1 + qs: ^6.11.1 + +dependencies: + '@iarna/toml': 2.2.5 + axios: 1.3.4 + cookie: 0.5.0 + node-schedule: 2.1.1 + qs: 6.11.1 + +packages: + + /@iarna/toml/2.2.5: + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + dev: false + + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /axios/1.3.4: + resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + dev: false + + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /cookie/0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /cron-parser/4.8.1: + resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==} + engines: {node: '>=12.0.0'} + dependencies: + luxon: 3.3.0 + dev: false + + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /follow-redirects/1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: false + + /get-intrinsic/1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: false + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: false + + /long-timeout/0.1.1: + resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} + dev: false + + /luxon/3.3.0: + resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==} + engines: {node: '>=12'} + dev: false + + /mime-db/1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types/2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /node-schedule/2.1.1: + resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} + engines: {node: '>=6'} + dependencies: + cron-parser: 4.8.1 + long-timeout: 0.1.1 + sorted-array-functions: 1.3.0 + dev: false + + /object-inspect/1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: false + + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + + /qs/6.11.1: + resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + dev: false + + /sorted-array-functions/1.3.0: + resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} + dev: false