├── .gitignore ├── README.md ├── index.js ├── package.json └── webapi.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | yarn.lock 87 | package-lock.json 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 163_daka 2 | 3 | - 用于网易云音乐每日听歌打卡,执行一次即可刷歌300+ 4 | - 本工具仅用于学习研究,对于刷歌打卡的后果由用户自行承担 5 | 6 | ## 使用方法 7 | 8 | 需要 nodejs >= 8.0 9 | 10 | 在 index.js 里顶部添加你的网易云手机号和密码 11 | 12 | 运行: 13 | 14 | ```bash 15 | $ npm install 16 | $ node index.js 17 | ``` 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // 用户名和密码 2 | const phone = ''; 3 | const password = ''; 4 | 5 | const rp = require('request-promise'); 6 | const tough = require('tough-cookie'); 7 | const webapi = require('./webapi'); 8 | const Cookie = tough.Cookie; 9 | const crypto = require('crypto'); 10 | 11 | function MD5(str) { 12 | return crypto.createHash('md5').update(str).digest('hex') 13 | } 14 | 15 | async function daka() { 16 | const cookie = await rp.post( 17 | 'https://music.163.com/weapi/login/cellphone', 18 | { 19 | form: webapi({ 20 | phone: phone, 21 | password: MD5(password), 22 | countrycode: '86', 23 | rememberLogin: 'true', 24 | csrf_token: '' 25 | }), 26 | headers: { 27 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15', 28 | Referer: 'https://music.163.com', 29 | 'Content-Type': 'application/x-www-form-urlencoded', 30 | 'Cookie': 'os=pc' 31 | }, 32 | json: true, 33 | resolveWithFullResponse: true, 34 | transform(body, response) { 35 | console.log(body); 36 | return Object.assign( 37 | ...response.headers['set-cookie'].map(cookie => { 38 | const c = Cookie.parse(cookie).toJSON(); 39 | const obj = {}; 40 | obj[c.key] = c.value; 41 | return obj; 42 | }) 43 | ); 44 | } 45 | } 46 | ); 47 | 48 | const cookieStr = Object.keys(cookie) 49 | .map(k => `${k}=${cookie[k]}`) 50 | .join(';'); 51 | 52 | const playlist = await rp.post( 53 | 'https://music.163.com/weapi/v1/discovery/recommend/resource', 54 | { 55 | form: webapi({ 56 | csrf_token: cookie.__csrf 57 | }), 58 | headers: { 59 | cookie: cookieStr 60 | }, 61 | json: true, 62 | transform(body) { 63 | if (body.code === 200) { 64 | return body.recommend.map(r => r.id); 65 | } else { 66 | throw new Error('获取推荐歌曲失败'); 67 | } 68 | } 69 | } 70 | ); 71 | 72 | let songid = []; 73 | for (let index = 0; index < playlist.length; index++) { 74 | const playlist_id = playlist[index]; 75 | const id = await rp.post( 76 | 'https://music.163.com/weapi/v3/playlist/detail', 77 | { 78 | qs: { 79 | csrf_token: '' 80 | }, 81 | form: webapi({ 82 | id: playlist_id, 83 | n: 1000, 84 | csrf_token: '' 85 | }), 86 | headers: { 87 | cookie: cookieStr 88 | }, 89 | json: true, 90 | transform(body) { 91 | if (body.code === 200) { 92 | return body.playlist.trackIds.map(i => i.id); 93 | } else { 94 | throw new Error('获取歌曲ID失败'); 95 | } 96 | } 97 | } 98 | ); 99 | songid = songid.concat(id); 100 | } 101 | 102 | const daka = await rp.post( 103 | 'http://music.163.com/weapi/feedback/weblog', 104 | { 105 | form: webapi({ 106 | logs: JSON.stringify( 107 | songid.map(id => { 108 | return { 109 | action: 'play', 110 | json: { 111 | download: 0, 112 | end: 'playend', 113 | id: id.id, 114 | sourceId: '', 115 | time: 240, 116 | type: 'song', 117 | wifi: 0 118 | } 119 | }; 120 | }) 121 | ) 122 | }), 123 | headers: { 124 | cookie: cookieStr 125 | }, 126 | json: true 127 | } 128 | ); 129 | if (daka.code === 200) { 130 | return daka; 131 | } else { 132 | throw new Error(daka.message); 133 | } 134 | } 135 | 136 | daka().then(e => { 137 | console.log('打卡成功', e); 138 | }).catch(err => { 139 | console.log('打卡失败', err); 140 | }) 141 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "163_daka", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "repository": "https://github.com/Ice-Hazymoon/163_daka", 6 | "author": "Ice-Hazymoon", 7 | "license": "MIT", 8 | "private": false, 9 | "dependencies": { 10 | "request": "^2.88.2", 11 | "request-promise": "^4.2.5", 12 | "tough-cookie": "^3.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /webapi.js: -------------------------------------------------------------------------------- 1 | // https://github.com/Binaryify/NeteaseCloudMusicApi/blob/b98d611989/util/crypto.js; 2 | 3 | const crypto = require('crypto'); 4 | const iv = Buffer.from('0102030405060708'); 5 | const presetKey = Buffer.from('0CoJUm6Qyw8W8jud'); 6 | const base62 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 7 | const publicKey = 8 | '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----'; 9 | 10 | const aesEncrypt = (buffer, mode, key, iv) => { 11 | const cipher = crypto.createCipheriv('aes-128-' + mode, key, iv); 12 | return Buffer.concat([cipher.update(buffer), cipher.final()]); 13 | }; 14 | 15 | const rsaEncrypt = (buffer, key) => { 16 | buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer]); 17 | return crypto.publicEncrypt( 18 | { 19 | key: key, 20 | padding: crypto.constants.RSA_NO_PADDING 21 | }, 22 | buffer 23 | ); 24 | }; 25 | 26 | const weapi = object => { 27 | const text = JSON.stringify(object); 28 | const secretKey = crypto 29 | .randomBytes(16) 30 | .map(n => base62.charAt(n % 62).charCodeAt()); 31 | return { 32 | params: aesEncrypt( 33 | Buffer.from( 34 | aesEncrypt(Buffer.from(text), 'cbc', presetKey, iv).toString( 35 | 'base64' 36 | ) 37 | ), 38 | 'cbc', 39 | secretKey, 40 | iv 41 | ).toString('base64'), 42 | encSecKey: rsaEncrypt(secretKey.reverse(), publicKey).toString('hex') 43 | }; 44 | }; 45 | 46 | module.exports = weapi; 47 | --------------------------------------------------------------------------------