实例介绍
【实例截图】
【核心代码】
#!/usr/bin/env node 'use strict'; /** * 控制train_data输出 */ const {debug, myUrl, baseUrl, mockHeaders, mimUsedTime: MIN_USED_TIME} = require('./config'); //解析命令行参数 let argv = require('yargs') .option('i', { alias: 'identity', demand: false, default: 'member', describe: '你的身份: member 或 people', type: 'string' }) .usage('Usage: sdBobot [options]')//用法格式 .example('sdBobot -i member', 'Your identity is party member.') .help('h') .alias('h', 'help') .alias('v', 'version') .epilog('Create by Jessewo | Copyright 2018')//出现在帮助信息的结尾 .argv; const request = require('superagent'); const fs = require('fs'); const log = require('./utils/logUtils'); const dateFormat = require('./utils/dateUtils'); const random = require('./utils/randomUtils'); //登录模块 const login = require('./login'); //控制台键盘输入读取 const readlineSync = require('readline-sync'); const DIVIDER = '----------------------------------------------------------------'; let failureList; let startTime; /** * mock 点击数据 * 下一题按钮 * 左上角(552,804) * 右下角(644,832) */ function mockHumanBehaviors(totalSubject) { let clickTimes = totalSubject - 1; let clientXArr = []; let clientXArrY = []; let maxArr = []; let obj = {}; for (let i = 0; i < clickTimes; i ) { let x = random(552, 644); clientXArr.push(x); let y = random(804, 832); clientXArrY.push(y); if (obj[x]) { obj[x] ; maxArr.push(obj[clientXArr[i]]); } else { obj[x] = 1 } } maxArr = maxArr.sort(function (x, y) { return x - y }); let repeatX = maxArr.length > 0 ? maxArr[maxArr.length - 1] : 0; //重复x 坐标的次数 return { sameNum: repeatX, clickX: clientXArr.join(','), clickY: clientXArrY.join(',') }; } /** * 查询答案 * @param userId * @param subjectInfoList * @returns {Promise<any>} */ function queryAnswer(userId, subjectInfoList) { log.d('答案查询中...'); return new Promise((resolve, reject) => { request .post(myUrl "/sdbeacononline/queryanswer") .send({userId, subjectInfoList}) .then(res => { let queryResult = res.body.data; log.i(queryResult.queryLog); resolve(queryResult); }) .catch(err => { log.e(err); }) }); } /** * 更新错题集 * @param {Iterable} failureList */ function updateFailureList(failureList) { request .post(myUrl "/sdbeacononline/updatefailurelist") .send(failureList) .then(res => log.d('错题上传成功!')) .catch(err => log.e(err)); } /** * 构建交卷的body * @param subject * @param queryResult * @returns {Promise<any>} */ function handleResult(subject, queryResult) { return new Promise((resolve, reject) => { const {recordId, roundOnlyId, orderId, totalSubject} = subject; let {answerList, failureMap} = queryResult; //对于查询失败的问题抛给用户 if (failureMap) { log.e(`有${Object.values(failureMap).length}个问题查询失败!\n`); for (let key in failureMap) { let index = parseInt(key); let item = failureMap[key]; const subjectTitle = item.subjectTitle; const subjectType = item.subjectType; const optionInfoList = item.optionInfoList; log.i(`${index 1}.[${subjectType === '0' ? '单选' : '多选'}] ${subjectTitle}`); optionInfoList.forEach(opt => log.i(`${opt.optionType}.${opt.optionTitle}`)); let inputStr = readlineSync.question('请输入答案(示例: 若单选则输入 A ;若多选则输入 ABC): ').trim().toUpperCase(); // // Handle the secret text (e.g. password). // var favFood = readlineSync.question('What is your favorite food? ', { // hideEchoBack: true // The typed text on screen is hidden by `*` (default). // }); while ((subjectType === '0' && !inputStr.match(/^[A-D]{1}$/g)) || (subjectType === '1' && !inputStr.match(/^[A-D]{1,4}$/g))) { inputStr = readlineSync.question('输入格式错误, 请重新输入:').trim().toUpperCase(); } let correctedOpts = inputStr.match(/./g).join(','); log.w(`您输入的是: ${correctedOpts}\n`); //将输入的答案添加到错题集里 item.answer = correctedOpts; for (let i = 0; i < inputStr.length; i ) { let c = inputStr.charAt(i); optionInfoList.forEach(opt => { if (opt.optionType.toUpperCase() == c) { opt.isRight = '1'; } else { opt.isRight = opt.isRight || '0'; } }); } //构建手动输入的结果 let answer = {}; answer.id = item.id; answer.answer = correctedOpts; //按照原来的顺序插入元素 answerList.splice(index, 0, answer); } failureList = Object.values(failureMap); } //模拟点击数据 let {sameNum, clickX, clickY} = mockHumanBehaviors(totalSubject); //构建交卷body let result = { recordId, roundOnlyId, orderId, subjectInfoList: answerList, sameNum, clickX, clickY }; let answer = readlineSync.question(`答题完成, 已耗时 ${getUsedTime()}秒, 是否交卷? (Y/N)`).trim().toUpperCase(); if (answer === 'Y') { let tips = ''; if (getUsedTime() < MIN_USED_TIME) { tips = `(建议大于${20 - Math.ceil(getUsedTime())}秒)`; } let delay = readlineSync.question(`请输入交卷延时${tips}: `).trim().toUpperCase(); while (!delay.match(/^[\d] $/g)) { delay = readlineSync.question('格式错误,请重新输入:').trim().toUpperCase(); } delay = Math.ceil(delay getUsedTime() >= MIN_USED_TIME ? delay : MIN_USED_TIME - getUsedTime()); //倒计时 let intervalId = setInterval(() => { log.w(`${delay--}秒后交卷...`); if (delay <= 0) { clearInterval(intervalId); resolve(result) } }, 1000); } else if (answer === 'N') { log.d('暂不交卷...'); } else { reject('输入错误, 暂不交卷...'); } }); } function getUsedTime() { return (new Date().getTime() - startTime) / 1000; } /** * 查询剩余答题次数 * @param userType * @returns {Promise<any>} */ function getLeftChance(userType, headers) { log.d(JSON.stringify(headers)); let q = {userType}; if (userType === 0) q.orgId = 0; return new Promise((resolve, reject) => { request .get(`${baseUrl}/quiz-api/game_info/user_left_chance`) .query(q) .set(headers) .then(res => { let {code, msg, success, data} = res.body; if (code === 200 && success) { resolve(data); } else { reject(`getLeftChance: ${code} ${msg}`); } }) .catch(err => log.e(err)); }); } /** * 查询排名和个人答题情况 * @param headers * @returns {Promise<any>} */ function getRankList(headers) { log.d(JSON.stringify(headers)); return new Promise((resolve, reject) => { request .get(`${baseUrl}/quiz-api/personal_rank/weblist?page=0&gameRoundId=`) .set(headers) .then(res => { let {code, msg, success, data} = res.body; if (code === 200 && success) { resolve(data); } else { reject(`getRankList: ${code} ${msg}`) } }); }); } /** * 获取正式的考试题目 * POST http://xxjs.dtdjzx.gov.cn/quiz-api/game_info/getGameSubject * header(登录校验) * user_hash:若群众则为手机号;若党员则 * system-type:web * @returns {Promise<any>} */ function getSubjectInfoList() { return new Promise((resolve, reject) => { log.d(JSON.stringify(mockHeaders)); request .post(`${baseUrl}/quiz-api/game_info/getGameSubject`) .set(mockHeaders) .then(res => { let {code, msg, success, data} = res.body; if (code === 200 && success) { //缓存试题 if (debug) { let jString = JSON.stringify(data); fs.writeFile(`train_data/subjectInfoList-${dateFormat('yyyyMMdd_HH-mm-ss')}.json`, jString, (err) => { if (err) { log.e(err); } else { log.d('试题缓存ok.'); } }); } startTime = new Date().getTime(); log.d(`开始答题, 共计${data.totalSubject}题.\n`); resolve(data); } else { reject(`getSubjectInfoList: ${code} Error ${msg}`); } }) .catch(err => log.e(err)); }); } /** * 交卷 * POST http://xxjs.dtdjzx.gov.cn/quiz-api/chapter_info/countScore * * 机器人校验 * 1. 记录点击[下一题按钮]的坐标 * 2. 记录重复X坐标的次数 * @param result * @returns {Promise<any>} */ function submit(result) { return new Promise((resolve, reject) => { log.d(JSON.stringify(mockHeaders)); log.d(JSON.stringify(result)); request .post(`${baseUrl}/quiz-api/chapter_info/countScore`) .set(mockHeaders) .send(result) .then(res => { let {code, msg, success, data} = res.body; if (code === 200 && success) { log.d(JSON.stringify(data)); let {recordId, useTime, totalScore, totalRight, totalWrong, overPercen} = data; let w_tt = useTime.split(':'); if (w_tt[0] !== '00') { useTime = w_tt[0] "小时" w_tt[1] "分" w_tt[2] "秒"; } else if (w_tt[1] !== '00') { useTime = w_tt[1] "分" w_tt[2] "秒"; } else { useTime = w_tt[2] "秒"; } log.i(DIVIDER); log.i('交卷成功!'); log.i(DIVIDER); log.i(`恭喜您! 超过了全省 ${overPercen} 的参赛者`); log.i(`总分: ${totalScore}, 用时${useTime}`); log.i(`正确: ${totalRight}, 错误: ${totalWrong}`); log.i(DIVIDER); resolve(totalScore); } else { reject(`交卷失败! ${code} Error ${msg}`); } }) .catch(err => log.e(err)); }); } async function printUserInfo(userType, hassh) { //查询个人信息的header let headers = Object.assign({}, mockHeaders); headers['Referer'] = `http://xxjs.dtdjzx.gov.cn/index.html?h=${hassh}`; let userInfo = await Promise.all([getLeftChance(userType, headers), getRankList(headers)]); let leftChance = userInfo[0]; let {rankList, selfScore, selfRank, rankme, nowTime} = userInfo[1]; log.i(DIVIDER); if (rankme) { let {id, orgName, totalScore, avgScore, avgTime, myRank, participationCount} = rankme; log.i(`${id}: ${orgName}.`); log.i(DIVIDER); log.i(`全省排名 ${myRank}`); log.i(`平均用时 ${avgTime}`); log.i(`答题次数 ${participationCount}`); log.i(`总得分 ${totalScore}`); log.i(`平均分 ${avgScore}`); } else { log.i(`${selfRank}: ${selfScore}.`); } log.i(DIVIDER); log.i(`[${nowTime}]`); log.i(DIVIDER); log.w(`您今天有${leftChance}次答题机会.`); return leftChance > 0; } function welcome() { return new Promise(resolve => { let confirm = readlineSync.question(`欢迎使用灯塔在线答题机器人sdBobot. 用机器代替人类去做简单重复性的劳动是大势所趋, 人类应该花更多的时间投入到更有价值和意义的事情中去, 比如推进个人成长,家庭幸福,事业进步. 开发此软件的目的仅是为了自用, 以及顺便学习node.js, 因此使用此软件答题造成的一切后果与开发者无关. 同意并继续使用请输入 Y 并回车,不同意请输入 N 后回车. `).trim().toUpperCase(); resolve(confirm === 'Y'); }); } async function main() { try { let agree = await welcome(); if (!agree) return; //登录 let {usetype, hassh, session} = await login(argv.i); //更新请求头 //答题用headers mockHeaders['user_hash'] = hassh; if (session) mockHeaders['Cookie'] = session; //查询个人信息 let allow = await printUserInfo(usetype, hassh); if (!allow) { log.e('今天答题次数已达上限或者不在答题时间!'); return; } //获取试题 let subject = await getSubjectInfoList(); //查询答案 let queryResult = await queryAnswer(hassh, subject.subjectInfoList); //请求交卷,交卷延时 let result = await handleResult(subject, queryResult); //交卷 let score = await submit(result); //满分则上传更新题库 if (score === 100 && failureList && failureList.length > 0) { updateFailureList(failureList); } } catch (e) { log.e(e); } } if (require.main === module) main();
标签:
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论