├── .gitignore ├── verifycode.png ├── imgs ├── chaoxing1.png └── chaoxing2.png ├── package.json ├── main.js ├── courselist.js ├── jobTask.js ├── logger.js ├── readme.md ├── player.js ├── courseTask.js ├── loginer.js ├── net.js ├── course.js ├── chapterTask.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | verifycode.png 3 | -------------------------------------------------------------------------------- /verifycode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainySY/chaoxing-xuexitong-autoflush/HEAD/verifycode.png -------------------------------------------------------------------------------- /imgs/chaoxing1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainySY/chaoxing-xuexitong-autoflush/HEAD/imgs/chaoxing1.png -------------------------------------------------------------------------------- /imgs/chaoxing2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainySY/chaoxing-xuexitong-autoflush/HEAD/imgs/chaoxing2.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chaoxing", 3 | "version": "1.0.0", 4 | "main": "main.js", 5 | "author": "854185073@qq.com", 6 | "license": "MIT", 7 | "dependencies": { 8 | "cheerio": "^1.0.0-rc.3", 9 | "clui-live": "^0.3.4", 10 | "node-fetch": "^2.6.0", 11 | "open": "^7.0.3", 12 | "prompts": "^2.3.2", 13 | "tough-cookie": "^4.0.0" 14 | }, 15 | "scripts": { 16 | "start": "node main.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | console.log("版权声明:"); 2 | console.log("作者: Wyatt Zheng"); 3 | console.log("邮箱: yuxon@qq.com"); 4 | console.log("\n"); 5 | console.log("本作品仅提供学习及研究程序原理用途\n\n"); 6 | 7 | 8 | 9 | let Loginer=require("./loginer.js"); 10 | let task=require("./courseTask.js"); 11 | let prompt=require("prompts"); 12 | 13 | 14 | new Loginer().login().then(async(ck)=>{ 15 | // console.log(ck) 16 | let {speed}=await prompt({type:"number",name:"speed",message:"请输入刷课速率(不填默认为2)"}); 17 | if(!speed)speed=2.0; 18 | 19 | new task(ck,speed); 20 | }).catch((e)=>{ 21 | console.log(e); 22 | }); 23 | 24 | 25 | -------------------------------------------------------------------------------- /courselist.js: -------------------------------------------------------------------------------- 1 | var cheerio=require("cheerio"); 2 | var url=require("url"); 3 | var qs=require("querystring"); 4 | 5 | class courselist{ 6 | constructor(user){ 7 | this.user=user; 8 | } 9 | async getList(){ 10 | let $=cheerio.load(await this.user.net.get("visit/interaction")); 11 | let courses=$(".Mconright"); 12 | let built=[]; 13 | for(let i=0;inew Promise((y)=>setTimeout(y,t)); 2 | var Player=require("./player.js"); 3 | 4 | class jobTask{ 5 | constructor(classid,jobs,user,playerspeed){ 6 | this.user=user; 7 | this.rawjobs=jobs.concat([]); 8 | this.jobs=jobs; 9 | this.taskend=false; 10 | this.clazzId=classid; 11 | this.index=0; 12 | this.currentPlayer=undefined; 13 | this.playerspeed=playerspeed; 14 | this.eventLoop(); 15 | } 16 | async wait(timeout){ 17 | let tick=0; 18 | if(!timeout)timeout=9999999; 19 | while(true){ 20 | if(this.taskend) 21 | return; 22 | 23 | if(tick++>timeout)return; 24 | await sleep(500); 25 | } 26 | } 27 | getJobProgress(){ 28 | return {current:this.index,total:this.rawjobs.length}; 29 | } 30 | getStatusInfo(){ 31 | if(!this.currentPlayer)return "播放器就绪中...\n\n"; 32 | return this.currentPlayer.getStatusInfo(); 33 | 34 | } 35 | async doTick(){ 36 | let job=this.jobs.shift(); 37 | if(!job){this.taskend=true;return;} 38 | 39 | 40 | if(!job.isPassed){ 41 | 42 | let player=new Player(this.clazzId,this.user,job,this.playerspeed); 43 | this.currentPlayer=player; 44 | 45 | await player.wait(); 46 | } 47 | this.index++; 48 | } 49 | async eventLoop(){ 50 | while(!this.taskend){ 51 | try{ 52 | await this.doTick(); 53 | }catch(e){ 54 | console.log(e); 55 | } 56 | await sleep(1000); 57 | } 58 | } 59 | } 60 | 61 | 62 | 63 | module.exports=jobTask; 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /logger.js: -------------------------------------------------------------------------------- 1 | var Net=require("./net.js"); 2 | 3 | class Logger{ 4 | constructor(classid,user){ 5 | this.user=user; 6 | this.clazzId=classid; 7 | } 8 | async sendLog(ruri,otherInfo,chapterId,jobid,objectId,current,status,isDrag){ 9 | //let otherInfo="nodeId_"+chapterId+"-cpi_82274641"; 10 | 11 | //console.log(status) 12 | let duration=status.duration; 13 | let dtoken=status.dtoken; 14 | 15 | 16 | let enc=this.encode(jobid,objectId,current,duration); 17 | let reportUrl=`${ruri}/${dtoken}?clazzId=${this.clazzId}&playingTime=${current}&duration=${duration}&clipTime=0_${duration}&objectId=${objectId}&otherInfo=${otherInfo}&jobid=${jobid}&userid=${this.user.userid}&isdrag=${isDrag}&view=pc&enc=${enc}&rt=0.9&dtype=Video&_t=`+new Date().getTime(); 18 | 19 | //console.log(reportUrl) 20 | //console.log(reportUrl) 21 | // console.log(this.user.net.getCookies()); 22 | return await this.user.net.rawGet(reportUrl); 23 | } 24 | encode(jobid,objectId,current,duration){//学习通加密算法 25 | //MD5([clazzId][userid][jobid][objectId][currentMs][d_yHJ!$pdA~5][durationMs][clipTime]) 26 | //clazzId=班级id 27 | //userid=用户id 28 | //jobid=任务点id 29 | //objectId=资源id 30 | //currentMs=当前看到的进度位置,单位毫秒 31 | //d_yHJ!$pdA~5=magicCode=固定码 32 | //durationMs=总时长,单位毫秒 33 | //clipTime=0_duration 34 | 35 | let clipTime="0_"+duration; 36 | let built=`[${this.clazzId}][${this.user.userid}][${jobid}][${objectId}][${current*1000}][d_yHJ!$pdA~5][${duration*1000}][${clipTime}]`; 37 | 38 | let enc=require("crypto").createHash("md5").update(built).digest("hex"); 39 | return enc; 40 | } 41 | } 42 | /* 43 | let net=new Net("https://mooc1-1.chaoxing.com/"); 44 | 45 | let logger=new Logger({ 46 | userid:"94374572", 47 | clazzId:"13610019", 48 | net 49 | }); 50 | logger.sendLog("1566395970781740","8a44bf2eb4907915f771f2d82055bd30",289).then(console.log); 51 | */ 52 | //.encode("1566395970781740","8a44bf2eb4907915f771f2d82055bd30","404","803")) 53 | 54 | 55 | 56 | module.exports=Logger; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 超星学习通全自动刷课程序 2 | 3 | **发布日期: 2020-3-22** 4 | 5 | --- 6 | 7 | 2020-3-23 更新内容: 8 | 9 | ①彻底修复有时刷完视频,无法成功完成任务点的BUG 10 | ②新增任意调倍速功能,以超快的速度刷完所有章节 11 | 12 | 预计下个版本新增功能: 13 | 14 | ①激进模式:将以最快可能的速度和策略把所有视频解决。 15 | 16 | --- 17 | 18 | 声明: 本程序仅供学习研究程序工程原理 19 | 20 | ![Image text](https://raw.githubusercontent.com/ZhyMc/chaoxing-xuexitong-autoflush/master/imgs/chaoxing1.png) 21 | 22 | ![Image text](https://raw.githubusercontent.com/ZhyMc/chaoxing-xuexitong-autoflush/master/imgs/chaoxing2.png) 23 | 24 | --- 25 | ``` 26 | 若该程序对你有帮助,请给作者一颗star以表示鼓励,谢谢! 27 | ``` 28 | 29 | --- 30 | 31 | 用法: 32 | 33 | --- 34 | 35 | ① 36 | ``` 37 | git clone https://github.com/ZhyMc/chaoxing-xuexitong-autoflush 38 | ``` 39 | ② 40 | ``` 41 | cd chaoxing-xuexitong-autoflush (到程序目录底下) 42 | ``` 43 | ③ 44 | ``` 45 | npm install 46 | ``` 47 | 48 | 之后你就可以通过执行 49 | 50 | ``` 51 | npm start 52 | ``` 53 | 54 | 启动本程序 55 | 56 | --- 57 | 如果看不懂上面的步骤(上面步骤需要一点编程知识),你可以参照详细使用步骤: 58 | 59 | Windows (x64)系统: 60 | 61 | ①进入 http://nodejs.cn/download/ 下载 nodejs 的 "windows安装包" 62 | ②打开安装包并安装,以保证你的机器拥有 Node.js 运行环境 63 | ③进入 http://npm.taobao.org/mirrors/git-for-windows/v2.26.0-rc2.windows.1/ 下载 git 安装包 64 | 你可以选择此版本:Git-2.26.0-rc2-64-bit.exe 65 | ④打开安装包并安装,以保证你的机器拥有 Git 环境。 66 | 67 | ⑤打开命令行,你可以按Win键+R,然后输入cmd打开 68 | ⑥依次敲入命令并执行 69 | 70 | ``` 71 | git clone https://github.com/ZhyMc/chaoxing-xuexitong-autoflush 72 | ``` 73 | ``` 74 | cd chaoxing-xuexitong-autoflush 75 | ``` 76 | ``` 77 | npm install 78 | ``` 79 | 每次执行请等待命令的执行完毕再运行下一个命令 80 | 然后通过运行命令 81 | ``` 82 | npm start 83 | ``` 84 | 启动本程序 85 | 86 | ⑦之后你都可以通过打开cmd (Win+R) 再运行 87 | ``` 88 | cd chaoxing-xuexitong-autoflush 89 | ``` 90 | ``` 91 | npm start 92 | ``` 93 | 的方式启动本程序 94 | 95 | --- 96 | 如果你觉得上面的步骤还是太麻烦,我直接提供了本程序的.exe(可执行文件,用nexe工程打包),可以直接执行 97 | 下载地址:https://share.weiyun.com/5WZoFlQ 98 | 99 | --- 100 | 说明: 101 | ``` 102 | 本程序会自动遍历你的所有课程,课程的所有章节,甚至包括上锁的章节内的视频都会被自动刷完。 103 | 所以你可以挂机本程序至后台。因为由 Node.js 平台编写,你可以在Linux下运行本程序. 104 | ``` -------------------------------------------------------------------------------- /player.js: -------------------------------------------------------------------------------- 1 | var sleep=(t)=>new Promise((y)=>setTimeout(y,t)); 2 | var Logger=require("./logger.js"); 3 | 4 | class Player{ 5 | constructor(classid,user,video,speed){ 6 | this.playing=video; 7 | this.progress=0; 8 | this.end=false; 9 | this.user=user; 10 | this.logger=new Logger(classid,user); 11 | this.statusinfo=""; 12 | if(!speed)speed=1.0;//倍速 13 | this.speed=speed; 14 | 15 | this.reportTimeInterval=60*1000; 16 | this.tick=this.reportTimeInterval;//确保视频刚开始report一次 17 | 18 | this.simspeed=5*1000;//更新速度; 19 | this.eventLoop(); 20 | } 21 | async wait(timeout){ 22 | let tick=0; 23 | if(!timeout)timeout=9999999; 24 | while(true){ 25 | if(this.end) 26 | return; 27 | 28 | if(tick++>timeout)return; 29 | await sleep(500); 30 | } 31 | 32 | } 33 | async init(){ 34 | 35 | } 36 | getStatusInfo(){ 37 | return this.statusinfo+"\n\n"; 38 | } 39 | async doLog(status,isDrag){ 40 | return await this.logger.sendLog(this.playing.ruri,this.playing.otherInfo,this.playing.chapid,this.playing.jobid,this.playing.objectId,parseInt(this.progress/1000),status,isDrag); 41 | } 42 | async doTick(){ 43 | 44 | let status=JSON.parse(await this.user.net.rawGet(`ananas/status/${this.playing.objectId}?k=9790&flag=normal&_dc=`+new Date().getTime())); 45 | 46 | let duration=status.duration; 47 | // if(duration<60*6) //小于6分钟的完整看完 48 | // this.speed=1; 49 | 50 | 51 | let loginfo=`下次报告: ${(this.reportTimeInterval - this.tick)/1000}s`; 52 | 53 | if(this.progress/1000>=duration) 54 | { 55 | this.progress=duration*1000; 56 | this.end=true; 57 | loginfo=await this.doLog(status,4); 58 | } 59 | if(this.tick>=this.reportTimeInterval){//时间达到需要log的时刻,sendLog一次 60 | this.tick=0; 61 | loginfo=await this.doLog(status,0); 62 | } 63 | /*try{ 64 | if(JSON.parse(loginfo).isPassed==true); 65 | this.end=true; 66 | }catch(e){}*/ 67 | //if(this.tick++%10==0) 68 | this.statusinfo="正在 "+this.speed.toFixed(1)+" 倍速播放: "+this.playing.property.name+" "+((this.progress/1000)/duration*100).toFixed(2)+"% "+((this.progress/1000)+"/"+duration)+" "+loginfo; 69 | this.progress+=parseInt(this.speed*this.simspeed); 70 | 71 | } 72 | async eventLoop(){//自动消费播放队列 73 | while(!this.end){ 74 | try{ 75 | await this.doTick(); 76 | this.tick+=this.simspeed; 77 | }catch(e){ 78 | console.log(e); 79 | } 80 | await sleep(this.simspeed); 81 | } 82 | } 83 | } 84 | 85 | 86 | module.exports=Player; -------------------------------------------------------------------------------- /courseTask.js: -------------------------------------------------------------------------------- 1 | var sleep=(t)=>new Promise((y)=>setTimeout(y,t)); 2 | var Net=require("./net.js"); 3 | var CourseList=require("./courselist.js"); 4 | var chapterTask=require("./chapterTask.js"); 5 | var Course=require("./course.js"); 6 | var {LiveContainer,LiveArea}=require("clui-live"); 7 | 8 | class courseTask{ 9 | constructor(cookie,speed){ 10 | this.taskend=false; 11 | this.domain="https://mooc1-1.chaoxing.com/"; 12 | this.cookie=cookie; 13 | this.speed=speed; 14 | this.current=undefined; 15 | setInterval(()=>{ 16 | this.drawGUI(); 17 | },1000) 18 | this.eventLoop(); 19 | 20 | } 21 | async init(){ 22 | this.net=new Net(this.domain); 23 | await this.net.setCookie(this.cookie); 24 | this.userid=Net.parseCookies(this.cookie)["_uid"]; 25 | 26 | this.user={ 27 | userid:this.userid, 28 | cookie:this.cookie, 29 | net:this.net 30 | } 31 | this.courses=[]; 32 | await this.fillCourses(); 33 | 34 | console.log("\n".repeat(30));//清屏 35 | 36 | this.gui=new LiveContainer(); 37 | this.gui_area=this.gui.createLiveArea(); 38 | 39 | } 40 | async drawGUI(){ 41 | if(!this.current)return; 42 | 43 | let nextcourse=this.courses[0]; 44 | 45 | let minheight=30; 46 | 47 | let lines=this.current.getGUI(); 48 | lines.unshift("当前课程:"+(this.current_course?this.current_course.title:"无")+" 下一个课程:"+(nextcourse?nextcourse.title:"无")+"\n"); 49 | 50 | if(lines.lengthtimeout)return; 85 | await sleep(500); 86 | } 87 | } 88 | 89 | async eventLoop(){ 90 | await this.init(); 91 | while(!this.taskend){ 92 | try{ 93 | await this.doTick(); 94 | }catch(e){ 95 | console.log(e); 96 | } 97 | await sleep(1000); 98 | } 99 | } 100 | 101 | } 102 | 103 | 104 | 105 | module.exports=courseTask; 106 | -------------------------------------------------------------------------------- /loginer.js: -------------------------------------------------------------------------------- 1 | let fs=require("fs"); 2 | let open=require("open"); 3 | let prompt=require("prompts"); 4 | let cheerio=require("cheerio"); 5 | let Net=require("./net.js"); 6 | 7 | class Loginer{//登录器 8 | constructor(){ 9 | this.net=new Net("http://www.xuexi365.com/"); 10 | this.imgurl=__dirname+"/verifycode.png"; 11 | // this.login(); 12 | } 13 | async login(){//设置各种引导,最终返回登录Cookie 14 | 15 | while(true){ 16 | let acc={}; 17 | acc=await prompt([ 18 | {type:"text",name:"uname",message:"请输入超星账户手机号"}, 19 | {type:"text",name:"password",message:"请输入账户密码"}, 20 | ]) 21 | if(!acc.uname || !acc.password)//填空代表退出登录 22 | throw "输入为空,退出程序" 23 | 24 | let vercode=await this.getNumcode(); 25 | try{ 26 | await this.doLogin(acc.uname,acc.password,vercode); 27 | break; 28 | }catch(e){ 29 | console.log(`登录失败: ${e},请重试!\n\n`); 30 | 31 | } 32 | } 33 | 34 | //这里处理Cookie,保留重要的部分 35 | let vc3spliter={ 36 | start:0, 37 | end:0, 38 | vc3:"", 39 | }; 40 | 41 | let ck=await this.net.getCookies(); 42 | 43 | vc3spliter.start=ck.indexOf(", vc3="); 44 | vc3spliter.end=ck.indexOf(";",vc3spliter.start+1); 45 | vc3spliter.vc3=ck.substring(vc3spliter.start+", vc3=".length,vc3spliter.end); 46 | 47 | let uidspliter={ 48 | start:0, 49 | end:0, 50 | uid:"", 51 | }; 52 | 53 | uidspliter.start=ck.indexOf(", _uid="); 54 | uidspliter.end=ck.indexOf(";",uidspliter.start+1); 55 | uidspliter.uid=ck.substring(uidspliter.start+", _uid=".length,uidspliter.end); 56 | 57 | let dspliter={ 58 | start:0, 59 | end:0, 60 | d:"", 61 | }; 62 | 63 | dspliter.start=ck.indexOf(", _d="); 64 | dspliter.end=ck.indexOf(";",dspliter.start+1); 65 | dspliter.d=ck.substring(dspliter.start+", _d=".length,dspliter.end); 66 | 67 | let vc3=vc3spliter.vc3; 68 | let uid=uidspliter.uid; 69 | let d=dspliter.d; 70 | 71 | 72 | return `vc3=${vc3}; _uid=${uid}; _d=${d}; `; 73 | } 74 | async getNumcode(){ 75 | let img=await this.net.getBin("num/code",true); 76 | fs.writeFileSync(this.imgurl,img); 77 | open(this.imgurl); 78 | let ret=await prompt({type:"text",name:"vercode",message:"已弹出验证码,请在这输入验证码"}); 79 | return ret["vercode"]; 80 | } 81 | async doLogin(uname,password,numcode){//返回成功或失败 82 | //cookie中有三个关键值特别重要 83 | //_uid、_d、vc3 84 | 85 | let info=await this.net.post("cxlogin?refer=http%3A%2F%2Fi.mooc.chaoxing.com",{ 86 | refer_0x001:"http%3A%2F%2Fi.mooc.chaoxing.com", 87 | pid:-1, 88 | pidName:"", 89 | fid:9136, 90 | fidName:"超星个人网", 91 | allowJoin:1, 92 | isCheckNumCode:1, 93 | f:0, 94 | uname, 95 | password, 96 | numcode 97 | },true); 98 | let ck=await this.net.getCookies(); 99 | //console.log(ck,info); 100 | // console.log(ck,uid,d,vc3); 101 | 102 | if(ck.indexOf("userinfo")!=-1) 103 | return; 104 | else{ 105 | let $=cheerio.load(info); 106 | let error=$("#show_error").text(); 107 | //console.log("error",error); 108 | throw error; 109 | } 110 | } 111 | } 112 | 113 | 114 | 115 | module.exports=Loginer; -------------------------------------------------------------------------------- /net.js: -------------------------------------------------------------------------------- 1 | var url=require("url"); 2 | var qs=require("querystring"); 3 | 4 | var {Cookie,Store,CookieJar}=require("tough-cookie"); 5 | 6 | var fetch=require("node-fetch"); 7 | class Net{ 8 | constructor(host){ 9 | let parsed=url.parse(host); 10 | this.base=parsed; 11 | this.jar=new CookieJar(); 12 | 13 | } 14 | setCookie(str){ 15 | return new Promise((y)=>{ 16 | this.jar.store.putCookie(str,y); 17 | }) 18 | } 19 | 20 | getCookies(){ 21 | return new Promise((y)=>{ 22 | return this.jar.store.getAllCookies((err,data)=>y(data[0])); 23 | }) 24 | } 25 | async get(page,query){ 26 | if(!query)query={}; 27 | let obj=Object.assign(this.base,{ 28 | pathname:page, 29 | query 30 | }); 31 | 32 | let res=await fetch(url.format(obj),{ 33 | headers:{ 34 | "Cookie":await this.getCookies(), 35 | "User-Agent":Net.UserAgent 36 | } 37 | }); 38 | //if(res.headers.has("set-cookie")) 39 | //await this.setCookie(res.headers.get("set-cookie")); 40 | 41 | return res.text(); 42 | } 43 | async getJSON(page,query){ 44 | return JSON.parse(await this.get(page,query)); 45 | } 46 | async rawGet(page,savecookie){ 47 | let res=await fetch(this.base.href+page,{ 48 | headers:{ 49 | "Cookie":await this.getCookies(), 50 | "User-Agent":Net.UserAgent 51 | } 52 | }); 53 | if(savecookie) 54 | if(res.headers.has("set-cookie")) 55 | await this.setCookie(res.headers.get("set-cookie")); 56 | 57 | return res.text(); 58 | } 59 | async getBin(page,savecookie){ 60 | let res=await fetch(this.base.href+page,{ 61 | headers:{ 62 | "Cookie":await this.getCookies(), 63 | "User-Agent":Net.UserAgent 64 | } 65 | }); 66 | if(savecookie) 67 | if(res.headers.has("set-cookie")) 68 | await this.setCookie(res.headers.get("set-cookie")); 69 | 70 | return res.buffer(); 71 | 72 | } 73 | async post(page,query,savecookie){ 74 | if(!query)query={}; 75 | 76 | let params = new URLSearchParams(); 77 | for(let i in query) 78 | params.append(i,query[i]); 79 | 80 | let res=await fetch(this.base.href+page,{ 81 | method:"POST", 82 | headers:{ 83 | "Content-Type": "application/x-www-form-urlencoded", 84 | "Cookie":await this.getCookies(), 85 | "User-Agent":Net.UserAgent, 86 | }, 87 | redirect: "manual", 88 | body:params 89 | }); 90 | 91 | 92 | //console.log(await res.text(),res.headers); 93 | 94 | if(savecookie) 95 | if(res.headers.has("set-cookie")) 96 | await this.setCookie(res.headers.get("set-cookie")); 97 | 98 | return res.text(); 99 | } 100 | } 101 | Net.parseCookies=function(cookies){ 102 | let arr=cookies.split("; "); 103 | let obj={}; 104 | for(let i in arr){ 105 | let item=arr[i].split("="); 106 | obj[item[0]]=item[1]; 107 | } 108 | return obj; 109 | } 110 | Net.UserAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"; 111 | 112 | module.exports=Net; 113 | 114 | //let net=new Net("http://www.baidu.com/"); 115 | 116 | //net.get("/").then(console.log); 117 | -------------------------------------------------------------------------------- /course.js: -------------------------------------------------------------------------------- 1 | var cheerio=require("cheerio"); 2 | var Net=require("./net.js"); 3 | class Course{ 4 | constructor(classId,courseid,user){ 5 | this.user=user; 6 | this.courseid=courseid; 7 | this.clazzId=classId; 8 | } 9 | async getChapters(){ 10 | let data=[]; 11 | 12 | let raw=await this.user.net.rawGet(`mycourse/studentstudycourselist?courseId=${this.courseid}&clazzid=${this.clazzId}`); 13 | 14 | let $=cheerio.load(raw); 15 | let chaps=$(".cells"); 16 | for(let i=0;i{ 62 | let card=await this.user.net.rawGet(`knowledge/cards?clazzid=${this.clazzId}&courseid=${this.courseid}&knowledgeid=${chapid}&num=${num}`); 63 | //console.log(card,`knowledge/cards?clazzid=${this.clazzId}&courseid=${this.courseid}&knowledgeid=${chapid}`); 64 | let loc1=card.indexOf(" mArg = "); 65 | let loc2=card.indexOf(";\n}catch(e){"); 66 | let raw=card.slice(loc1+10,loc2); 67 | try{ 68 | let jobs=JSON.parse(raw); 69 | let def=jobs.defaults; 70 | //console.log(def); 71 | let videos=[]; 72 | for(let i in jobs.attachments){ 73 | if(jobs.attachments[i].type=="video"){ 74 | let o=jobs.attachments[i]; 75 | o.chapid=chapid; 76 | o.ruri=def.reportUrl.replace("https://mooc1-1.chaoxing.com/",""); 77 | 78 | videos.push(o); 79 | } 80 | } 81 | return videos; 82 | }catch(e){ 83 | throw "End of cards" 84 | } 85 | } 86 | 87 | let vds=[];let count=0; 88 | do{ 89 | let vd=[]; 90 | try{ 91 | vd=await getCardVideos(count); 92 | }catch(e){ 93 | break;//到达最终卡片 94 | } 95 | vds=vds.concat(vd); 96 | 97 | count++; 98 | }while(true); 99 | //console.log(`总共${count}页`); 100 | return vds; 101 | } 102 | 103 | } 104 | 105 | module.exports=Course; -------------------------------------------------------------------------------- /chapterTask.js: -------------------------------------------------------------------------------- 1 | var sleep=(t)=>new Promise((y)=>setTimeout(y,t)); 2 | let Course=require("./course.js"); 3 | let jobTask=require("./jobTask.js") 4 | 5 | function reduceTree(tree,lvl){ 6 | if(!lvl)lvl=0; 7 | 8 | let ret=[]; 9 | for(let i in tree) 10 | { 11 | ret.push({id:tree[i].id,title:tree[i].title,courseid:tree[i].courseid,lvl}); 12 | if(tree[i].subchaps && tree[i].subchaps.length>0) 13 | ret=ret.concat(reduceTree(tree[i].subchaps,lvl+1)); 14 | } 15 | return ret; 16 | } 17 | class chapterTask{ 18 | constructor(classid,chapters,user,playerspeed){ 19 | 20 | let red=reduceTree(chapters); 21 | 22 | this.rawchapters=red.concat([]); 23 | this.chapters=red; 24 | this.user=user; 25 | this.taskend=false; 26 | this.clazzId=classid; 27 | 28 | this.current=undefined; 29 | this.current_task=undefined; 30 | this.playerspeed=playerspeed; 31 | 32 | this.eventLoop(); 33 | } 34 | 35 | async wait(timeout){ 36 | let tick=0; 37 | if(!timeout)timeout=9999999; 38 | while(true){ 39 | if(this.taskend) 40 | return; 41 | 42 | if(tick++>timeout)return; 43 | await sleep(500); 44 | } 45 | } 46 | getGUI(){ 47 | if(!this.current)return []; 48 | 49 | let built=[]; 50 | //built.push("\n\n\n\n\n\n\n\n\n\n"); 51 | built.push("\n"); 52 | let focus=-1; 53 | for(let i in this.rawchapters){ 54 | let iscurrent=this.rawchapters[i].id==this.current.id; 55 | if(iscurrent)focus=i; 56 | built.push(" ".repeat(this.rawchapters[i].lvl)+this.rawchapters[i].title+" "+(iscurrent?" <- ":"")+" "+(this.rawchapters[i].tipinfo || "")); 57 | } 58 | let offset=10; 59 | let start=parseInt(focus)-offset>0?(parseInt(focus)-offset):0; 60 | let end=parseInt(focus)+offset>built.length-1?(built.length-1):(parseInt(focus)+offset); 61 | 62 | let rebuilt=[]; 63 | 64 | for(let i in built){ 65 | if(parseInt(i)>start && parseInt(i)(x.id==this.current.id)).tipinfo=tipinfo; 79 | return tipinfo; 80 | } 81 | getStatusInfo(){ 82 | if(!this.current || ! this.current_task)return "就绪中..."; 83 | 84 | return " 进行中的章节 -> "+this.current.title+"\n\n"+ 85 | this.current_task.getStatusInfo(); 86 | } 87 | async studyChapter(chapter){ 88 | return this.user.net.post("mycourse/studentstudyAjax",{ 89 | courseId:chapter.courseid, 90 | clazzid:this.clazzId, 91 | chapterId:chapter.id, 92 | cpi:0, 93 | verificationcode:"" 94 | }) 95 | 96 | } 97 | async doTick(){ 98 | let chapter=this.chapters.shift(); 99 | this.current=chapter; 100 | if(!chapter){this.taskend=true;return;} 101 | 102 | await this.studyChapter(chapter); 103 | 104 | let course=new Course(this.clazzId,chapter.courseid,this.user); 105 | let jobs=await course.getVideoJobs(chapter.id); 106 | let task=new jobTask(this.clazzId,jobs,this.user,this.playerspeed); 107 | this.current_task=task; 108 | 109 | this.refreshTipInfo(); 110 | await task.wait(); 111 | this.refreshTipInfo(); 112 | 113 | } 114 | async eventLoop(){ 115 | while(!this.taskend){ 116 | try{ 117 | await this.doTick(); 118 | }catch(e){ 119 | console.log(e); 120 | } 121 | await sleep(1000); 122 | } 123 | } 124 | 125 | } 126 | 127 | module.exports=chapterTask; 128 | 129 | /* 130 | var Net=require("./net.js"); 131 | let net=new Net("https://mooc1-1.chaoxing.com/"); 132 | net.setCookie(``); 133 | let courselist=require("./courselist.js"); 134 | 135 | (async()=>{ 136 | let user={ 137 | userid:"94374572", 138 | net 139 | }; 140 | 141 | let classid="13132734"; 142 | let courseid="206211149"; 143 | 144 | let cs=new Course(classid,courseid,user); 145 | let cps=await cs.getChapters(); 146 | console.log(cps); 147 | //new chapterTask(classid,cps,user); 148 | 149 | console.log(await (new courselist(user)).getList()); 150 | 151 | })(); 152 | */ 153 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@*": 6 | version "13.9.3" 7 | resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d" 8 | integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA== 9 | 10 | ansi-escapes@^3.1.0: 11 | version "3.2.0" 12 | resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" 13 | integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== 14 | 15 | ansi-regex@^3.0.0: 16 | version "3.0.0" 17 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 18 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 19 | 20 | boolbase@~1.0.0: 21 | version "1.0.0" 22 | resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 23 | integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= 24 | 25 | cheerio@^1.0.0-rc.3: 26 | version "1.0.0-rc.3" 27 | resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" 28 | integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== 29 | dependencies: 30 | css-select "~1.2.0" 31 | dom-serializer "~0.1.1" 32 | entities "~1.1.1" 33 | htmlparser2 "^3.9.1" 34 | lodash "^4.15.0" 35 | parse5 "^3.0.1" 36 | 37 | clui-live@^0.3.4: 38 | version "0.3.4" 39 | resolved "https://registry.npmjs.org/clui-live/-/clui-live-0.3.4.tgz#af9f1141218224012fe47d17dcacb65cbf612a34" 40 | integrity sha512-iYHIezBTDhRgkK3WXjcZLDt8c4uqby7ngzrhi9897Tr7HzXhd785pa3hIyWu3iqU7u1UT20bfeKowHNS/aA3iQ== 41 | dependencies: 42 | ansi-escapes "^3.1.0" 43 | strip-ansi "^4.0.0" 44 | term-size "^1.2.0" 45 | 46 | cross-spawn@^5.0.1: 47 | version "5.1.0" 48 | resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 49 | integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= 50 | dependencies: 51 | lru-cache "^4.0.1" 52 | shebang-command "^1.2.0" 53 | which "^1.2.9" 54 | 55 | css-select@~1.2.0: 56 | version "1.2.0" 57 | resolved "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" 58 | integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= 59 | dependencies: 60 | boolbase "~1.0.0" 61 | css-what "2.1" 62 | domutils "1.5.1" 63 | nth-check "~1.0.1" 64 | 65 | css-what@2.1: 66 | version "2.1.3" 67 | resolved "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" 68 | integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== 69 | 70 | dom-serializer@0: 71 | version "0.2.2" 72 | resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" 73 | integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== 74 | dependencies: 75 | domelementtype "^2.0.1" 76 | entities "^2.0.0" 77 | 78 | dom-serializer@~0.1.1: 79 | version "0.1.1" 80 | resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" 81 | integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== 82 | dependencies: 83 | domelementtype "^1.3.0" 84 | entities "^1.1.1" 85 | 86 | domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: 87 | version "1.3.1" 88 | resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" 89 | integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== 90 | 91 | domelementtype@^2.0.1: 92 | version "2.0.1" 93 | resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" 94 | integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== 95 | 96 | domhandler@^2.3.0: 97 | version "2.4.2" 98 | resolved "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" 99 | integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== 100 | dependencies: 101 | domelementtype "1" 102 | 103 | domutils@1.5.1: 104 | version "1.5.1" 105 | resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" 106 | integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= 107 | dependencies: 108 | dom-serializer "0" 109 | domelementtype "1" 110 | 111 | domutils@^1.5.1: 112 | version "1.7.0" 113 | resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" 114 | integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== 115 | dependencies: 116 | dom-serializer "0" 117 | domelementtype "1" 118 | 119 | entities@^1.1.1, entities@~1.1.1: 120 | version "1.1.2" 121 | resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" 122 | integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== 123 | 124 | entities@^2.0.0: 125 | version "2.0.0" 126 | resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" 127 | integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== 128 | 129 | execa@^0.7.0: 130 | version "0.7.0" 131 | resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" 132 | integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= 133 | dependencies: 134 | cross-spawn "^5.0.1" 135 | get-stream "^3.0.0" 136 | is-stream "^1.1.0" 137 | npm-run-path "^2.0.0" 138 | p-finally "^1.0.0" 139 | signal-exit "^3.0.0" 140 | strip-eof "^1.0.0" 141 | 142 | get-stream@^3.0.0: 143 | version "3.0.0" 144 | resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" 145 | integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= 146 | 147 | htmlparser2@^3.9.1: 148 | version "3.10.1" 149 | resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" 150 | integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== 151 | dependencies: 152 | domelementtype "^1.3.1" 153 | domhandler "^2.3.0" 154 | domutils "^1.5.1" 155 | entities "^1.1.1" 156 | inherits "^2.0.1" 157 | readable-stream "^3.1.1" 158 | 159 | inherits@^2.0.1, inherits@^2.0.3: 160 | version "2.0.4" 161 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 162 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 163 | 164 | is-docker@^2.0.0: 165 | version "2.0.0" 166 | resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" 167 | integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== 168 | 169 | is-stream@^1.1.0: 170 | version "1.1.0" 171 | resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 172 | integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= 173 | 174 | is-wsl@^2.1.1: 175 | version "2.1.1" 176 | resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" 177 | integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== 178 | 179 | isexe@^2.0.0: 180 | version "2.0.0" 181 | resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 182 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 183 | 184 | kleur@^3.0.3: 185 | version "3.0.3" 186 | resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" 187 | integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== 188 | 189 | lodash@^4.15.0: 190 | version "4.17.15" 191 | resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 192 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 193 | 194 | lru-cache@^4.0.1: 195 | version "4.1.5" 196 | resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" 197 | integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== 198 | dependencies: 199 | pseudomap "^1.0.2" 200 | yallist "^2.1.2" 201 | 202 | node-fetch@^2.6.0: 203 | version "2.6.0" 204 | resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" 205 | integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== 206 | 207 | npm-run-path@^2.0.0: 208 | version "2.0.2" 209 | resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 210 | integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= 211 | dependencies: 212 | path-key "^2.0.0" 213 | 214 | nth-check@~1.0.1: 215 | version "1.0.2" 216 | resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" 217 | integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== 218 | dependencies: 219 | boolbase "~1.0.0" 220 | 221 | open@^7.0.3: 222 | version "7.0.3" 223 | resolved "https://registry.npmjs.org/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" 224 | integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== 225 | dependencies: 226 | is-docker "^2.0.0" 227 | is-wsl "^2.1.1" 228 | 229 | p-finally@^1.0.0: 230 | version "1.0.0" 231 | resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 232 | integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= 233 | 234 | parse5@^3.0.1: 235 | version "3.0.3" 236 | resolved "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" 237 | integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== 238 | dependencies: 239 | "@types/node" "*" 240 | 241 | path-key@^2.0.0: 242 | version "2.0.1" 243 | resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 244 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 245 | 246 | prompts@^2.3.2: 247 | version "2.3.2" 248 | resolved "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" 249 | integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== 250 | dependencies: 251 | kleur "^3.0.3" 252 | sisteransi "^1.0.4" 253 | 254 | pseudomap@^1.0.2: 255 | version "1.0.2" 256 | resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 257 | integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= 258 | 259 | psl@^1.1.33: 260 | version "1.7.0" 261 | resolved "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" 262 | integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== 263 | 264 | punycode@^2.1.1: 265 | version "2.1.1" 266 | resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 267 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 268 | 269 | readable-stream@^3.1.1: 270 | version "3.6.0" 271 | resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 272 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 273 | dependencies: 274 | inherits "^2.0.3" 275 | string_decoder "^1.1.1" 276 | util-deprecate "^1.0.1" 277 | 278 | safe-buffer@~5.2.0: 279 | version "5.2.0" 280 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 281 | integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== 282 | 283 | shebang-command@^1.2.0: 284 | version "1.2.0" 285 | resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 286 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 287 | dependencies: 288 | shebang-regex "^1.0.0" 289 | 290 | shebang-regex@^1.0.0: 291 | version "1.0.0" 292 | resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 293 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 294 | 295 | signal-exit@^3.0.0: 296 | version "3.0.2" 297 | resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 298 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 299 | 300 | sisteransi@^1.0.4: 301 | version "1.0.5" 302 | resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" 303 | integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== 304 | 305 | string_decoder@^1.1.1: 306 | version "1.3.0" 307 | resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 308 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 309 | dependencies: 310 | safe-buffer "~5.2.0" 311 | 312 | strip-ansi@^4.0.0: 313 | version "4.0.0" 314 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 315 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 316 | dependencies: 317 | ansi-regex "^3.0.0" 318 | 319 | strip-eof@^1.0.0: 320 | version "1.0.0" 321 | resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 322 | integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= 323 | 324 | term-size@^1.2.0: 325 | version "1.2.0" 326 | resolved "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" 327 | integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= 328 | dependencies: 329 | execa "^0.7.0" 330 | 331 | tough-cookie@^4.0.0: 332 | version "4.0.0" 333 | resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" 334 | integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== 335 | dependencies: 336 | psl "^1.1.33" 337 | punycode "^2.1.1" 338 | universalify "^0.1.2" 339 | 340 | universalify@^0.1.2: 341 | version "0.1.2" 342 | resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 343 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 344 | 345 | util-deprecate@^1.0.1: 346 | version "1.0.2" 347 | resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 348 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 349 | 350 | which@^1.2.9: 351 | version "1.3.1" 352 | resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 353 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 354 | dependencies: 355 | isexe "^2.0.0" 356 | 357 | yallist@^2.1.2: 358 | version "2.1.2" 359 | resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 360 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= 361 | --------------------------------------------------------------------------------