Compare commits

...
4 Commits
Author SHA1 Message Date
shanmiteko dd234691ea fix: 因黑名单而关注失败(#45) 2021-10-11 18:46:32 +08:00
shanmiteko c3c8ffba95 feat: 内置默认设置 2021-10-11 09:32:11 +08:00
shanmiteko ec9a11d5c0 feat: 增加更多时延自定义值(#44) 2021-10-10 21:50:45 +08:00
shanmiteko 1458c800b8 feat: api线路切换具有记忆功能
perf: 重构部分代码
fix: 清理动态时关注列表获取失败
2021-10-10 20:48:17 +08:00
14 changed files with 987 additions and 604 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 12
"ecmaVersion": "latest"
},
"rules": {
}
+25 -9
View File
@@ -26,6 +26,8 @@
- 监控专栏合集
- 自动点赞、评论、乱序转发、@好友、带话题、可选随机动态
- 检测是否中奖
- 已读@
- 已读私信
- 清理动态关注
- 检查更新
- 更多功能设置请参考配置文件
@@ -96,11 +98,25 @@ Chrome浏览器:
3. 在`env.js`中填入`COOKIE`和推送参数
4. 在`my_config.js`中自定义设置
5. 在当前目录下**打开终端**运行可执行文件`lottery`(勿直接点击`lottery`)
- `lottery start` 启动抽奖
- `lottery check` 检测中奖
- `lottery clear` 清理动态
```sh
# unix-like
## 进入脚本所在目录
## 授予执行权限
$ chmod u+x lottery
## 启动脚本
$ ./lottery start
## 检测中奖
$ ./lottery check
## 清理关注动态
$ ./lottery clear
## 下载最新版本
$ ./lottery clear
或者 新建文本文档写入`lottery start`然后更改扩展名为.bat点击运行(已自带start.bat可直接点击)
# windows
## 不需要chmod
## 把`./`换成`.\`或去掉
## 已自带*.bat可直接点击
```
7. 运行截图
![lottery_start](https://gitee.com/shanmite/LotteryAutoScript/raw/main/doc/pic/lottery_start.png)
@@ -175,12 +191,12 @@ npm i && npm run clear
1.初始化
```bash
curl -fsSL https://cdn.staticaly.com/gh/shanmiteko/LotteryAutoScript/main/script/docker/init.sh | sh
$ curl -fsSL https://cdn.staticaly.com/gh/shanmiteko/LotteryAutoScript/main/script/docker/init.sh | sh
```
进入`lottery`文件夹
```bash
cd lottery
$ cd lottery
```
编辑`env.js`与`my_config.js`文件
@@ -192,7 +208,7 @@ cd lottery
例如:
```bash
./start.sh
$ ./start.sh
```
2.更新
@@ -206,8 +222,8 @@ cd lottery
进入lottery上一级目录
```bash
docker image rm -f shanmite/lottery_auto_docker
rm -rf lottery/
$ docker image rm -f shanmite/lottery_auto_docker
$ rm -rf lottery/
```
----------------------------------------
+8 -6
View File
@@ -9,9 +9,11 @@ const bili = require('./net/bili')
*/
async function isMe() {
let desp = '';
const UnreadAtNum = await bili.getUnreadAtNum();
const UnreadSessionNum = await bili.getUnreadSessionNum();
const { follow_unread, unfollow_unread } = UnreadSessionNum || { unfollow_unread: 0, follow_unread: 0 };
const
{ notice_key_words, update_session_wait, get_session_wait } = config,
UnreadAtNum = await bili.getUnreadAtNum(),
UnreadSessionNum = await bili.getUnreadSessionNum(),
{ follow_unread, unfollow_unread } = UnreadSessionNum || { unfollow_unread: 0, follow_unread: 0 };
if (UnreadAtNum > 0) {
log.info('中奖检测', '<-- 正在检查at');
const MyAtInfo = await bili.getMyAtInfo();
@@ -36,8 +38,8 @@ async function isMe() {
session_t = session_ts;
if (unread_count) {
bili.updateSessionStatus(talker_id, type, msg_seqno);
await delay(1000);
if (judge(content, config.notice_key_words)) {
await delay(update_session_wait);
if (judge(content, notice_key_words)) {
desp += `发生时间: ${new Date(timestamp * 1000).toLocaleString()} \n\n`
desp += `用户: ${sender_uid} \n\n`
desp += `私信你(${global_var.get("myUID")})说: ${content} \n\n`
@@ -47,7 +49,7 @@ async function isMe() {
}
}
if (MySession.has_more && index < 16) {
await delay(3e3);
await delay(get_session_wait);
MySession = await bili.getSessionInfo(type, session_t)
} else {
break
+8 -6
View File
@@ -9,20 +9,22 @@ const config = require("./data/config");
* @returns { Promise<number[]> }
*/
async function getFollowList() {
const tagid = await bili.checkMyPartition(config.clear_partition);
const
{ clear_partition, clear_white_list, get_partition_wait } = config,
tagid = await bili.checkMyPartition(clear_partition);
let rmup = [];
if (typeof tagid === 'undefined') {
log.info('获取关注列表', '未能成功获取关注分区id');
return
return rmup
}
let rmup = [];
for (let index = 1; index < 42; index++) {
const uids = await bili.getPartitionUID(tagid, index);
await delay(2e3);
await delay(get_partition_wait);
if (!uids.length) break;
rmup.push(...uids)
}
return config.clear_white_list.length
? rmup.filter(uid => config.clear_white_list.split(',').indexOf(String(uid)) === -1)
return clear_white_list.length
? rmup.filter(uid => clear_white_list.split(',').indexOf(String(uid)) === -1)
: rmup;
}
+43 -41
View File
@@ -83,7 +83,7 @@ class Monitor extends Searcher {
async startLottery() {
const allLottery = await this.filterLotteryInfo()
, len = allLottery.length
, { dy_contents, create_dy, create_dy_mode, wait } = config;
, { create_dy, create_dy_mode, wait } = config;
log.info('筛选动态', `筛选完毕(${len})`);
@@ -129,6 +129,8 @@ class Monitor extends Searcher {
status = await this.go(Lottery)
switch (status) {
case 0:
case 9:
case 19:
break;
case 22:
is_exception = true
@@ -321,7 +323,9 @@ class Monitor extends Searcher {
* @param {LotteryOptions} option
* @returns {Promise<number>}
* 0 - 成功
* 9 - 评论黑名单
* 11 - 评论错误
* 19 - 关注黑名单
* 21 - 关注错误
* 22 - 关注异常
* 31 - 转发失败
@@ -332,53 +336,51 @@ class Monitor extends Searcher {
log.info('NOT_GO_LOTTERY', 'ON');
return 0
}
const { uid, dyid, chat_type, rid, relay_chat, ctrl } = option;
/* 评论 */
if (rid && chat_type) {
let status = 0;
const max_retry_times = 5;
for (let times = 0; times < max_retry_times; times++) {
status = await bili.sendChat(rid, utils.getRandomOne(config.chat), chat_type)
if (status === -1) {
log.warn('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return 0
let status = 0
const
{ chat } = config,
retry = [...new Array(5).keys()],
max_retry_times = retry.length;
await utils.try_for_each(retry, async (times) => {
status = await bili.sendChat(rid, utils.getRandomOne(chat), chat_type)
switch (status) {
case 0:
return true
case -1:
log.warn('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return true
default:
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
log.info('自动评论', `将在 ${times + 1} 分钟后再次发送评论(${times + 1}/${max_retry_times})`)
await utils.delay(60 * 1000 * (times + 1))
return false
}
if (!status) {
break
}
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
log.info('自动评论', `将在 ${times + 1} 分钟后再次发送评论(${times + 1}/${max_retry_times})`)
await utils.delay(60 * 1000 * (times + 1))
}
if (status) return 11;
})
if (status) return 10 + status;
}
/* 关注 */
const [u1, u2] = uid
if (u1) {
let status = await bili.autoAttention(u1)
if (status) {
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return 20 + status
};
status = await bili.movePartition(u1, this.tagid)
if (status) {
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return 20 + status
};
if (u2) {
utils.delay(5000)
status = await bili.autoAttention(u2)
if (status) {
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return 20 + status
};
status = await bili.movePartition(u2, this.tagid)
if (status) {
log.error('抽奖信息', `uid: ${uid},dyid: ${dyid}`)
return 20 + status
};
}
if (uid.length) {
let status = 0
await utils.try_for_each(uid, async (u) => {
status = await bili.autoAttention(u)
switch (status) {
case 0:
return false
case -1:
log.warn('抽奖信息', `uid: ${u},dyid: ${dyid}`)
return true
default:
log.error('抽奖信息', `uid: ${u},dyid: ${dyid}`)
return true
}
})
if (status) return 20 + status
}
/* 点赞 */
+12 -7
View File
@@ -215,7 +215,9 @@ class Searcher {
*/
async getLotteryInfoByUID(UID) {
log.info('获取动态', `开始获取用户${UID}的动态信息`);
const { allModifyDynamicResArray } = await Searcher.checkAllDynamic(UID, config.uid_scan_page, config.search_wait);
const
{ uid_scan_page, search_wait, get_dynamic_detail_wait } = config,
{ allModifyDynamicResArray } = await Searcher.checkAllDynamic(UID, uid_scan_page, search_wait);
let { length } = allModifyDynamicResArray
@@ -239,7 +241,7 @@ class Searcher {
const card = await bili.getOneDynamicByDyid(origin_dynamic_id)
if (card) {
await utils.delay(2000)
await utils.delay(get_dynamic_detail_wait)
const { is_liked } = parseDynamicCard(card)
@@ -273,6 +275,7 @@ class Searcher {
*/
async getLotteryInfoByTag(tag_name) {
const
{ tag_scan_page, search_wait } = config,
tag_id = await bili.getTagIDByTagName(tag_name),
hotdy = await bili.getHotDynamicInfoByTagID(tag_id),
modDR = modifyDynamicRes(hotdy);
@@ -288,7 +291,7 @@ class Searcher {
let mDRdata = modDR.modifyDynamicResArray;
let next_offset = modDR.nextinfo.next_offset;
for (let index = 0; index < config.tag_scan_page; index++) {
for (let index = 0; index < tag_scan_page; index++) {
log.info('获取动态', `读取第${index + 1}页动态`);
const
newdy = await bili.getOneDynamicInfoByTag(tag_name, next_offset),
@@ -299,7 +302,7 @@ class Searcher {
mDRdata.push.apply(mDRdata, _modify.modifyDynamicResArray);
next_offset = _modify.nextinfo.next_offset;
await utils.delay(config.search_wait);
await utils.delay(search_wait);
}
const fomatdata = mDRdata.map(o => {
return {
@@ -326,7 +329,9 @@ class Searcher {
*/
async getLotteryInfoByArticle(key_words) {
log.info('获取动态', `开始获取含关键词${key_words}的专栏信息`);
const cvs = (await bili.searchArticlesByKeyword(key_words)).slice(0, config.article_scan_page);
const
{ article_scan_page, not_check_article, get_dynamic_detail_wait } = config,
cvs = (await bili.searchArticlesByKeyword(key_words)).slice(0, article_scan_page);
/**存储所有专栏中的dyid */
let dyinfos = [];
@@ -354,7 +359,7 @@ class Searcher {
const card = await bili.getOneDynamicByDyid(dyid)
if (card) {
await utils.delay(2000)
await utils.delay(get_dynamic_detail_wait)
const parsed_card = parseDynamicCard(card)
, { is_liked } = parsed_card;
@@ -364,7 +369,7 @@ class Searcher {
_weight += 1;
}
if (_weight >= weight && !config.not_check_article) {
if (_weight >= weight && !not_check_article) {
log.warn('获取动态', `1/2动态曾经转过,该专栏或已查看,故中止`)
_dyinfos = []
break
+284 -1
View File
@@ -1,6 +1,286 @@
const { config_file } = require('../utils');
const config = {
/**
* 监视更转的用户uid
*/
UIDs: [],
/**
* 监视的专栏关键词
*/
Articles: [],
/**
* 监视的tag
*/
TAGs: [],
/**
* 动态中的关键词(表示须同时满足以下条件)
* 符合js正则表达式的字符串
*/
key_words: [
"[抽奖]",
"[转关].*[转关]"
],
/**
* - '00' 关闭自动抽奖
* - '10' 只转发官方抽奖
* - '01' 只转发非官方抽奖
* - '11' 都转
*/
model: '11',
/**
* - '00'关闭自动评论
* - '10'只评论官抽
* - '01'只评论非官抽
* - '11'都评论
*/
chatmodel: '01',
/**
* 不加判断的转发所监视的uid转发的动态
*/
is_imitator: false,
/**
* - 在uid里检索的页数
*/
uid_scan_page: 3,
/**
* - 在tag里检索的页数
*/
tag_scan_page: 3,
/**
* - 获取专栏数量
*/
article_scan_page: 3,
/**
* - 不检查专栏是否看过,若选择检查可以提高检测效率
* - 默认false(检查)
*/
not_check_article: false,
/**
* - 开奖时间距离现在的最大天数
* - 默认不限制
*/
maxday: Infinity,
/**
* - 循环等待时间(指所有操作完毕后的休眠时间)
* - 单位毫秒
*/
lottery_loop_wait: 0,
check_loop_wait: 0,
clear_loop_wait: 0,
update_loop_wait: 0,
/**
* - 转发间隔时间
* - 单位毫秒
* - 上下浮动50%
*/
wait: 30 * 1000,
/**
* - 检索动态间隔
* - 单位毫秒
*/
search_wait: 1000,
/**
* - 读取下一页私信间隔
* - 单位毫秒
*/
get_session_wait: 3000,
/**
* - 已读私信间隔
* - 单位毫秒
*/
update_session_wait: 1000,
/**
* - 读取下一页关注列表间隔
* - 单位毫秒
*/
get_partition_wait: 2000,
/**
* - 获取动态细节间隔
* - 单位毫秒
*/
get_dynamic_detail_wait: 2000,
/**
* - 随机动态间隔
* - 单位毫秒
*/
random_dynamic_wait: 2000,
/**
* - up主粉丝数限制
*/
minfollower: 1000,
/**
* - 只转发已关注的
*/
only_followed: false,
/**
* - 是否发送随机动态(防止被开奖机过滤)
*/
create_dy: false,
/**
* 随机动态类型
* - 0 自定义文字与图片
* - 1 推荐视频
* - -1 混合
*/
create_dy_type: 0,
/**
* - 结束运行时发送随机动态的数量
*/
create_dy_num: 1,
/**
* - 随机动态内容
* - 类型 `content[]`
* @typedef Picture
* @property {string} img_src 站内源
* @property {number} img_width
* @property {number} img_height
* @param { string | Picture[] } content
*/
dy_contents: ['[doge]', '[doge][doge]'],
/**
* - 每转发x条抽奖动态就发送x条随机动态
* - @example [[10,11,9],[6,8,9]] 每转发9,10,11条抽奖动态就发送6,8,9条随机动态
*/
create_dy_mode: [[0], [0]],
/**
* 转发时[at]的用户
*/
at_users: [['转发抽奖娘', 294887687], ['你的工具人老公', 100680137]],
/**
* - 自动同步 https://gitee.com/shanmite/lottery-notice/raw/master/notice.json
* - 英文逗号分隔 如: 1,2,3
*/
blacklist: '',
/**
* 屏蔽词
*/
blockword: ["脚本抽奖", "恭喜", "结果", "抽奖号", "钓鱼", "涨粉"],
/**
* 转发评语
*/
relay: ['转发动态'],
/**
* 评论内容
*/
chat: [
'[OK]', '[星星眼]', '[歪嘴]', '[喜欢]', '[偷笑]', '[笑]', '[喜极而泣]', '[辣眼睛]', '[吃瓜]', '[奋斗]',
'永不缺席 永不中奖 永不放弃!', '万一呢', '在', '冲吖~', '来了', '万一', '[保佑][保佑]', '从未中,从未停', '[吃瓜]', '[抠鼻][抠鼻]',
'来力', '秋梨膏', '[呲牙]', '从不缺席', '分子', '可以', '恰', '不会吧', '1', '好',
'rush', '来来来', 'ok', '冲', '凑热闹', '我要我要[打call]', '我还能中!让我中!!!', '大家都散了吧,已经抽完了,是我的', '我是天选之子', '给我中一次吧!',
'坚持不懈,迎难而上,开拓创新!', '[OK][OK]', '我来抽个奖', '中中中中中中', '[doge][doge][doge]', '我我我',
],
/**
* - 抽奖UP用户分组id(网页端点击分区后地址栏中的tagid)
* - 自动获取
*/
partition_id: 0,
/**
* 是否关注异常
*/
is_exception: false,
/**
* - 中奖通知关键词(满足一个就推送)
* - 符合js正则表达式的字符串
* - 若以 ~ 开头则表示为黑名单规则
*/
notice_key_words: [
"中奖|获得|填写|写上|提供|收货地址|支付宝账号|码|大会员",
"~有奖预约通知"
],
/**
* - 清理白名单uid或dyid
* - 英文逗号分隔 如: 1,2,3
*/
clear_white_list: '',
/**
* - 取关分区
* - 默认为: 此处存放因抽奖临时关注的up
*/
clear_partition: '',
/**
* 清理多少天之前的动态或关注
*/
clear_max_day: 30,
/**
* - 快速移除关注
* - 不加判断只去除关注
*/
clear_quick_remove_attention: false,
/**
* 是否移除动态
*/
clear_remove_dynamic: true,
/**
* 是否移除关注
*/
clear_remove_attention: true,
/**
* 清除动态延时(毫秒)
*/
clear_remove_delay: 8000,
/**
* 清除动态类型
*
* | 动态类型 | type值 |
* | :------- |:----- |
* | 无 | `0` |
* | 转发 | `1` |
* | 含图片 | `2` |
* | 无图纯文字 | `4` |
* | 视频 | `8` |
* | 番剧 | `512` |
* | 活动 | `2048` |
* | 专栏 | `64` |
*
* @example
* 1
* [1,2,4]
*/
clear_dynamic_type: [1],
/**
* 原始设置
* @returns {Object}
@@ -9,6 +289,7 @@ const config = {
delete require.cache[config_file];
return require(config_file)
},
/**
* @param {string} n
*/
@@ -18,10 +299,12 @@ const config = {
this.setObject(new_config)
}
},
init() {
this.setObject(this.raw_config()["default_config"])
},
setObject(o) {
setObject(o = {}) {
Object.entries(o).forEach(([k, v]) => this[k] = v)
}
};
+2 -3
View File
@@ -1,4 +1,4 @@
const { getRemoteConfig, createDir, createFile } = require("../utils");
const { getRemoteConfig } = require("../utils");
const config = require("../data/config");
const key_map = new Map([['DedeUserID', 'myUID'], ['bili_jct', 'csrf']]);
@@ -15,9 +15,8 @@ let global_var = {
* 全局变量初始化
* 更新config
* @param {string} cookie
* @param {number} n
*/
async init(cookie, n) {
async init(cookie) {
if (cookie) {
config.updata(process.env.NUMBER);
+3 -3
View File
@@ -8,12 +8,12 @@ const utils = require("../utils");
* @returns
*/
async function randomDynamic(num) {
const { create_dy_type, dy_contents } = config;
const { create_dy_type, dy_contents, random_dynamic_wait } = config;
if (create_dy_type === -1 || create_dy_type === 0 || typeof create_dy_type === 'undefined') {
for (let index = 0; index < num; index++) {
await bili.createDynamic(utils.getRandomOne(dy_contents));
await utils.delay(2000);
await utils.delay(random_dynamic_wait);
}
}
@@ -26,7 +26,7 @@ async function randomDynamic(num) {
await utils.try_for_each(videos, async ([uid, aid]) => {
if (num--) {
await bili.shareVideo(uid, aid)
await utils.delay(2000)
await utils.delay(random_dynamic_wait)
return false
} else {
return true
+560 -524
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -77,10 +77,10 @@ const utils = {
},
/**
* 延时函数
* @param {number} time ms
* @param {number} [time] ms
* @returns {Promise<void>}
*/
delay(time) {
delay(time = 1000) {
utils.log.info('时延', `${~~time}ms`);
return new Promise(resolve => {
setTimeout(() => {
+30
View File
@@ -107,6 +107,36 @@ module.exports = Object.freeze({
*/
search_wait: 1000,
/**
* - 读取下一页私信间隔
* - 单位毫秒
*/
get_session_wait: 3000,
/**
* - 已读私信间隔
* - 单位毫秒
*/
update_session_wait: 1000,
/**
* - 读取下一页关注列表间隔
* - 单位毫秒
*/
get_partition_wait: 2000,
/**
* - 获取动态细节间隔
* - 单位毫秒
*/
get_dynamic_detail_wait: 2000,
/**
* - 随机动态间隔
* - 单位毫秒
*/
random_dynamic_wait: 2000,
/**
* - up主粉丝数限制
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lottery-auto-script",
"version": "2.2.3",
"version": "2.2.5",
"description": "自动参与B站动态抽奖",
"main": "main.js",
"scripts": {
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
./lottery update
if [[ -r "latest_version.zip" ]]; then
unzip latest_version.zip
rm latest_version.zip
chmod u+x lottery
fi