├── .dockerignore ├── .editorconfig ├── .env.example ├── .eslintrc.js ├── .gitignore ├── .yarnclean ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── index.js ├── modules ├── auth.js ├── capsule.js ├── dailybag.js ├── giftsend.js ├── group.js ├── guard.js ├── heart.js ├── silver.js ├── silver2coin.js └── tasks.js ├── package.json ├── utils ├── config.js ├── got.js ├── init.js ├── logger.js ├── share.js ├── sign.js ├── sleep.js └── tomorrow.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | Dockerfile* 4 | docker-compose* 5 | .dockerignore 6 | .git 7 | .gitignore 8 | README.md 9 | LICENSE 10 | CHANGELOG.md 11 | .vscode 12 | .eslintrc.js 13 | .editorconfig 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.js] 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.yml] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | USERNAME= 2 | PASSWORD= 3 | 4 | # Needed when environment variable 'USERNAME' exists 5 | BILI_USERNAME= 6 | 7 | ACCESS_TOKEN= 8 | REFRESH_TOKEN= 9 | 10 | ROOM_ID=3746256 11 | DEBUG=true 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 2018 10 | }, 11 | "rules": { 12 | "indent": [ 13 | "error", 14 | 2 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "single" 23 | ], 24 | "semi": [ 25 | "error", 26 | "never" 27 | ], 28 | "no-console": "off", 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cookies 3 | .config 4 | .env 5 | -------------------------------------------------------------------------------- /.yarnclean: -------------------------------------------------------------------------------- 1 | # test directories 2 | __tests__ 3 | test 4 | tests 5 | powered-test 6 | test*.js 7 | 8 | # asset directories 9 | docs 10 | doc 11 | website 12 | images 13 | assets 14 | 15 | # examples 16 | example 17 | examples 18 | 19 | # code coverage directories 20 | coverage 21 | .nyc_output 22 | 23 | # build scripts 24 | Makefile 25 | Gulpfile.js 26 | Gruntfile.js 27 | 28 | # configs 29 | appveyor.yml 30 | circle.yml 31 | codeship-services.yml 32 | codeship-steps.yml 33 | wercker.yml 34 | .tern-project 35 | .gitattributes 36 | .editorconfig 37 | .*ignore 38 | .eslintrc* 39 | .jshintrc 40 | .flowconfig 41 | .documentup.json 42 | .yarn-metadata.json 43 | .travis.yml 44 | bower.json 45 | 46 | # misc 47 | *.md 48 | *.txt 49 | *.map 50 | *.html 51 | LICENSE* 52 | license 53 | *.ts 54 | README* 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## v0.10.4 (2020-07-25) 4 | 5 | ### Changed 6 | - 更新 Docker 基础镜像 7 | 8 | ## v0.10.3 (2020-05-24) 9 | 10 | ### Changed 11 | - 修正登录使用的 AppKey 以及 AppSecret (#79) 12 | - 更新舰长经验Api,对齐B站v3新接口 (#85) 13 | - 更换网页版心跳接口 (#86) 14 | - 修复用户名获取错误问题 (#103) 15 | 16 | ## v0.10.2 (2019-05-03) 17 | 18 | ### Added 19 | - 添加舰长经验领取相关功能 (#57) 20 | - 添加舰长领取累计亲密度的统计 (#58) 21 | 22 | ### Changed 23 | - 优化舰长经验领取逻辑,添加模块开关 24 | 25 | ## v0.10.1 (2019-04-22) 26 | 27 | ### Added 28 | - 增加扭蛋币支持 29 | - 添加 Docker 支持 30 | - 添加银瓜子兑换硬币支持 31 | 32 | ### Changed 33 | - 修改配置文件 34 | 35 | ## v0.10.0 (2019-02-11) 36 | 37 | **0.10.x 开始全部使用 Node.js 重写,请参照新版 README 重新部署** 38 | 39 | ## v0.9.5 (2019-01-09) 40 | 41 | ### Fixed 42 | - 修复扭蛋接口版本过低的问题 43 | 44 | ## v0.9.4 (2018-12-20) 45 | 46 | ### Fixed 47 | - 修复扭蛋失败的问题 48 | 49 | ## v0.9.2 (2018-09-30) 50 | 51 | ### Removed 52 | - 移除小电视抽奖相关 53 | 54 | ### Fixed 55 | - 修复 composer.lock 因 BootCDN 停止维护的影响 56 | 57 | ## v0.9.1 (2018-05-09) 58 | 59 | ### Added 60 | - 添加应援团签到功能 61 | - 添加扭蛋机功能(暂时支持普通扭蛋币) 62 | 63 | ### Changed 64 | - [dev] 当配置文件为空时跳过写入 65 | - 当回调地址为空时跳过 66 | 67 | ### Fixed 68 | - 兼容 PHP 5.6 版本测试 ([#37](https://github.com/metowolf/BilibiliHelper/issues/37)) 69 | - 修复部分接口逻辑问题 70 | 71 | 72 | ## v0.9.0-pre (2018-05-07) 73 | 74 | **该版本为非兼容更新,从 0.8.x 升级需要重新覆盖配置文件** 75 | 76 | ### Added 77 | - 项目重构,为多帐号准备 78 | - 弹幕监听采用 websocket 接口 (https://github.com/varspool/Wrench) 79 | - 更换 guzzle 库,支持 CookieJar (https://github.com/guzzle/guzzle) 80 | - 添加网络代理支持 81 | 82 | ### Changed 83 | - 修改插件逻辑 84 | 85 | ### Fixed 86 | - 修复配置文件检测问题 87 | - 修复 debug 信息递归黑洞问题 88 | 89 | 90 | ## v0.8.0 (2018-05-04) 91 | 92 | **该版本为非兼容更新,从 0.7.x 升级需要重新覆盖配置文件** 93 | 94 | ### Added 95 | - 新增小电视抽奖功能 ([#18](https://github.com/metowolf/BilibiliHelper/issues/18)) 96 | - 新增配置项检测 97 | 98 | ### Changed 99 | - 修改部分时间锁逻辑 100 | - 修改部分日志提示 101 | 102 | ## v0.7.3 (2018-04-28) 103 | 104 | ### Added 105 | - 新增帐号别名参数 106 | 107 | ### Changed 108 | - 更新模拟客户端版本 (iOS 6670) 109 | - 修正环境变量刷新逻辑 110 | 111 | ### Fixed 112 | - 修复部分语法错误 113 | - 修复错误信息回调函数逻辑错误 114 | 115 | 116 | ## v0.7.2 (2018-04-22) 117 | 118 | ### Added 119 | - 新增令牌刷新机制 120 | - 新增日志通知级别设置 121 | 122 | ### Changed 123 | - 调整部分日志文案 124 | - 修正 README 的错误 ([#29](https://github.com/metowolf/BilibiliHelper/pull/29)) 125 | 126 | ### Fixed 127 | - 修复每日任务无法领取的问题 128 | - 修复部分逻辑错误 129 | 130 | 131 | ## v0.7.1 (2018-04-21) 132 | 133 | ### Changed 134 | - 调整部分通知为警告级别 135 | 136 | ### Fixed 137 | - 修复过早领取宝箱的问题 138 | 139 | 140 | ## v0.7.0 (2018-04-20) 141 | 142 | ### Added 143 | - 项目重构,拥抱 composer 144 | - 全面更换客户端 API 145 | - 添加用户登录机制 ([#22](https://github.com/metowolf/BilibiliHelper/issues/22)) 146 | 147 | ### Changed 148 | - Require PHP 5.4.0 or newer 149 | 150 | ### Fixed 151 | - 修复宝箱验证码问题 ([#27](https://github.com/metowolf/BilibiliHelper/issues/27)) 152 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | LABEL maintainer="metowolf " 4 | 5 | ENV USERNAME= 6 | ENV PASSWORD= 7 | ENV ACCESS_TOKEN= 8 | ENV REFRESH_TOKEN= 9 | ENV ROOM_ID 3746256 10 | ENV DEBUG true 11 | ENV TZ Asia/Shanghai 12 | 13 | WORKDIR /app 14 | COPY package.json yarn.lock .yarnclean ./ 15 | RUN apk add --no-cache tzdata \ 16 | && yarn \ 17 | && yarn cache clean 18 | COPY . . 19 | 20 | CMD ["node", "index.js"] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 metowolf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | ------------------------------------------------------------------------------------ 24 | 25 | https://github.com/copyliu/bililive_dm 26 | 27 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 28 | Version 2, December 2004 29 | 30 | Copyright (C) 2004 Sam Hocevar 31 | 32 | Everyone is permitted to copy and distribute verbatim or modified 33 | copies of this license document, and changing it is allowed as long 34 | as the name is changed. 35 | 36 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 37 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 38 | 39 | 0. You just DO WHAT THE FUCK YOU WANT TO. 40 | 41 | ------------------------------------------------------------------------------------ 42 | 43 | And wait, the most important, you shall star/+1/like the project(s) in project url 44 | section above first, and then thank the author(s) in Copyright section. 45 | 46 | Here are some suggested ways: 47 | 48 | - Email the authors a thank-you letter, and make friends with him/her/them. 49 | - Report bugs or issues. 50 | - Tell friends what a wonderful project this is. 51 | - And, sure, you can just express thanks in your mind without telling the world. 52 | 53 | Contributors of this project by forking have the option to add his/her name and 54 | forked project url at copyright and project url sections, but shall not delete 55 | or modify anything else in these two sections. 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 | 6 | 7 |

8 | 9 | 10 | # BilibiliHelper 11 | 12 | B 站挂机实用脚本,[>> 点此返回 PHP 旧版](https://github.com/metowolf/BilibiliHelper/tree/0.9x) 13 | 14 | ## 功能组件 15 | 16 | |plugin |version |description | 17 | |------------|---------|--------------| 18 | |Auth |19.02.11 |帐号登录组件 | 19 | |Capsule |19.02.12 |扭蛋机(普通) | 20 | |DailyBag |19.04.22 |每日礼包领取 | 21 | |Group |19.02.11 |应援团签到 | 22 | |Guard |19.05.03 |舰长亲密度领取 | 23 | |Heart |19.02.11 |双端直播间心跳 | 24 | |Silver |19.02.11 |免费宝箱领取 | 25 | |Silver2Coin |19.02.12 |银瓜子兑换硬币 | 26 | |Task |19.02.11 |每日任务 | 27 | 28 | 29 | ## 环境依赖 30 | 31 | |Requirement| 32 | |-------| 33 | |Node.js (>=8.x)| 34 | 35 | 36 | ## 搭建指南 37 | 38 | - [Docker 搭建指南](https://github.com/metowolf/BilibiliHelper/wiki/Docker-%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97) 39 | - [Node.js 搭建指南](https://github.com/metowolf/BilibiliHelper/wiki/Node.js-%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97) 40 | 41 | 42 | ## License 许可证 43 | 44 | 本项目基于 MIT 协议发布,并增加了 SATA 协议。 45 | 46 | 当你使用了使用 SATA 的开源软件或文档的时候,在遵守基础许可证的前提下,你必须马不停蹄地给你所使用的开源项目 “点赞” ,比如在 GitHub 上 star,然后你必须感谢这个帮助了你的开源项目的作者,作者信息可以在许可证头部的版权声明部分找到。 47 | 48 | 本项目的所有代码文件、配置项,除另有说明外,均基于上述介绍的协议发布,具体请看分支下的 LICENSE。 49 | 50 | 此处的文字仅用于说明,条款以 LICENSE 文件中的内容为准。 51 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * metowolf BilibiliHelper 3 | * https://i-meto.com/ 4 | * 5 | * Copyright 2019, metowolf 6 | * Released under the MIT license 7 | */ 8 | 9 | const module_auth = require('./modules/auth') 10 | const module_tasks = require('./modules/tasks') 11 | const module_heart = require('./modules/heart') 12 | const module_silver = require('./modules/silver') 13 | const module_group = require('./modules/group') 14 | const module_guard = require('./modules/guard') 15 | const module_capsule = require('./modules/capsule') 16 | const module_giftsend = require('./modules/giftsend') 17 | const module_dailybag = require('./modules/dailybag') 18 | const module_silver2coin = require('./modules/silver2coin') 19 | 20 | const init = require('./utils/init') 21 | const sleep = require('./utils/sleep') 22 | 23 | const app = async () => { 24 | init() 25 | while (true) { 26 | await module_auth() 27 | await module_tasks() 28 | await module_heart() 29 | await module_silver() 30 | await module_group() 31 | await module_capsule() 32 | await module_giftsend() 33 | await module_dailybag() 34 | await module_silver2coin() 35 | await module_guard() 36 | await sleep(1000) 37 | } 38 | } 39 | 40 | app() 41 | -------------------------------------------------------------------------------- /modules/auth.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | const got = require('../utils/got') 4 | const share = require('../utils/share').auth 5 | const sign = require('../utils/sign') 6 | const logger = require('../utils/logger') 7 | const config = require('../utils/config') 8 | 9 | const getPublicKey = async () => { 10 | logger.info('正在获取公钥') 11 | 12 | let payload = {} 13 | let {body} = await got.post('https://passport.bilibili.com/api/oauth2/getKey', { 14 | body: sign(payload), 15 | form: true, 16 | json: true, 17 | }) 18 | 19 | if (body.code) throw new Error('公钥获取失败') 20 | logger.notice('公钥获取成功') 21 | return body.data 22 | } 23 | 24 | const loginPassword = async () => { 25 | let data = await getPublicKey() 26 | 27 | logger.info('正在尝试使用用户名、密码登录') 28 | 29 | let username = config.get('username') 30 | let password = crypto.publicEncrypt( 31 | { 32 | key: data.key, 33 | padding: 1, 34 | }, 35 | Buffer.from(`${data.hash}${config.get('password')}`) // eslint-disable-line 36 | ).toString('base64') 37 | 38 | let payload = { 39 | seccode: '', 40 | validate: '', 41 | subid: 1, 42 | permission: 'ALL', 43 | username, 44 | password, 45 | captcha: '', 46 | challenge: '', 47 | } 48 | 49 | let {body} = await got.post('https://passport.bilibili.com/api/v3/oauth2/login', { 50 | body: sign(payload), 51 | form: true, 52 | json: true, 53 | }) 54 | 55 | if (body.code || body.data.status) throw new Error('登录失败') 56 | logger.notice('登录成功') 57 | 58 | config.set('access_token', body.data.token_info.access_token) 59 | config.set('refresh_token', body.data.token_info.refresh_token) 60 | } 61 | 62 | const refreshToken = async () => { 63 | 64 | if (config.get('refresh_token', '') === '') return false 65 | 66 | logger.info('正在刷新 Token') 67 | 68 | let payload = { 69 | access_token: config.get('access_token'), 70 | refresh_token: config.get('refresh_token'), 71 | } 72 | let {body} = await got.post('https://passport.bilibili.com/api/oauth2/refreshToken', { 73 | body: sign(payload), 74 | form: true, 75 | json: true, 76 | }) 77 | 78 | if (body.code) { 79 | config.set('access_token', '') 80 | config.set('refresh_token', '') 81 | return false 82 | } 83 | logger.notice('Token 刷新成功') 84 | config.set('access_token', body.data.access_token) 85 | config.set('refresh_token', body.data.refresh_token) 86 | } 87 | 88 | const checkCookie = async () => { 89 | logger.info('检查 Cookie 是否过期') 90 | 91 | const body = await getUserInfo() 92 | 93 | if (body.code !== 'REPONSE_OK') { 94 | logger.warning('检测到 Cookie 已经过期') 95 | logger.info('正在刷新 Cookie') 96 | await got.get('https://passport.bilibili.com/api/login/sso', {query: sign({})}) 97 | logger.notice('Cookie 刷新成功') 98 | await getUserInfo() // 获取UID,舰长经验检测有用到 99 | } 100 | } 101 | 102 | const getUserInfo = async () => { 103 | const { body } = await got.get('https://api.live.bilibili.com/User/getUserInfo', { 104 | query: { 105 | ts: Math.round(Date.now() / 1000) 106 | }, 107 | json: true, 108 | }) 109 | 110 | // 获取UID 111 | if (body.code === 'REPONSE_OK') config.set('uid', body.data.uid) 112 | 113 | return body 114 | } 115 | 116 | const checkToken = async () => { 117 | logger.info('检查 Token 是否过期') 118 | 119 | let payload = { 120 | access_token: config.get('access_token', ''), 121 | } 122 | let {body} = await got.get('https://passport.bilibili.com/api/v2/oauth2/info', { 123 | query: sign(payload), 124 | json: true, 125 | }) 126 | 127 | if (body.code || body.data.expires_in < 14400) { 128 | logger.warning('检测到 Token 需要更新') 129 | 130 | let status = await refreshToken() 131 | if (!status) await loginPassword() 132 | } 133 | } 134 | 135 | const main = async () => { 136 | await checkToken() 137 | await checkCookie() 138 | } 139 | 140 | module.exports = () => { 141 | if (share.lock > Date.now()) return 142 | return main() 143 | .then(() => { 144 | share.lock = Date.now() + 60 * 60 * 1000 145 | }) 146 | .catch(e => { 147 | logger.error(e.message) 148 | share.lock = Date.now() + 10 * 60 * 1000 149 | }) 150 | } 151 | -------------------------------------------------------------------------------- /modules/capsule.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').capsule 3 | const logger = require('../utils/logger') 4 | const sleep = require('../utils/sleep') 5 | 6 | const getCsrf = async () => { 7 | let cookies = got.defaults.options.cookieJar.getCookiesSync('https://api.bilibili.com/') 8 | for (let cookie of cookies) { 9 | let found = `${cookie}`.match(/bili_jct=([0-9a-f]*)/i) 10 | if (found) return found[1] 11 | } 12 | throw new Error('csrf 提取失败') 13 | } 14 | 15 | const main = async () => { 16 | let coin = await getCoin() 17 | let step = 100 18 | while (coin && step) { 19 | while (coin >= step) { 20 | coin = await openCapsule(step) 21 | await sleep(2000) 22 | } 23 | step = Math.floor(step / 10) 24 | } 25 | } 26 | 27 | const openCapsule = async step => { 28 | let csrf = await getCsrf() 29 | let payload = { 30 | csrf, 31 | csrf_token: csrf, 32 | count: step, 33 | type: 'normal', 34 | platform: 'h5', 35 | } 36 | let {body} = await got.post('https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/open_capsule', { 37 | body: payload, 38 | form: true, 39 | json: true, 40 | }) 41 | if (body.code) throw new Error('扭蛋失败,稍后重试') 42 | for (let item of body.data.awards) { 43 | logger.notice(`扭蛋成功,获得 ${item.num} 个 ${item.name}`) 44 | } 45 | return body.data.coin || 0 46 | } 47 | 48 | const getCoin = async () => { 49 | logger.info('正在查询扭蛋币余额') 50 | let {body} = await got.get('https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/get_detail', {json: true}) 51 | if (body.code) throw new Error('扭蛋币余额查询异常') 52 | logger.info(`当前还有 ${body.data.normal.coin} 枚扭蛋币`) 53 | return body.data.normal.coin 54 | } 55 | 56 | module.exports = () => { 57 | if (process.env.DISABLE_CAPSULE === 'true') return 58 | if (share.lock > Date.now()) return 59 | return main() 60 | .then(() => { 61 | share.lock = Date.now() + 60 * 60 * 1000 62 | }) 63 | .catch(e => { 64 | logger.error(e.message) 65 | share.lock = Date.now() + 60 * 60 * 1000 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /modules/dailybag.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').dailybag 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const tomorrow = require('../utils/tomorrow') 6 | 7 | const main = async () => { 8 | if (share.lock_web < Date.now()) await web() 9 | if (share.lock_mobile < Date.now()) await mobile() 10 | } 11 | 12 | const web = async () => { 13 | let {body} = await got.get('https://api.live.bilibili.com/gift/v2/live/receive_daily_bag', {json: true}) 14 | if (body.code) throw new Error('每日礼包领取失败 (web)') 15 | share.lock_web = tomorrow(10) 16 | } 17 | 18 | const mobile = async () => { 19 | let {body} = await got.get('https://api.live.bilibili.com/xlive/app-room/v1/gift/daily_bag', {query: sign({}), json: true}) 20 | if (body.code) throw new Error('每日礼包领取失败 (app)') 21 | share.lock_mobile = tomorrow(10) 22 | } 23 | 24 | module.exports = () => { 25 | if (process.env.DISABLE_DAILYBAG === 'true') return 26 | return main() 27 | .catch(e => { 28 | logger.error(e.message) 29 | share.lock_web = Date.now() + 10 * 60 * 1000 30 | share.lock_mobile = Date.now() + 10 * 60 * 1000 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /modules/giftsend.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').giftsend 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const sleep = require('../utils/sleep') 6 | const config = require('../utils/config') 7 | 8 | const main = async () => { 9 | if (!share.ruid) await getRoomInfo() 10 | let data = await getBagList() 11 | for (let item of data.list) { 12 | if (item.expire_at >= data.time && item.expire_at <= data.time + 3600) { 13 | await giftSend(item) 14 | await sleep(2000) 15 | } 16 | } 17 | } 18 | 19 | const giftSend = async (data) => { 20 | let payload = { 21 | coin_type: 'silver', 22 | gift_id: data.gift_id, 23 | ruid: share.ruid, 24 | uid: share.uid, 25 | biz_id: share.roomid, 26 | gift_num: data.gift_num, 27 | data_source_id: '', 28 | data_behavior_id: '', 29 | bag_id: data.bag_id, 30 | } 31 | let {body} = await got.post('https://api.live.bilibili.com/gift/v2/live/bag_send', { 32 | body: sign(payload), 33 | form: true, 34 | json: true, 35 | }) 36 | if (body.code) logger.error(`尝试向直播间投喂 ${data.gift_name} 失败`) 37 | else logger.notice(`成功向直播间 ${payload.biz_id} 投喂了 ${data.gift_num} 个 ${data.gift_name}`) 38 | } 39 | 40 | const getBagList = async () => { 41 | let {body} = await got.get('https://api.live.bilibili.com/gift/v2/gift/bag_list', {query: sign({}), json: true}) 42 | if (body.code) throw new Error('背包查看失败') 43 | return body.data 44 | } 45 | 46 | const getRoomInfo = async () => { 47 | logger.info('正在补全用户信息') 48 | { 49 | let {body} = await got.get('https://account.bilibili.com/api/myinfo/v2', {query: sign({}), json: true}) 50 | if (body.code) throw new Error('获取用户信息失败') 51 | share.uid = body.mid 52 | } 53 | logger.info('正在补全直播间信息') 54 | { 55 | let payload = { 56 | id: config.get('room_id'), 57 | } 58 | let {body} = await got.get('https://api.live.bilibili.com/room/v1/Room/get_info', {query: sign(payload), json: true}) 59 | if (body.code) throw new Error('获取用户信息失败') 60 | share.ruid = body.data.uid 61 | share.roomid = body.data.room_id 62 | } 63 | } 64 | 65 | module.exports = () => { 66 | if (process.env.DISABLE_GIFTSEND === 'true') return 67 | if (share.lock > Date.now()) return 68 | return main() 69 | .then(() => { 70 | share.lock = Date.now() + 60 * 60 * 1000 71 | }) 72 | .catch(e => { 73 | logger.error(e.message) 74 | share.lock = Date.now() + 10 * 60 * 1000 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /modules/group.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').group 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const sleep = require('../utils/sleep') 6 | const tomorrow = require('../utils/tomorrow') 7 | 8 | const main = async () => { 9 | share.count = 0 10 | let list = await getList() 11 | for (let item of list) { 12 | await signGroup(item) 13 | await sleep(1000) 14 | } 15 | if (share.count === list.length) share.lock = tomorrow(10) 16 | else share.lock = share.lock = Date.now() + 60 * 60 * 1000 17 | } 18 | 19 | const signGroup = async data => { 20 | let payload = { 21 | group_id: data.group_id, 22 | owner_id: data.owner_uid, 23 | } 24 | let {body} = await got.post('https://api.vc.bilibili.com/link_setting/v1/link_setting/sign_in', { 25 | body: sign(payload), 26 | form: true, 27 | json: true, 28 | }) 29 | if (body.code) throw new Error(`应援团 ${data.group_name} 签到异常`) 30 | if (body.data.status) logger.info(`应援团 ${data.group_name} 已经签到过了`) 31 | else logger.info(`应援团 ${data.group_name} 签到成功,增加 ${body.data.add_num} 点亲密度`) 32 | share.count += 1 33 | } 34 | 35 | const getList = async () => { 36 | let {body} = await got.post('https://api.vc.bilibili.com/link_group/v1/member/my_groups', { 37 | body: sign({}), 38 | form: true, 39 | json: true, 40 | }) 41 | if (body.code) throw new Error('应援团列表拉取异常') 42 | return body.data.list 43 | } 44 | 45 | module.exports = () => { 46 | if (process.env.DISABLE_GROUP === 'true') return 47 | if (share.lock > Date.now()) return 48 | return main() 49 | .catch(e => { 50 | logger.error(e.message) 51 | share.lock = Date.now() + 10 * 60 * 1000 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /modules/guard.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const got_unsafe = require('got') 3 | const config = require('../utils/config') 4 | const logger = require('../utils/logger') 5 | const share = require('../utils/share').guard 6 | const sleep = require('../utils/sleep') 7 | 8 | let csrfToken 9 | let list_cache = [] 10 | 11 | const getCsrf = () => { 12 | const cookies = got.defaults.options.cookieJar.getCookiesSync('https://api.bilibili.com/') 13 | for (const cookie of cookies) { 14 | const found = `${cookie}`.match(/bili_jct=([0-9a-f]*)/i) 15 | if (found) return found[1] 16 | } 17 | throw new Error('guard: csrf 提取失败') 18 | } 19 | 20 | const main = async () => { 21 | 22 | // 锁定流程,防止重复执行 23 | share.lock = Date.now() + 24 * 60 * 60 * 1000 24 | 25 | csrfToken = getCsrf() 26 | 27 | const uid = config.get('uid', '') 28 | if (uid === '') throw new Error('uid获取失败') 29 | 30 | // 获取列表 31 | const list = await getGuardList(uid) 32 | 33 | const originList = list.filter(item => !list_cache.includes(item.Id)) 34 | if (list_cache.length > 10000) list_cache.splice(0, 9000) 35 | 36 | for (const currentItem of originList) { 37 | const guardId = currentItem.Id 38 | const originRoomid = currentItem.RoomId 39 | 40 | // 记录已经检查过的 GuardId 41 | list_cache.push(guardId) 42 | 43 | // 非特定时间跳过领取 44 | const guardHours = config.get('guard.hours', []) 45 | if (!guardHours.includes((new Date).getHours())) { 46 | logger.debug('guard:非特定时间跳过领取') 47 | continue 48 | } 49 | 50 | // 概率性跳过领取 51 | const guardPercent = config.get('guard.percent', 100) 52 | if (Math.random() * 100 >= guardPercent) { 53 | logger.debug('guard:概率性跳过领取') 54 | continue 55 | } 56 | 57 | // 检测是否是真实存在的room 58 | const isTrueRoom = await checkTrueRoom(originRoomid) 59 | if (isTrueRoom) { 60 | 61 | // 如果已经在这个房间就不用再进一遍 62 | if (share.lastGuardRoom !== originRoomid) { 63 | await goToRoom(originRoomid) 64 | await sleep(2000 + Math.random() * 2000) 65 | share.lastGuardRoom = originRoomid 66 | } 67 | 68 | const result = await getLottery(originRoomid, guardId) 69 | 70 | if (result.code === 0) { 71 | logger.notice(`guard: ${originRoomid} 舰长经验领取成功,${result.msg}`) 72 | continue 73 | } 74 | 75 | if (result.code === 400 && result.msg.includes('领取过')) { 76 | logger.notice(`guard: ${originRoomid} 舰长经验已经领取过`) 77 | continue 78 | } 79 | 80 | if (result.code === 400 && result.msg.includes('过期')) { 81 | logger.notice(`guard: ${originRoomid} 舰长经验已经过期`) 82 | continue 83 | } 84 | 85 | if (result.code) { 86 | throw new Error('guard: 舰长经验领取失败,稍后重试') 87 | } 88 | } 89 | 90 | await sleep(5 * 1000 + Math.random() * 60 * 1000) 91 | } 92 | } 93 | 94 | async function getGuardList(uid) { 95 | const { body } = await got_unsafe.get('http://118.25.108.153:8080/guard', { 96 | headers: { 97 | 'User-Agent': `bilibili-live-tools/${uid}` 98 | }, 99 | timeout: 20000, 100 | json: true 101 | }) 102 | return body 103 | } 104 | 105 | async function checkTrueRoom(roomId) { 106 | const { body } = await got.get( 107 | `https://api.live.bilibili.com/room/v1/Room/room_init?id=${roomId}`, 108 | { json: true } 109 | ) 110 | if (body.code === 0) { 111 | const { is_hidden, is_locked, encrypted } = body.data 112 | return !(is_hidden || is_locked || encrypted) 113 | } else { 114 | logger.warning('guard: 获取房间信息失败') 115 | return false 116 | } 117 | } 118 | 119 | async function goToRoom(roomId) { 120 | const { body } = await got.post( 121 | 'https://api.live.bilibili.com/room/v1/Room/room_entry_action', 122 | { 123 | body: { 124 | room_id: roomId, 125 | csrf_token: csrfToken, 126 | csrf: csrfToken, 127 | platform: 'pc', 128 | }, 129 | form: true, 130 | json: true 131 | } 132 | ) 133 | return body 134 | } 135 | 136 | async function getLottery(roomId, guardId) { 137 | const { body } = await got.post( 138 | 'https://api.live.bilibili.com/xlive/lottery-interface/v3/guard/join', 139 | { 140 | body: { 141 | roomid: roomId, 142 | id: guardId, 143 | type: 'guard', 144 | csrf_token: csrfToken, 145 | csrf: csrfToken, 146 | }, 147 | form: true, 148 | json: true 149 | } 150 | ) 151 | return body 152 | } 153 | 154 | module.exports = () => { 155 | if (process.env.DISABLE_GUARD === 'true') return 156 | if (share.lock > Date.now()) return 157 | return main() 158 | .then(() => { 159 | share.lock = Date.now() + 5 * 60 * 1000 160 | }) 161 | .catch(e => { 162 | logger.error(e.message) 163 | share.lock = Date.now() + 60 * 60 * 1000 164 | }) 165 | } 166 | -------------------------------------------------------------------------------- /modules/heart.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').heart 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const config = require('../utils/config') 6 | let lastInterval = 0 7 | 8 | const main = async () => { 9 | await heart_web() 10 | await heart_mobile() 11 | } 12 | 13 | const heart_web = async () => { 14 | { 15 | let baseStr = Buffer.from(`${lastInterval}|${config.get('room_id')}|1|0`).toString('base64') 16 | let {body} = await got.get('https://live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?pf=web&hb=' + baseStr, {json: true}) 17 | if (body.code) throw new Error('直播间心跳异常 (web)') 18 | lastInterval = body.data.next_interval 19 | } 20 | } 21 | 22 | const heart_mobile = async () => { 23 | { 24 | let payload = { 25 | room_id: config.get('room_id'), 26 | } 27 | let {body} = await got.post('https://api.live.bilibili.com/mobile/userOnlineHeart', { 28 | body: sign(payload), 29 | form: true, 30 | json: true, 31 | }) 32 | if (body.code) throw new Error('直播间心跳异常 (app)') 33 | } 34 | } 35 | 36 | module.exports = () => { 37 | if (process.env.DISABLE_HEART === 'true') return 38 | if (share.lock > Date.now()) return 39 | return main() 40 | .then(() => { 41 | share.lock = Date.now() + 5 * 60 * 1000 42 | }) 43 | .catch(e => { 44 | logger.error(e.message) 45 | share.lock = Date.now() + 5 * 60 * 1000 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /modules/silver.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').silver 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const tomorrow = require('../utils/tomorrow') 6 | 7 | const main = async () => { 8 | if (share.task === 0) await getSilver() 9 | else await openSilver() 10 | } 11 | 12 | const openSilver = async () => { 13 | let data 14 | { 15 | let {body} = await got.get('https://api.live.bilibili.com/mobile/freeSilverAward', {query: sign({}), json: true}) 16 | if (body.code) throw new Error('银瓜子宝箱开启失败') 17 | data = body.data 18 | } 19 | 20 | logger.notice(`宝箱开启成功,瓜子 ${data.silver}(+${data.awardSilver})`) 21 | 22 | share.lock = Date.now() + 10 * 1000 23 | share.task = 0 24 | } 25 | 26 | const getSilver = async () => { 27 | let data 28 | { 29 | let {body} = await got.get('https://api.live.bilibili.com/lottery/v1/SilverBox/getCurrentTask', {query: sign({}), json: true}) 30 | if (body.code === -10017) { 31 | logger.notice(body.message) 32 | share.lock = tomorrow(10) 33 | return 34 | } 35 | if (body.code) throw new Error('银瓜子宝箱领取失败') 36 | data = body.data 37 | } 38 | logger.notice(`领取宝箱成功,内含 ${data.silver} 个瓜子`) 39 | logger.info(`等待 ${data.minute} 分钟后打开宝箱`) 40 | 41 | share.task = data.time_start 42 | share.lock = Date.now() + data.minute * 60 * 1000 + 10 * 1000 43 | } 44 | 45 | module.exports = () => { 46 | if (process.env.DISABLE_SILVER === 'true') return 47 | if (share.lock > Date.now()) return 48 | return main() 49 | .catch(e => { 50 | logger.error(e.message) 51 | share.lock = Date.now() + 5 * 60 * 1000 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /modules/silver2coin.js: -------------------------------------------------------------------------------- 1 | const got = require('../utils/got') 2 | const share = require('../utils/share').silver2coin 3 | const sign = require('../utils/sign') 4 | const logger = require('../utils/logger') 5 | const tomorrow = require('../utils/tomorrow') 6 | 7 | const main = async () => { 8 | await silver2coin() 9 | } 10 | 11 | const silver2coin = async () => { 12 | { 13 | logger.info('查询硬币兑换状态') 14 | let {body} = await got.get('https://api.live.bilibili.com/pay/v1/Exchange/getStatus', {query: sign({}), json: true}) 15 | if (body.code) throw new Error('硬币兑换状态获取失败') 16 | if (!body.data.silver_2_coin_left) { 17 | logger.warning('每天最多只能兑换 1 个硬币') 18 | share.lock = tomorrow(20) 19 | return 20 | } 21 | } 22 | { 23 | logger.info('正在兑换硬币') 24 | let payload = { 25 | num: 1, 26 | } 27 | let {body} = await got.post('https://api.live.bilibili.com/pay/v1/Exchange/silver2coin', { 28 | body: sign(payload), 29 | form: true, 30 | json: true, 31 | }) 32 | if (body.code) throw new Error('硬币兑换失败') 33 | logger.notice('硬币兑换成功') 34 | share.lock = tomorrow(20) 35 | } 36 | } 37 | 38 | module.exports = () => { 39 | if (process.env.DISABLE_SILVER2CION === 'true') return 40 | if (share.lock > Date.now()) return 41 | return main() 42 | .catch(e => { 43 | logger.error(e.message) 44 | share.lock = Date.now() + 60 * 60 * 1000 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /modules/tasks.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment') 2 | 3 | const got = require('../utils/got') 4 | const share = require('../utils/share').tasks 5 | const sign = require('../utils/sign') 6 | const logger = require('../utils/logger') 7 | const sleep = require('../utils/sleep') 8 | 9 | const main = async () => { 10 | logger.info('检查每日任务') 11 | 12 | let {body} = await got.get('https://api.live.bilibili.com/i/api/taskInfo', {json: true}) 13 | if (body.code) throw new Error('每日任务获取失败') 14 | 15 | share.count = 0 16 | if (body.data.sign_info) await check_sign_info() 17 | if (body.data.double_watch_info) await check_double_watch_info(body.data.double_watch_info) 18 | 19 | if (share.count >= 2) { 20 | let unix = moment().add(1, 'd').startOf('day').add(10, 'm').format('x') 21 | share.lock = parseInt(unix, 10) 22 | return 23 | } 24 | share.lock = Date.now() + 10 * 60 * 1000 25 | } 26 | 27 | const check_double_watch_info = async data => { 28 | { 29 | logger.info('检查任务「双端观看直播」') 30 | if (data.status === 2) { 31 | logger.notice('「双端观看直播」奖励已经领取') 32 | share.count += 1 33 | return 34 | } 35 | if (data.mobile_watch !== 1 || data.web_watch !== 1) { 36 | logger.info('「双端观看直播」未完成,请等待') 37 | return 38 | } 39 | } 40 | { 41 | logger.info('领取「双端观看直播」奖励') 42 | let payload = { 43 | task_id: 'double_watch_task', 44 | } 45 | let {body} = await got.post('https://api.live.bilibili.com/activity/v1/task/receive_award', { 46 | body: sign(payload), 47 | form: true, 48 | json: true, 49 | }) 50 | if (body.code) throw new Error('「双端观看直播」奖励领取失败') 51 | logger.notice('「双端观看直播」奖励领取成功') 52 | for (let item of data.awards) { 53 | logger.notice(`获得 ${item.name} × ${item.num}`) 54 | } 55 | } 56 | } 57 | 58 | const check_sign_info = async () => { 59 | { 60 | logger.info('检查任务「每日签到」') 61 | let {body} = await got.get('https://api.live.bilibili.com/sign/GetSignInfo', {json: true}) 62 | if (body.code) throw new Error('任务「每日签到」获取失败') 63 | if (body.data.status) { 64 | logger.notice('「每日签到」奖励已经领取') 65 | share.count += 1 66 | return 67 | } 68 | } 69 | { 70 | logger.info('正在尝试网页签到') 71 | let {body} = await got.get('https://api.live.bilibili.com/sign/doSign', {json: true}) 72 | if (body.code === 0 && body.message === '0') { 73 | logger.notice(`签到成功,您已经连续签到 ${body.data.hadSignDays} 天,获得${body.data.text}${body.data.specialText}`) 74 | return 75 | } 76 | } 77 | } 78 | 79 | module.exports = () => { 80 | if (process.env.DISABLE_TASKS === 'true') return 81 | if (share.lock > Date.now()) return 82 | return main() 83 | .catch(e => { 84 | logger.error(e.message) 85 | share.lock = Date.now() + 10 * 60 * 1000 86 | }) 87 | } 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@metowolf/bilibili", 3 | "version": "0.10.4", 4 | "description": "B 站自动领瓜子、直播挂机脚本", 5 | "main": "index.js", 6 | "repository": "https://github.com/metowolf/BilibiliHelper.git", 7 | "author": "metowolf ", 8 | "license": "MIT", 9 | "scripts": { 10 | "start": "node index.js" 11 | }, 12 | "dependencies": { 13 | "chalk": "^2.4.2", 14 | "conf": "^4.0.1", 15 | "dotenv": "^8.0.0", 16 | "got": "^9.6.0", 17 | "md5": "^2.2.1", 18 | "moment": "^2.24.0", 19 | "qs": "^6.7.0", 20 | "tough-cookie": "^3.0.1", 21 | "tough-cookie-file-store": "^1.2.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /utils/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const Conf = require('conf') 4 | const config = new Conf({ 5 | cwd: `${__dirname}/../`, 6 | configName: '.config', 7 | fileExtension: '', 8 | }) 9 | 10 | module.exports = config 11 | -------------------------------------------------------------------------------- /utils/got.js: -------------------------------------------------------------------------------- 1 | const got = require('got') 2 | const chalk = require('chalk') 3 | const config = require('./config') 4 | const CookieStore = require('tough-cookie-file-store') 5 | const CookieJar = require('tough-cookie').CookieJar 6 | 7 | const cookieJar = new CookieJar(new CookieStore('./.cookies')) 8 | 9 | const _got = got.extend({ 10 | headers: { 11 | 'User-Agent': 'bili-universal/8470 CFNetwork/978.0.7 Darwin/18.5.0', 12 | 'Accept': '*/*', 13 | 'Accept-Language': 'zh-cn', 14 | 'Connection': 'keep-alive', 15 | 'Content-Type': 'application/x-www-form-urlencoded', 16 | 'Referer': `https://live.bilibili.com/${config.get('room_id')}`, 17 | }, 18 | cookieJar, 19 | timeout: 20000, 20 | hooks: { 21 | beforeRequest: [ 22 | options => { 23 | if (config.get('debug')) console.log(`${chalk.cyan(options.method)} ${chalk.yellow(options.href)}`) 24 | } 25 | ], 26 | afterResponse: [ 27 | response => { 28 | if (config.get('debug') && response.body.length < 1000) console.log(chalk.gray(response.body)) 29 | return response 30 | } 31 | ] 32 | }, 33 | }) 34 | 35 | module.exports = _got 36 | -------------------------------------------------------------------------------- /utils/init.js: -------------------------------------------------------------------------------- 1 | const config = require('./config') 2 | const package = require('../package.json') 3 | 4 | const init = () => { 5 | if (config.get('version') !== package.version) { 6 | let t = { 7 | version: package.version, 8 | debug: process.env.DEBUG === 'true', 9 | username: process.env.BILI_USERNAME || process.env.USERNAME || '', 10 | password: process.env.PASSWORD || '', 11 | access_token: process.env.ACCESS_TOKEN || '', 12 | refresh_token: process.env.REFRESH_TOKEN || '', 13 | room_id: process.env.ROOM_ID || '3746256', 14 | guard: { 15 | hours: process.env.GUARD_HOURS || '11,12,13,19,20,21,22,23', 16 | percent: process.env.GUARD_PERCENT || '60', 17 | }, 18 | } 19 | t.guard.hours = t.guard.hours.split(',').map(x => parseInt(x, 10)) 20 | t.guard.percent = parseInt(t.guard.percent, 10) 21 | config.store = t 22 | } 23 | console.log(config.store) 24 | } 25 | 26 | module.exports = init 27 | -------------------------------------------------------------------------------- /utils/logger.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | 3 | module.exports = { 4 | debug: message => console.log(`${chalk.bgCyan('DEBUG')} ${message}`), 5 | info: message => console.log(`${chalk.bgCyan('INFO')} ${message}`), 6 | notice: message => console.log(`${chalk.bgGreen('NOTICE')} ${message}`), 7 | warning: message => console.log(`${chalk.bgYellow('WARNING')} ${message}`), 8 | error: message => console.log(`${chalk.bgRed('ERROR')} ${message}`), 9 | } 10 | -------------------------------------------------------------------------------- /utils/share.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | auth: { 3 | lock: 0, 4 | }, 5 | tasks: { 6 | lock: 0, 7 | done: {}, 8 | }, 9 | heart: { 10 | lock: 0, 11 | }, 12 | dailybag: { 13 | lock_web: 0, 14 | lock_mobile: 0, 15 | }, 16 | silver: { 17 | lock: 0, 18 | task: 0, 19 | }, 20 | group: { 21 | lock: 0, 22 | count: 0, 23 | }, 24 | giftsend: { 25 | lock: 0, 26 | uid: 0, 27 | ruid: 0, 28 | roomid: 0, 29 | }, 30 | capsule: { 31 | lock: 0, 32 | }, 33 | silver2coin: { 34 | lock: 0, 35 | }, 36 | guard: { 37 | lock: 0, 38 | lastGuardRoom: 0 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /utils/sign.js: -------------------------------------------------------------------------------- 1 | const qs = require('qs') 2 | const md5 = require('md5') 3 | const config = require('./config') 4 | 5 | const sign = data => { 6 | 7 | const appkey = '4409e2ce8ffd12b8' 8 | const appsecret = '59b43e04ad6965f34319062b478f83dd' 9 | 10 | let defaults = { 11 | access_key: config.get('access_token', ''), 12 | actionKey: 'appkey', 13 | appkey, 14 | build: '8470', 15 | device: 'phone', 16 | mobi_app: 'iphone', 17 | platform: 'ios', 18 | ts: Math.round(Date.now() / 1000), 19 | type: 'json', 20 | } 21 | 22 | data = { 23 | ...defaults, 24 | ...data 25 | } 26 | 27 | let hash = qs.stringify(data, {sort: (a, b) => a.localeCompare(b)}) 28 | hash = md5(hash + appsecret) 29 | 30 | data.sign = hash 31 | 32 | return data 33 | } 34 | 35 | module.exports = sign 36 | -------------------------------------------------------------------------------- /utils/sleep.js: -------------------------------------------------------------------------------- 1 | const sleep = ms => { 2 | return new Promise(resolve => { 3 | setTimeout(resolve, ms) 4 | }) 5 | } 6 | 7 | module.exports = sleep 8 | -------------------------------------------------------------------------------- /utils/tomorrow.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment') 2 | 3 | const tomorrow = (m = 0, s = 0) => { 4 | let unix = moment().add(1, 'd').startOf('day').add(m, 'm').add(s, 's').format('x') 5 | return parseInt(unix, 10) 6 | } 7 | 8 | module.exports = tomorrow 9 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@sindresorhus/is@^0.14.0": 6 | version "0.14.0" 7 | resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" 8 | integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== 9 | 10 | "@szmarczak/http-timer@^1.1.2": 11 | version "1.1.2" 12 | resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" 13 | integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== 14 | dependencies: 15 | defer-to-connect "^1.0.1" 16 | 17 | ajv@^6.10.0: 18 | version "6.10.0" 19 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" 20 | integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== 21 | dependencies: 22 | fast-deep-equal "^2.0.1" 23 | fast-json-stable-stringify "^2.0.0" 24 | json-schema-traverse "^0.4.1" 25 | uri-js "^4.2.2" 26 | 27 | ansi-styles@^3.2.1: 28 | version "3.2.1" 29 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 30 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 31 | dependencies: 32 | color-convert "^1.9.0" 33 | 34 | cacheable-request@^6.0.0: 35 | version "6.0.0" 36 | resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.0.0.tgz#4a1727414e02ac4af82560c4da1b61daa3fa2b63" 37 | integrity sha512-2N7AmszH/WPPpl5Z3XMw1HAP+8d+xugnKQAeKvxFZ/04dbT/CAznqwbl+7eSr3HkwdepNwtb2yx3CAMQWvG01Q== 38 | dependencies: 39 | clone-response "^1.0.2" 40 | get-stream "^4.0.0" 41 | http-cache-semantics "^4.0.0" 42 | keyv "^3.0.0" 43 | lowercase-keys "^1.0.1" 44 | normalize-url "^3.1.0" 45 | responselike "^1.0.2" 46 | 47 | chalk@^2.4.2: 48 | version "2.4.2" 49 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 50 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 51 | dependencies: 52 | ansi-styles "^3.2.1" 53 | escape-string-regexp "^1.0.5" 54 | supports-color "^5.3.0" 55 | 56 | charenc@~0.0.1: 57 | version "0.0.2" 58 | resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" 59 | integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= 60 | 61 | clone-response@^1.0.2: 62 | version "1.0.2" 63 | resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" 64 | integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= 65 | dependencies: 66 | mimic-response "^1.0.0" 67 | 68 | color-convert@^1.9.0: 69 | version "1.9.3" 70 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 71 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 72 | dependencies: 73 | color-name "1.1.3" 74 | 75 | color-name@1.1.3: 76 | version "1.1.3" 77 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 78 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 79 | 80 | conf@^4.0.1: 81 | version "4.0.1" 82 | resolved "https://registry.yarnpkg.com/conf/-/conf-4.0.1.tgz#a001a88fc2e999d0691fc9ebd89145d99edc9f1f" 83 | integrity sha512-2imcmy0JZtOlksSkbbG/6h18YnNcg3GPTrQEofHXNYxqo2pofOBAHFKtqrQhY6KqNA9sxo0rJugGdjR8SnUZww== 84 | dependencies: 85 | ajv "^6.10.0" 86 | dot-prop "^5.0.0" 87 | env-paths "^2.2.0" 88 | json-schema-typed "^7.0.0" 89 | make-dir "^3.0.0" 90 | pkg-up "^3.0.1" 91 | write-file-atomic "^2.4.2" 92 | 93 | crypt@~0.0.1: 94 | version "0.0.2" 95 | resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" 96 | integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= 97 | 98 | decompress-response@^3.3.0: 99 | version "3.3.0" 100 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" 101 | integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= 102 | dependencies: 103 | mimic-response "^1.0.0" 104 | 105 | defer-to-connect@^1.0.1: 106 | version "1.0.2" 107 | resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.0.2.tgz#4bae758a314b034ae33902b5aac25a8dd6a8633e" 108 | integrity sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw== 109 | 110 | dot-prop@^5.0.0: 111 | version "5.2.0" 112 | resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" 113 | integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== 114 | dependencies: 115 | is-obj "^2.0.0" 116 | 117 | dotenv@^8.0.0: 118 | version "8.0.0" 119 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.0.0.tgz#ed310c165b4e8a97bb745b0a9d99c31bda566440" 120 | integrity sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg== 121 | 122 | duplexer3@^0.1.4: 123 | version "0.1.4" 124 | resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" 125 | integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= 126 | 127 | end-of-stream@^1.1.0: 128 | version "1.4.1" 129 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" 130 | integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== 131 | dependencies: 132 | once "^1.4.0" 133 | 134 | env-paths@^2.2.0: 135 | version "2.2.0" 136 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" 137 | integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== 138 | 139 | escape-string-regexp@^1.0.5: 140 | version "1.0.5" 141 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 142 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 143 | 144 | fast-deep-equal@^2.0.1: 145 | version "2.0.1" 146 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" 147 | integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= 148 | 149 | fast-json-stable-stringify@^2.0.0: 150 | version "2.0.0" 151 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 152 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 153 | 154 | find-up@^3.0.0: 155 | version "3.0.0" 156 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" 157 | integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== 158 | dependencies: 159 | locate-path "^3.0.0" 160 | 161 | get-stream@^4.0.0, get-stream@^4.1.0: 162 | version "4.1.0" 163 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" 164 | integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== 165 | dependencies: 166 | pump "^3.0.0" 167 | 168 | got@^9.6.0: 169 | version "9.6.0" 170 | resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" 171 | integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== 172 | dependencies: 173 | "@sindresorhus/is" "^0.14.0" 174 | "@szmarczak/http-timer" "^1.1.2" 175 | cacheable-request "^6.0.0" 176 | decompress-response "^3.3.0" 177 | duplexer3 "^0.1.4" 178 | get-stream "^4.1.0" 179 | lowercase-keys "^1.0.1" 180 | mimic-response "^1.0.1" 181 | p-cancelable "^1.0.0" 182 | to-readable-stream "^1.0.0" 183 | url-parse-lax "^3.0.0" 184 | 185 | graceful-fs@^4.1.11: 186 | version "4.1.15" 187 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" 188 | integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== 189 | 190 | has-flag@^3.0.0: 191 | version "3.0.0" 192 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 193 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 194 | 195 | http-cache-semantics@^4.0.0: 196 | version "4.0.3" 197 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" 198 | integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== 199 | 200 | imurmurhash@^0.1.4: 201 | version "0.1.4" 202 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 203 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 204 | 205 | ip-regex@^2.1.0: 206 | version "2.1.0" 207 | resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" 208 | integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= 209 | 210 | is-buffer@~1.1.1: 211 | version "1.1.6" 212 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 213 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 214 | 215 | is-obj@^2.0.0: 216 | version "2.0.0" 217 | resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" 218 | integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== 219 | 220 | json-buffer@3.0.0: 221 | version "3.0.0" 222 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" 223 | integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= 224 | 225 | json-schema-traverse@^0.4.1: 226 | version "0.4.1" 227 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 228 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 229 | 230 | json-schema-typed@^7.0.0: 231 | version "7.0.0" 232 | resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.0.tgz#714f3bb539637644b8cb9c99a097c4ee8f8e8c8f" 233 | integrity sha512-ikVqF4dlAgRvAb3MDAgDQRtB/GIC8+iq+z5bczPh9bUT7bAZCdGfGCypJHBquzZNoxebql1UgPxWbImnvkSuJg== 234 | 235 | keyv@^3.0.0: 236 | version "3.1.0" 237 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" 238 | integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== 239 | dependencies: 240 | json-buffer "3.0.0" 241 | 242 | locate-path@^3.0.0: 243 | version "3.0.0" 244 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" 245 | integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== 246 | dependencies: 247 | p-locate "^3.0.0" 248 | path-exists "^3.0.0" 249 | 250 | lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: 251 | version "1.0.1" 252 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" 253 | integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== 254 | 255 | make-dir@^3.0.0: 256 | version "3.0.0" 257 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" 258 | integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw== 259 | dependencies: 260 | semver "^6.0.0" 261 | 262 | md5@^2.2.1: 263 | version "2.2.1" 264 | resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" 265 | integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= 266 | dependencies: 267 | charenc "~0.0.1" 268 | crypt "~0.0.1" 269 | is-buffer "~1.1.1" 270 | 271 | mimic-response@^1.0.0, mimic-response@^1.0.1: 272 | version "1.0.1" 273 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" 274 | integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== 275 | 276 | moment@^2.24.0: 277 | version "2.24.0" 278 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" 279 | integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== 280 | 281 | normalize-url@^3.1.0: 282 | version "3.3.0" 283 | resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" 284 | integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== 285 | 286 | once@^1.3.1, once@^1.4.0: 287 | version "1.4.0" 288 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 289 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 290 | dependencies: 291 | wrappy "1" 292 | 293 | p-cancelable@^1.0.0: 294 | version "1.1.0" 295 | resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" 296 | integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== 297 | 298 | p-limit@^2.0.0: 299 | version "2.2.0" 300 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" 301 | integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== 302 | dependencies: 303 | p-try "^2.0.0" 304 | 305 | p-locate@^3.0.0: 306 | version "3.0.0" 307 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" 308 | integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== 309 | dependencies: 310 | p-limit "^2.0.0" 311 | 312 | p-try@^2.0.0: 313 | version "2.2.0" 314 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 315 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 316 | 317 | path-exists@^3.0.0: 318 | version "3.0.0" 319 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 320 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 321 | 322 | pkg-up@^3.0.1: 323 | version "3.1.0" 324 | resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" 325 | integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== 326 | dependencies: 327 | find-up "^3.0.0" 328 | 329 | prepend-http@^2.0.0: 330 | version "2.0.0" 331 | resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" 332 | integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= 333 | 334 | psl@^1.1.28: 335 | version "1.1.31" 336 | resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" 337 | integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== 338 | 339 | pump@^3.0.0: 340 | version "3.0.0" 341 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 342 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 343 | dependencies: 344 | end-of-stream "^1.1.0" 345 | once "^1.3.1" 346 | 347 | punycode@^2.1.0, punycode@^2.1.1: 348 | version "2.1.1" 349 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 350 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 351 | 352 | qs@^6.7.0: 353 | version "6.7.0" 354 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 355 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== 356 | 357 | responselike@^1.0.2: 358 | version "1.0.2" 359 | resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" 360 | integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= 361 | dependencies: 362 | lowercase-keys "^1.0.0" 363 | 364 | semver@^6.0.0: 365 | version "6.0.0" 366 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" 367 | integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ== 368 | 369 | signal-exit@^3.0.2: 370 | version "3.0.2" 371 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 372 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 373 | 374 | supports-color@^5.3.0: 375 | version "5.5.0" 376 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 377 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 378 | dependencies: 379 | has-flag "^3.0.0" 380 | 381 | to-readable-stream@^1.0.0: 382 | version "1.0.0" 383 | resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" 384 | integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== 385 | 386 | tough-cookie-file-store@^1.2.0: 387 | version "1.2.0" 388 | resolved "https://registry.yarnpkg.com/tough-cookie-file-store/-/tough-cookie-file-store-1.2.0.tgz#606e217d9d32fc8fdee3b83f3df349d6657aacb2" 389 | integrity sha512-DQ/ngruF/Dn8wPx0lYACPjao8HuDR0vAIBiTehw+TsrpKp4lifjueHDh7dwkIi9IMQ7m4OcTYLx4m9be0KhaBg== 390 | dependencies: 391 | tough-cookie "^2.3.3" 392 | 393 | tough-cookie@^2.3.3: 394 | version "2.5.0" 395 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" 396 | integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== 397 | dependencies: 398 | psl "^1.1.28" 399 | punycode "^2.1.1" 400 | 401 | tough-cookie@^3.0.1: 402 | version "3.0.1" 403 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" 404 | integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== 405 | dependencies: 406 | ip-regex "^2.1.0" 407 | psl "^1.1.28" 408 | punycode "^2.1.1" 409 | 410 | uri-js@^4.2.2: 411 | version "4.2.2" 412 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 413 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 414 | dependencies: 415 | punycode "^2.1.0" 416 | 417 | url-parse-lax@^3.0.0: 418 | version "3.0.0" 419 | resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" 420 | integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= 421 | dependencies: 422 | prepend-http "^2.0.0" 423 | 424 | wrappy@1: 425 | version "1.0.2" 426 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 427 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 428 | 429 | write-file-atomic@^2.4.2: 430 | version "2.4.2" 431 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.2.tgz#a7181706dfba17855d221140a9c06e15fcdd87b9" 432 | integrity sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g== 433 | dependencies: 434 | graceful-fs "^4.1.11" 435 | imurmurhash "^0.1.4" 436 | signal-exit "^3.0.2" 437 | --------------------------------------------------------------------------------