diff --git a/.eslintrc.json b/.eslintrc.json index 3a28b3ef20..339d795e7b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -9,5 +9,13 @@ "ecmaVersion": "latest" }, "rules": { + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ] } -} +} \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8618b89f1b..8f7613d81c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx @@ -31,4 +31,4 @@ jobs: with: push: true tags: ${{ secrets.DOCKERHUB_REPO }} - platforms: "linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x" + platforms: "linux/amd64,linux/arm64" diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 0b1b8f301d..329102d235 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Mirror + trigger CI diff --git a/.github/workflows/npmp.yml b/.github/workflows/npmp.yml index f50ec67a6d..dbaf625518 100644 --- a/.github/workflows/npmp.yml +++ b/.github/workflows/npmp.yml @@ -12,7 +12,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: JS-DevTools/npm-publish@v1 with: token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 0a4be6a1a9..407301d475 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -30,16 +30,16 @@ jobs: # nodev: 12 steps: - name: "Checkout codes" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Use Node.js" - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "*" - name: "Pkg this" run: | npm run pkg "node${{ matrix.nodev }}-${{ matrix.platform }}-x64" - name: "Upload to artifact" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: node${{ matrix.nodev }}-${{ matrix.platform }}-x64 path: "dist/*.zip" @@ -62,14 +62,14 @@ jobs: - platform: alpine steps: - name: "Checkout codes" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Pkg this" run: | - npm run pkg "${{ matrix.platform }}-arm64" + npm run pkg "node18-${{ matrix.platform }}-arm64" - name: "Upload to artifact" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: arm64 + name: node18-${{ matrix.platform }}-arm64 path: "dist/*.zip" - name: "Upload to release draft" uses: xresloader/upload-to-github-release@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c9e1835ad..daa009b6a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,136 @@ # CHANGELOG +## 主要变化(2.10.2) +* d0a0dd9 feat: 新增QMSG_SOCKET推送参数支持私有云 (#473) +* d40daa2 docs: 注解默认设置 +* a0856fa fix: 风控导致动态内容为空 (#465) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.10.1) +* 30bf989 feat: ai评论支持所有兼容OpenAI API的平台 (#463) +* 15c1d22 fix: 官方非官方抽奖类型判断 (#461) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.10.0) +* b74bb02 feat: ai 评论 (#462) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.8) +* c85c910 doc: 图片链接换源 +* c4d37ed fix: Dockerfile.pkg-arm64换源 +* ca1ea8c fix: 青龙换源gitlab +* d007d8b feat: 更新换源及优化 + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.7) +* bcf87a9 feat: 新增日志等级Notice (#454) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.6) +* f261e91 fix: API.X_POLYMER_WEB_DYNAMIC_V1_DETAIL 缺少参数 (#452) +* 5336068 fix: 修复通过uid监视转发抽奖动态 (#449) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.5) +* f5f63bc fix: 官方抽奖判断未获取到内容 (#445) +* 1832f6d feat: 增加多账号的代理 (#433) +* 717d8cf fix: 换源gitlab + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.4) +* 2f5990f fix: 动态ID字符串长度超过18 + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.3) +* 1d4e8ea fix: 专栏获取为空 (#425) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.2) +* 4134190 fix: 分区移动失败继续运行 (#423) +* 66fa06f fix: 源动态禁止转发 +* b1de122 fix: 验证码识别失败无限重试 +* d3475a5 feat(notify.js): QYWX推送变为图文消息 (#416) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.1) +* 9b0f9bf fix: 仅屏蔽自动回复 (#401) +* 74337ca fix: 账号转发动态途中被系统强制登出,任务直接终止 (#392) +* 6c75d57 lint: eslint +* e723136 feat: 可自定义验证码识别API + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.9.0) +* 1e7d8b2 feat: 动态详情新老api共存 +* fc41750 fix: 源rid_str获取 +* 1a08c0a fix: 话题uid动态获取使用老解析函数 +* 666c057 feat: 使用新接口替换动态详情老接口 (#382) +* df3e97d ci: fix "an artifact with this name already exists on the workflow run" + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.19) +* d9f6bf2 feat: 中奖检测屏蔽自动回复 (#381) +* ded981e fix: 动态卡片解析出错导致程序停止 +* 93d3727 fix: searcher.js typo fix (#379) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.17) +* 60e211e fix: 读取uid出错 (#369) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.17) +* 5b33ae3 fix: 读取uid出错 (#369) +* 7e4650c feat: env可自定义UA (#371) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.16) +* 469a6dc fix: add getOneDynamicByDyid v1 + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.15) +* 7b6356a fix: 风控导致动态全为过时 +* d337fb2 fix: 查询动态详情的接口失效 (#349) +* d9dba6f fix: Cannot read properties of null (reading 'is_liked') +* eafa7c7 fix: Cannot read properties of null (reading 'length') (#360) +* e2976cc docs: 文档更新cookie获取方式 (#357) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.14) +* f5bdd28 fix: update api getOneDynamicByDyid +* cb6fad0 fix: get_dynamic_detail api (#351) +* 780a3a9 fix: update get_dynamic_detail api (#350) + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.13) +* addb9c6 fix: ghproxy.com->mirror.ghproxy.com (#333) +* b86e756 fix: at错位 + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + +## 主要变化(2.8.12) +* 5d30178 ci: docker构建 +* 0c61070 fix: 评论并转发 (#259) +* 6dfee7e fix: 出现帐号未登录错误时自动跳转下一个帐号 + +_如果之前版本小于上一版本,请查看[CHANGELOG](https://github.com/shanmiteko/LotteryAutoScript/blob/main/CHANGELOG.md)变更说明_ + ## 主要变化(2.8.11) * 6496a8f fix: fs.close diff --git a/Dockerfile.pkg-arm64 b/Dockerfile.pkg-arm64 index 80216743bc..38798ca5cb 100644 --- a/Dockerfile.pkg-arm64 +++ b/Dockerfile.pkg-arm64 @@ -5,7 +5,7 @@ WORKDIR /root/lottery RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ apk add --no-cache curl -ENV DOWNLOAD_HOST=https://ghproxy.com/https://github.com \ +ENV DOWNLOAD_HOST=https://github.com \ RELEASE_TAG=v3.4 \ NODEV=18.5.0 \ PKG_CACHE_PATH=/root/.pkg-cache \ diff --git a/README.md b/README.md index 8a16b8f0ed..6c6af8a9d6 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ - 监控用户转发 - 监控话题页面 - 监控专栏合集 -- 自动点赞、评论、乱序转发、@好友、带话题、可选随机动态 +- 自动点赞、AI评论、乱序转发、@好友、带话题、可选随机动态 - 直播预约抽奖 - 检测是否中奖 - 已读@ @@ -64,6 +64,35 @@ #### 手动获取 +第一种 +进入[B站主页](https://www.bilibili.com/)点击个人头像进入个人主页获取Cookie用于登录 + +Chrome浏览器: + +进入个人主页后 +1. `F12`打开控制台 + +2. F5刷新 + +3. 根据图中找到network/网络 搜索nav,点击找到的nav,点标头,下滑,找到COOKIE全部复制 +![image](doc/pic/getCookies2.png) + +注意!!!!!!!!!!! + +注意!!!!!!!!!!! + +注意!!!!!!!!!!! + +所有网页端获取的COOKIE,每次打开网页端时,都会有概率刷新COOKIE,点击退出账号则会退出当前COOKIE。可以利用Chrome内核的浏览器创建多用户,专门用于获取COOKIE。 + +注意!!!!!!!!!!! + +注意!!!!!!!!!!! + +注意!!!!!!!!!!! + + +第二种 进入[B站主页](https://www.bilibili.com/)获取Cookie用于登录 Chrome浏览器: @@ -74,7 +103,7 @@ Chrome浏览器: (此步骤是为了方便后续采用JS获取Cookies,获取完毕后应再次勾选) -![取消httponly](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/getCookies.png) +![取消httponly](doc/pic/getCookies.png) 3. 在Console中复制以下代码回车 @@ -137,7 +166,7 @@ buvid3亦可不填 使用随机生成值 ``` 1. 运行截图 - ![lottery_start](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/lottery_start.png) + ![lottery_start](doc/pic/lottery_start.png) #### 以源码方式运行 @@ -175,36 +204,39 @@ buvid3亦可不填 使用随机生成值 以下是支持的推送方式 -| Name | 归属 | 说明 | -| :----------------: | :--------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `SCKEY` | 微信server酱推送(于2021/4月下线) | server酱的微信通知[官方文档](http://sc.ftqq.com/3.version) | -| `SENDKEY` | 微信server酱(Turbo版)推送 | [获取SENDKEY](https://sct.ftqq.com/sendkey) [选择消息通道](https://sct.ftqq.com/forward) | -| `BARK_PUSH` | [BARK推送](https://apps.apple.com/us/app/bark-customed-notifications/id1403753865) | IOS用户下载BARK这个APP,填写内容是app提供的`设备码`,例如: ,那么此处的设备码就是`123`,再不懂看 [这个图](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/bark.jpg)(注:支持自建填完整链接即可) | -| `BARK_SOUND` | [BARK推送](https://apps.apple.com/us/app/bark-customed-notifications/id1403753865) | bark推送声音设置,例如`choo`,具体值请在`bark`-`推送铃声`-`查看所有铃声` | -| `PUSHDEER_URL` | [Pushdeer](https://github.com/easychen/pushdeer) | 推送api 默认: | -| `PUSHDEER_PUSHKEY` | [Pushdeer](https://github.com/easychen/pushdeer) | PushKey | -| `TG_BOT_TOKEN` | telegram推送 | tg推送(需设备可连接外网),`TG_BOT_TOKEN`和`TG_USER_ID`两者必需,填写自己申请[@BotFather](https://t.me/BotFather)的Token,如`10xxx4:AAFcqxxxxgER5uw` , [具体教程](doc/TG_PUSH.md) | -| `TG_USER_ID` | telegram推送 | tg推送(需设备可连接外网),`TG_BOT_TOKEN`和`TG_USER_ID`两者必需,填写[@getuseridbot](https://t.me/getuseridbot)中获取到的纯数字ID, [具体教程](doc/TG_PUSH.md) | -| `TG_PROXY_HOST` | Telegram 代理的 IP | 代理类型为 http。例子:http代理 则填写 127.0.0.1 | -| `TG_PROXY_PORT` | Telegram 代理的端口 | 例子:http代理 则填写 1080 | -| `DD_BOT_TOKEN` | 钉钉推送 | 钉钉推送(`DD_BOT_TOKEN`和`DD_BOT_SECRET`两者必需)[官方文档](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq) ,只需`https://oapi.dingtalk.com/robot/send?access_token=XXX` 等于`=`符号后面的XXX即可 | -| `DD_BOT_SECRET` | 钉钉推送 | (`DD_BOT_TOKEN`和`DD_BOT_SECRET`两者必需) ,密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的`SECXXXXXXXXXX`等字符 , 注:钉钉机器人安全设置只需勾选`加签`即可,其他选项不要勾选,再不懂看 [这个图](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/DD_bot.png) | -| `IGOT_PUSH_KEY` | iGot推送 | iGot聚合推送,支持多方式推送,确保消息可达。 [参考文档](https://wahao.github.io/Bark-MP-helper ) | -| `QQ_SKEY` | 酷推(Cool Push)推送 | 推送所需的Skey,登录后获取Skey [参考文档](https://cp.xuthus.cc/) | -| `QQ_MODE` | 酷推(Cool Push)推送 | 推送方式(send或group或者wx,默认send) [参考文档](https://cp.xuthus.cc/) | -| `QYWX_AM` | 企业微信应用 | 第一个值是企业id,第二个值是secret,第三个值@all(或者成员id),第四个值是AgentID (逗号分割) 可查看此[教程](http://note.youdao.com/s/HMiudGkb) [官方文档](https://developer.work.weixin.qq.com/document/path/90236) | -| `QYWX_KEY` | 企业微信Bot推送 | 密钥,企业微信推送 webhook 后面的 key [详见官方说明文档](https://work.weixin.qq.com/api/doc/90000/90136/91770) | -| `PUSH_PLUS_TOKEN` | pushplus推送 | 微信扫码登录后一对一推送或一对多推送下面的token(您的Token) [官方网站](http://pushplus.hxtrip.com/) | -| `PUSH_PLUS_USER` | pushplus推送 | 一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码)注:(1、需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送),只填`PUSH_PLUS_TOKEN`默认为一对一推送 | -| `QMSG_KEY` | [Qmsg酱](https://qmsg.zendee.cn)私聊推送 | [Qmsg注册](https://qmsg.zendee.cn/login.html) | -| `QMSG_QQ` | 私聊消息推送接口,指定需要接收消息的QQ | 指定的QQ号必须在你的[管理台](https://qmsg.zendee.cn/me.html)已添加 | -| `SMTP_HOST` | 电子邮件 | smtp服务器的主机名 如: `smtp.qq.com` | -| `SMTP_PORT` | 电子邮件 | smtp服务器的端口 如: `465` | -| `SMTP_USER` | 电子邮件 | 发送方的电子邮件 如: `xxxxxxxxx@qq.com` | -| `SMTP_PASS` | 电子邮件 | smtp服务对应的授权码 | -| `SMTP_TO_USER` | 电子邮件 | 接收方电子邮件 | -| `GOTIFY_URL` | gotify推送 | gotify消息推送地址(例如 http://localhost:8008/message),[官方文档](https://gotify.net/docs/) | -| `GOTIFY_APPKEY` | gotify推送 | 一个gotify application的token,[官方文档](https://gotify.net/docs/) | +| Name | 归属 | 说明 | +| :----------------: | :--------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SCKEY` | 微信server酱推送(于2021/4月下线) | server酱的微信通知[官方文档](http://sc.ftqq.com/3.version) | +| `SENDKEY` | 微信server酱(Turbo版)推送 | [获取SENDKEY](https://sct.ftqq.com/sendkey) [选择消息通道](https://sct.ftqq.com/forward) | +| `BARK_PUSH` | [BARK推送](https://apps.apple.com/us/app/bark-customed-notifications/id1403753865) | IOS用户下载BARK这个APP,填写内容是app提供的`设备码`,例如: ,那么此处的设备码就是`123`,再不懂看 [这个图](doc/pic/bark.jpg)(注:支持自建填完整链接即可) | +| `BARK_SOUND` | [BARK推送](https://apps.apple.com/us/app/bark-customed-notifications/id1403753865) | bark推送声音设置,例如`choo`,具体值请在`bark`-`推送铃声`-`查看所有铃声` | +| `PUSHDEER_URL` | [Pushdeer](https://github.com/easychen/pushdeer) | 推送api 默认: | +| `PUSHDEER_PUSHKEY` | [Pushdeer](https://github.com/easychen/pushdeer) | PushKey | +| `TG_BOT_TOKEN` | telegram推送 | tg推送(需设备可连接外网),`TG_BOT_TOKEN`和`TG_USER_ID`两者必需,填写自己申请[@BotFather](https://t.me/BotFather)的Token,如`10xxx4:AAFcqxxxxgER5uw` , [具体教程](doc/TG_PUSH.md) | +| `TG_USER_ID` | telegram推送 | tg推送(需设备可连接外网),`TG_BOT_TOKEN`和`TG_USER_ID`两者必需,填写[@getuseridbot](https://t.me/getuseridbot)中获取到的纯数字ID, [具体教程](doc/TG_PUSH.md) | +| `TG_PROXY_HOST` | Telegram 代理的 IP | 代理类型为 http。例子:http代理 则填写 127.0.0.1 | +| `TG_PROXY_PORT` | Telegram 代理的端口 | 例子:http代理 则填写 1080 | +| `DD_BOT_TOKEN` | 钉钉推送 | 钉钉推送(`DD_BOT_TOKEN`和`DD_BOT_SECRET`两者必需)[官方文档](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq) ,只需`https://oapi.dingtalk.com/robot/send?access_token=XXX` 等于`=`符号后面的XXX即可 | +| `DD_BOT_SECRET` | 钉钉推送 | (`DD_BOT_TOKEN`和`DD_BOT_SECRET`两者必需) ,密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的`SECXXXXXXXXXX`等字符 , 注:钉钉机器人安全设置只需勾选`加签`即可,其他选项不要勾选,再不懂看 [这个图](doc/pic/DD_bot.png) | +| `FS_BOT_WEBHOOK` | 飞书机器人 | 飞书机器人 webhook,创建自定义机器人后复制 webhook 地址,[官方文档](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot) | +| `FS_BOT_SECRET` | 飞书机器人 | 飞书机器人安全设置中的签名密钥(若开启“签名校验”则必填),[官方文档](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot) | +| `IGOT_PUSH_KEY` | iGot推送 | iGot聚合推送,支持多方式推送,确保消息可达。 [参考文档](https://wahao.github.io/Bark-MP-helper ) | +| `QQ_SKEY` | 酷推(Cool Push)推送 | 推送所需的Skey,登录后获取Skey [参考文档](https://cp.xuthus.cc/) | +| `QQ_MODE` | 酷推(Cool Push)推送 | 推送方式(send或group或者wx,默认send) [参考文档](https://cp.xuthus.cc/) | +| `QYWX_AM` | 企业微信应用 | 第一个值是企业id,第二个值是secret,第三个值@all(或者成员id),第四个值是AgentID (逗号分割) 可查看此[教程](http://note.youdao.com/s/HMiudGkb) [官方文档](https://developer.work.weixin.qq.com/document/path/90236) | +| `QYWX_KEY` | 企业微信Bot推送 | 密钥,企业微信推送 webhook 后面的 key [详见官方说明文档](https://work.weixin.qq.com/api/doc/90000/90136/91770) | +| `PUSH_PLUS_TOKEN` | pushplus推送 | 微信扫码登录后一对一推送或一对多推送下面的token(您的Token) [官方网站](http://pushplus.hxtrip.com/) | +| `PUSH_PLUS_USER` | pushplus推送 | 一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码)注:(1、需订阅者扫描二维码 2、如果您是创建群组所属人,也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送),只填`PUSH_PLUS_TOKEN`默认为一对一推送 | +| `QMSG_SOCKET` | [Qmsg酱](https://qmsg.zendee.cn)私聊推送 | 私有云IP:私有云WEB端口 默认`qmsg.zendee.cn` | +| `QMSG_KEY` | [Qmsg酱](https://qmsg.zendee.cn)私聊推送 | [Qmsg注册](https://qmsg.zendee.cn/login.html) | +| `QMSG_QQ` | 私聊消息推送接口,指定需要接收消息的QQ | 指定的QQ号必须在你的[管理台](https://qmsg.zendee.cn/me.html)已添加 | +| `SMTP_HOST` | 电子邮件 | smtp服务器的主机名 如: `smtp.qq.com` | +| `SMTP_PORT` | 电子邮件 | smtp服务器的端口 如: `465` | +| `SMTP_USER` | 电子邮件 | 发送方的电子邮件 如: `xxxxxxxxx@qq.com` | +| `SMTP_PASS` | 电子邮件 | smtp服务对应的授权码 | +| `SMTP_TO_USER` | 电子邮件 | 接收方电子邮件 | +| `GOTIFY_URL` | gotify推送 | gotify消息推送地址(例如 http://localhost:8008/message),[官方文档](https://gotify.net/docs/) | +| `GOTIFY_APPKEY` | gotify推送 | 一个gotify application的token,[官方文档](https://gotify.net/docs/) | ---------------------------------------- diff --git a/doc/TG_PUSH.md b/doc/TG_PUSH.md index 87dbe1291a..ee0eaa2d1d 100644 --- a/doc/TG_PUSH.md +++ b/doc/TG_PUSH.md @@ -4,16 +4,16 @@ Ⅰ.首先在Telegram上搜索[BotFather](https://t.me/BotFather)机器人
-![TG_PUSH1](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/TG_PUSH1.png) +![TG_PUSH1](pic/TG_PUSH1.png) Ⅱ.利用[BotFather](https://t.me/BotFather)创建一个属于自己的通知机器人,按照下图中的1、2、3步骤拿到token,格式形如```10xxx4:AAFcqxxxxgER5uw```。填入```TG_BOT_TOKEN```
-![TG_PUSH2](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/TG_PUSH2.png)
+![TG_PUSH2](pic/TG_PUSH2.png)
**新创建的机器人需要跟它发一条消息来开启对话,否则可能会遇到secret填对了但是收不到消息的情况**
Ⅲ.再次在Telegram上搜索[getuserIDbot](https://t.me/getuserIDbot)机器人,获取UserID。填入```TG_USER_ID```
-![TG_PUSH3](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/TG_PUSH3.png) +![TG_PUSH3](pic/TG_PUSH3.png) 至此,获取**TG_BOT_TOKEN**以及**TG_USER_ID**的教程结束 diff --git a/doc/linux_schedule.md b/doc/linux_schedule.md index b1839bdee2..2a1c5b1043 100644 --- a/doc/linux_schedule.md +++ b/doc/linux_schedule.md @@ -73,7 +73,7 @@ PATH=/sbin:/bin:/usr/sbin/:/usr/bin 看看有没有效果 -![image-start](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/image-start.png) +![image-start](pic/image-start.png) 7.如果本地的脚本需要更新 diff --git a/doc/pic/getCookies2.png b/doc/pic/getCookies2.png new file mode 100644 index 0000000000..b4cf11ddbc Binary files /dev/null and b/doc/pic/getCookies2.png differ diff --git a/doc/run_use_docker.md b/doc/run_use_docker.md index 79f7441b21..2bb79c6dd1 100644 --- a/doc/run_use_docker.md +++ b/doc/run_use_docker.md @@ -1,7 +1,7 @@ 1.初始化 ```bash -curl -fsSL https://ghproxy.com/https://raw.githubusercontent.com/shanmiteko/LotteryAutoScript/main/script/docker/init.sh | sudo bash +curl -fsSL https://gitlab.com/shanmiteko/LotteryAutoScript/-/raw/main/script/docker/init.sh | sudo bash ``` 进入`lottery`文件夹 diff --git a/doc/run_use_sc.md b/doc/run_use_sc.md index 577f23c466..dac57ef1a2 100644 --- a/doc/run_use_sc.md +++ b/doc/run_use_sc.md @@ -4,7 +4,7 @@ step1: 下载代码到本地 [点此下载](https://github.com/shanmiteko/LotteryAutoScript/archive/refs/heads/main.zip)或如图示下载↓ -![点我加载下载操作图示](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/download.png) +![点我加载下载操作图示](pic/download.png) 下载的压缩包解压后修改env.example.js文件,详见step3 @@ -12,7 +12,7 @@ step2: 下载并安装Node.js [点此进入nodejs下载页面](http://nodejs.cn/download) -![点我加载下载nodejs操作图示](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/nodejs.png) +![点我加载下载nodejs操作图示](pic/nodejs.png) step3:修改env.example.js文件及创建运行文件(打开扩展名显示) diff --git a/doc/win_schedule.md b/doc/win_schedule.md index b2218c2695..9e99d20dcd 100644 --- a/doc/win_schedule.md +++ b/doc/win_schedule.md @@ -16,13 +16,13 @@ 7.在`操作`页面中点击`新建`,选择操作为`启动程序`,在设置里点击浏览找到`start.bat`文件并选择,在`起始于(可选)(T):`中的空白框里输入`start.bat`文件的目录地址,也就是`程序或脚本(P):`里`start.bat`的前面那一串目录地址,最后是以`\`结尾的 ,填好东西后按下面`确认` -![点我加载步骤7结果图示](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/step_menu.png) +![点我加载步骤7结果图示](pic/step_menu.png) 8.在`条件`页面中选择`网络`,设定启动条件为任何连接 9.在`设置`页面中选择如图示选项,或者不修改默认设置 -![点我加载步骤9结果图示](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/shezhi_renwu.png) +![点我加载步骤9结果图示](pic/shezhi_renwu.png) 10.最后按`确定` @@ -30,6 +30,6 @@ 最后你可以在任务列表中选择已有的任务,右边的操作框中选择`运行`点击,启动计划的任务,如下图所示 -![点我加载运行图示](https://raw.githubusercontents.com/shanmiteko/LotteryAutoScript/main/doc/pic/start_renwu.png) +![点我加载运行图示](pic/start_renwu.png) 具体看任务是否正常执行,你可以看看自己的账号动态的最新转发,运行成功每几分钟自动转发抽奖动态 diff --git a/env.example.js b/env.example.js index 967ca7a2ab..9532b96461 100644 --- a/env.example.js +++ b/env.example.js @@ -5,15 +5,20 @@ module.exports = Object.freeze({ * - `NOTE` 帐号备注 * - `NUMBER` 表示是第几个账号 * - `CLEAR` 是否启用清理功能 + * - `ACCOUNT_UA` 账号UA, 可在浏览器控制台输入 navigator.userAgent 查看 + * * ## 高级功能 * - `ENABLE_CHAT_CAPTCHA_OCR` 开启评论验证码识别 使用方法见README - * - `ENABLE_MULTIPLE_ACCOUNT` 是否启用多账号 - * - `MULTIPLE_ACCOUNT_PARM` 多账号参数(JSON格式) <不推荐使用 + * - `CHAT_CAPTCHA_OCR_URL` 验证码识别接口 POST `url`->`code` + * - `ENABLE_AI_COMMENTS` 是否启用AI评论 + * * ## 调试相关 - * - `LOTTERY_LOG_LEVEL` 输出日志等级 Error { - desp += '## [at]检测结果\n\n' - desp += '- - - -\n\n' - desp += `发生时间: ${new Date(at_time * 1000).toLocaleString()}\n\n` - desp += `用户: ${up_uname}\n\n` - desp += `在${business}中@了[你](https://space.bilibili.com/${global_var.get("myUID")})\n\n` - desp += `原内容为: ${source_content}\n\n` - desp += `[直达链接](${url})\n\n` - desp += '- - - -\n\n' + desp += '## [at]检测结果\n\n'; + desp += '- - - -\n\n'; + desp += `发生时间: ${new Date(at_time * 1000).toLocaleString()}\n\n`; + desp += `用户: ${up_uname}\n\n`; + desp += `在${business}中@了[你](https://space.bilibili.com/${global_var.get('myUID')})\n\n`; + desp += `原内容为: ${source_content}\n\n`; + desp += `[直达链接](${url})\n\n`; + desp += '- - - -\n\n'; }); log.info('中奖检测', '--> OK'); } @@ -39,22 +39,22 @@ async function isMe(num) { .slice(0, unread_reply_num) .forEach(({ nickname, uri, source, timestamp }) => { if (judge(source, notice_key_words)) { - desp += '## 回复检测结果\n\n' - desp += '- - - -\n\n' - desp += `发生时间: ${new Date(timestamp * 1000).toLocaleString()}\n\n` - desp += `用户: ${nickname}\n\n` - desp += `回复[你](https://space.bilibili.com/${global_var.get("myUID")})说:\n${source}\n\n` - desp += `[直达链接](${uri})\n\n` - desp += '- - - -\n\n' + desp += '## 回复检测结果\n\n'; + desp += '- - - -\n\n'; + desp += `发生时间: ${new Date(timestamp * 1000).toLocaleString()}\n\n`; + desp += `用户: ${nickname}\n\n`; + desp += `回复[你](https://space.bilibili.com/${global_var.get('myUID')})说:\n${source}\n\n`; + desp += `[直达链接](${uri})\n\n`; + desp += '- - - -\n\n'; } - }) + }); log.info('中奖检测', '--> OK'); } if (follow_unread + unfollow_unread > 0) { const check = async (type) => { let session_t = ''; - let MySession = await bili.getSessionInfo(type) - log.info("准备检查私信", check_session_pages + "页") + let MySession = await bili.getSessionInfo(type); + log.info('准备检查私信', check_session_pages + '页'); for (const index of infiniteNumber()) { for (const Session of MySession.data) { const { sender_uid, session_ts, timestamp, unread_count, talker_id, msg_seqno } = Session; @@ -62,13 +62,13 @@ async function isMe(num) { if (unread_count) { const content = await bili.fetch_session_msgs(talker_id, unread_count); if (judge(content, notice_key_words)) { - desp += '## 私信检测结果\n\n' - desp += '- - - -\n\n' - desp += `发生时间: ${new Date(timestamp * 1000).toLocaleString()}\n\n` - desp += `用户: ${sender_uid}\n\n` - desp += `私信[你](https://space.bilibili.com/${global_var.get("myUID")})说:\n${content}\n\n` - desp += `[直达链接](https://message.bilibili.com/#/whisper/mid${sender_uid})\n\n` - desp += '- - - -\n\n' + desp += '## 私信检测结果\n\n'; + desp += '- - - -\n\n'; + desp += `发生时间: ${new Date(timestamp * 1000).toLocaleString()}\n\n`; + desp += `用户: ${sender_uid}\n\n`; + desp += `私信[你](https://space.bilibili.com/${global_var.get('myUID')})说:\n${content}\n\n`; + desp += `[直达链接](https://message.bilibili.com/#/whisper/mid${sender_uid})\n\n`; + desp += '- - - -\n\n'; } await bili.updateSessionStatus(talker_id, type, msg_seqno); await delay(update_session_wait); @@ -76,26 +76,26 @@ async function isMe(num) { } if (MySession.has_more && index < check_session_pages) { await delay(get_session_wait); - MySession = await bili.getSessionInfo(type, session_t) + MySession = await bili.getSessionInfo(type, session_t); } else { - break + break; } } - } + }; if (follow_unread) { - log.info('中奖检测', '<-- 正在检查已关注者的私信') + log.info('中奖检测', '<-- 正在检查已关注者的私信'); } if (unfollow_unread) { - log.info('中奖检测', '<-- 正在检查未关注者的私信') + log.info('中奖检测', '<-- 正在检查未关注者的私信'); } - await check("1") + await check('1'); log.info('中奖检测', '--> OK'); } if (desp) { - log.info('可能中奖了', desp); + log.notice('可能中奖了', desp); await sendNotify(`帐号${num}可能中奖了`, desp); } else { - log.info('中奖检测', "暂未中奖"); + log.notice('中奖检测', '暂未中奖'); } return; } diff --git a/lib/clear.js b/lib/clear.js index 882dcbc9a7..2b7b4c1530 100644 --- a/lib/clear.js +++ b/lib/clear.js @@ -1,8 +1,8 @@ -const { log, delay, infiniteNumber, retryfn } = require("./utils"); -const bili = require("./net/bili"); -const { Searcher } = require("./core/searcher"); +const { log, delay, infiniteNumber, retryfn } = require('./utils'); +const bili = require('./net/bili'); +const { Searcher } = require('./core/searcher'); const global_var = require('./data/global_var'); -const config = require("./data/config"); +const config = require('./data/config'); /** * 获取关注分区里的uid @@ -16,13 +16,13 @@ async function getFollowList(partition) { let rmup = []; if (typeof tagid === 'undefined') { log.info('获取关注列表', '未能成功获取关注分区id'); - return rmup + return rmup; } for (let index = 1; index < 100; index++) { const uids = await bili.getPartitionUID(tagid, index); await delay(get_partition_wait); if (!uids.length) break; - rmup.push(...uids) + rmup.push(...uids); } return clear_white_list.length ? rmup.filter(uid => clear_white_list.split(',').indexOf(String(uid)) === -1) @@ -37,24 +37,24 @@ async function clear() { let success = true; const uid_list = (await Promise.all(clear_partition.split(',').map(getFollowList))).flat(); if (!uid_list.length) { - log.info('清理关注', `关注为空`) + log.info('清理关注', '关注为空'); } else { - log.info('清理关注', `共有${uid_list.length}个关注`) + log.info('清理关注', `共有${uid_list.length}个关注`); } if (clear_quick_remove_attention) { - log.info('清理关注', '进入只清理关注模式') + log.info('清理关注', '进入只清理关注模式'); /* 专清关注 */ for (const [index, uid] of uid_list.entries()) { const fannumber = await bili.getUserInfo(uid); - log.info('清理关注', `粉丝数${fannumber}`) + log.info('清理关注', `粉丝数${fannumber}`); if (fannumber !== -1 && fannumber < clear_quick_remove_attention_fans_number_smallest) { - log.info('清理关注', `(${index}) (${uid})`) + log.info('清理关注', `(${index}) (${uid})`); /* 取消关注 */ if (await retryfn(3, [false], () => bili.cancelAttention(uid))) { - log.info('清理关注', '成功') + log.info('清理关注', '成功'); } else { - log.error('清理关注', '失败') - break + log.error('清理关注', '失败'); + break; } } /* 延时 */ @@ -77,7 +77,7 @@ async function clear() { )) || { allModifyDynamicResArray: [], offset: '0' }; next_offset = offset; for (const [index, dyinfo] of allModifyDynamicResArray.entries()) { - log.info('清理动态', `第${page + 1}页中的第${index + 1}个动态`) + log.info('清理动态', `第${page + 1}页中的第${index + 1}个动态`); const { type, dynamic_id, create_time, origin_uid } = dyinfo || {}; if (typeof type !== 'undefined' && clear_dynamic_type instanceof Array @@ -87,12 +87,12 @@ async function clear() { const days_ago = (Now - create_time) / 86400; if (days_ago > clear_max_day) { - log.debug('清理动态', `当前UID保护列表:\n${before_separate.join(',')}\n`) + log.debug('清理动态', `当前UID保护列表:\n${before_separate.join(',')}\n`); /* 移除动态 */ if (dynamic_id && clear_remove_dynamic && !(new RegExp(dynamic_id).test(clear_white_list))) { - success = await retryfn(3, [false], () => bili.rmDynamic(dynamic_id)) + success = await retryfn(3, [false], () => bili.rmDynamic(dynamic_id)); } /* 取消关注 */ @@ -101,38 +101,38 @@ async function clear() { && clear_remove_attention && before_separate.indexOf(origin_uid) === -1 && uid_list.indexOf(origin_uid) > -1) { - success = await retryfn(3, [false], () => bili.cancelAttention(origin_uid)) + success = await retryfn(3, [false], () => bili.cancelAttention(origin_uid)); } if (!success) { - log.error("清理失败", "出现错误") - break + log.error('清理失败', '出现错误'); + break; } /* 延时 */ await delay(clear_remove_delay); } else { - log.info('清理动态', `已设置跳过${clear_max_day}天 当前动态(${dynamic_id})发布时间: ${~~days_ago}天前`) + log.info('清理动态', `已设置跳过${clear_max_day}天 当前动态(${dynamic_id})发布时间: ${~~days_ago}天前`); if (origin_uid) { - log.info('清理动态', `储存用户(${origin_uid})防止误删`) - before_separate.push(origin_uid) + log.info('清理动态', `储存用户(${origin_uid})防止误删`); + before_separate.push(origin_uid); } else { - log.info('清理动态', `非转发动态`) + log.info('清理动态', '非转发动态'); } } } else { - log.info('清理动态', `此动态类型为${type} != 要清理的动态类型${clear_dynamic_type}`) + log.info('清理动态', `此动态类型为${type} != 要清理的动态类型${clear_dynamic_type}`); } } /* 延时 */ await delay(clear_remove_delay); - log.info('清理动态', `第${page + 1}页(${allModifyDynamicResArray.length})中的转发动态与关注全部处理成功`) + log.info('清理动态', `第${page + 1}页(${allModifyDynamicResArray.length})中的转发动态与关注全部处理成功`); if (next_offset === '0' || !success) break; } } - return + return; } -module.exports = { clear } +module.exports = { clear }; diff --git a/lib/core/monitor.js b/lib/core/monitor.js index 90584c3bf3..ec7ab15f45 100644 --- a/lib/core/monitor.js +++ b/lib/core/monitor.js @@ -1,12 +1,12 @@ -const { log, hasEnv, shuffle, getRandomOne, delay, try_for_each, retryfn, appendLotteryInfoFile } = require('../utils'); +const { log, hasEnv, shuffle, getRandomOne, getAiContent, delay, try_for_each, retryfn, appendLotteryInfoFile } = require('../utils'); const { send } = require('../net/http'); const bili = require('../net/bili'); const { sendNotify } = require('../helper/notify'); const event_bus = require('../helper/event_bus'); -const { randomDynamic } = require('../helper/randomDynamic') +const { randomDynamic } = require('../helper/randomDynamic'); const { Searcher } = require('./searcher'); -const global_var = require("../data/global_var"); -const config = require("../data/config"); +const global_var = require('../data/global_var'); +const config = require('../data/config'); const d_storage = require('../helper/d_storage'); /** @@ -19,7 +19,7 @@ class Monitor extends Searcher { */ constructor(lottery_param) { super(); - this.lottery_param = lottery_param + this.lottery_param = lottery_param; this.tagid = config.partition_id; /* tagid初始化 */ this.attentionList = ''; /* 转为字符串的所有关注的up主uid */ this.LotteryInfoMap = new Map([ @@ -35,65 +35,65 @@ class Monitor extends Searcher { */ async init() { if (config.model === '00') { - event_bus.emit('Turn_off_the_Monitor', '已关闭所有转发行为') - return + event_bus.emit('Turn_off_the_Monitor', '已关闭所有转发行为'); + return; } if (!this.tagid && config.is_not_create_partition !== true) { - this.tagid = await bili.checkMyPartition() /* 检查关注分区 */ + this.tagid = await bili.checkMyPartition(); /* 检查关注分区 */ if (!this.tagid) { - event_bus.emit('Turn_off_the_Monitor', '分区获取失败') - return + event_bus.emit('Turn_off_the_Monitor', '分区获取失败'); + return; } } /** 关注列表初始化 */ - this.attentionList = await bili.getAttentionList(global_var.get("myUID")); + this.attentionList = await bili.getAttentionList(global_var.get('myUID')); const status = await this.startLottery(); switch (status) { case 0: - event_bus.emit('Turn_on_the_Monitor') + event_bus.emit('Turn_on_the_Monitor'); break; case 1001: - event_bus.emit('Turn_off_the_Monitor', '评论失败') + event_bus.emit('Turn_off_the_Monitor', '评论失败'); + break; + case 1010: + event_bus.emit('Turn_off_the_Monitor', '已掉号'); break; case 1004: - event_bus.emit('Turn_off_the_Monitor', '需要输入验证码') - break - case 2001: - event_bus.emit('Turn_off_the_Monitor', '关注出错') + event_bus.emit('Turn_off_the_Monitor', '需要输入验证码'); break; - case 3001: - event_bus.emit('Turn_off_the_Monitor', '分区移动出错 可于设置处关闭移动分区') + case 2001: + event_bus.emit('Turn_off_the_Monitor', '关注出错'); break; case 2004: case 4005: - log.warn(`账号异常${status}`, `UID(${global_var.get('myUID')})异常号只会对部分UP出现异常`) + log.warn(`账号异常${status}`, `UID(${global_var.get('myUID')})异常号只会对部分UP出现异常`); if (!config.is_exception) { await sendNotify( `[动态抽奖]账号异常${status}通知`, `UID: ${global_var.get('myUID')}\n异常号只会对部分UP出现异常\n可在设置中令is_exception为true关闭此推送\n${log._cache.filter(it => /Error|\s抽奖信息\]/.test(it)).join('\n')}` - ) + ); } config.is_exception = true; - event_bus.emit('Turn_on_the_Monitor') - break + event_bus.emit('Turn_on_the_Monitor'); + break; case 2005: - log.warn('关注已达上限', `UID(${global_var.get('myUID')})关注已达上限,已临时进入只转已关注模式`) + log.warn('关注已达上限', `UID(${global_var.get('myUID')})关注已达上限,已临时进入只转已关注模式`); if (!config.is_outof_maxfollow) { await sendNotify( '[动态抽奖]关注已达上限', `UID: ${global_var.get('myUID')}\n关注已达上限,已临时进入只转已关注模式\n可在设置中令is_outof_maxfollow为true关闭此推送\n${log._cache.filter(it => /Error|\s抽奖信息\]/.test(it)).join('\n')}` - ) + ); } config.is_outof_maxfollow = true; config.only_followed = true; - event_bus.emit('Turn_on_the_Monitor') - break + event_bus.emit('Turn_on_the_Monitor'); + break; case 5001: - event_bus.emit('Turn_off_the_Monitor', '转发失败') - break + event_bus.emit('Turn_off_the_Monitor', '转发失败'); + break; default: - event_bus.emit('Turn_off_the_Monitor', `??? 未知错误: ${status}`) + event_bus.emit('Turn_off_the_Monitor', `??? 未知错误: ${status}`); break; } } @@ -123,53 +123,53 @@ class Monitor extends Searcher { && lottery.uid.length && (new RegExp(lottery.uid.join('|'))).test(this.attentionList) ) { - log.info('过滤', `已关注(${lottery.uid.join(',')})`) - continue + log.info('过滤', `已关注(${lottery.uid.join(',')})`); + continue; } if (lottery.isOfficialLottery) { let { ts } = await bili.getLotteryNotice(lottery.dyid); const ts_10 = Date.now() / 1000; if (ts === -1) { - log.warn('过滤', '无法判断开奖时间') - await delay(filter_wait) - continue + log.warn('过滤', '无法判断开奖时间'); + await delay(filter_wait); + continue; } if (ts === -9999) { - log.info('过滤', '已撤销抽奖') - d_storage.updateDyid(lottery.dyid) - await delay(filter_wait) - continue + log.info('过滤', '已撤销抽奖'); + d_storage.updateDyid(lottery.dyid); + await delay(filter_wait); + continue; } if (ts < ts_10) { - log.info('过滤', '已过开奖时间') - d_storage.updateDyid(lottery.dyid) - await delay(filter_wait) - continue + log.info('过滤', '已过开奖时间'); + d_storage.updateDyid(lottery.dyid); + await delay(filter_wait); + continue; } if (ts > ts_10 + config.maxday * 86400) { - log.info('过滤', '超过指定开奖时间') - d_storage.updateDyid(lottery.dyid) - await delay(filter_wait) - continue + log.info('过滤', '超过指定开奖时间'); + d_storage.updateDyid(lottery.dyid); + await delay(filter_wait); + continue; } } else if (lottery.uid[0]) { - const { minfollower } = config + const { minfollower } = config; if (minfollower > 0) { const followerNum = await bili.getUserInfo(lottery.uid[0]); if (followerNum === -1) { - log.warn('过滤', `粉丝数(${followerNum})获取失败`) - await delay(filter_wait) - continue + log.warn('过滤', `粉丝数(${followerNum})获取失败`); + await delay(filter_wait); + continue; } if (followerNum < minfollower) { - log.info('过滤', `粉丝数(${followerNum})小于指定数量`) - d_storage.updateDyid(lottery.dyid) - await delay(filter_wait) - continue + log.info('过滤', `粉丝数(${followerNum})小于指定数量`); + d_storage.updateDyid(lottery.dyid); + await delay(filter_wait); + continue; } } else { - log.info('过滤', "不过滤粉丝数") + log.info('过滤', '不过滤粉丝数'); } } @@ -179,13 +179,13 @@ class Monitor extends Searcher { && index % getRandomOne(create_dy_mode[0]) === 0 ) { const number = getRandomOne(create_dy_mode[1]) || 0; - randomDynamic(number) + randomDynamic(number); } - status = await this.go(lottery) + status = await this.go(lottery); switch (status) { case 0: - relayed_nums += 1 + relayed_nums += 1; break; case 1002: case 1003: @@ -194,10 +194,10 @@ class Monitor extends Searcher { case 1007: case 1008: case 1009: - case 1010: case 1011: case 2002: case 2003: + case 3001: case 4001: case 4002: case 4003: @@ -205,41 +205,42 @@ class Monitor extends Searcher { case 5002: case 5003: case 5004: - status = 0 + case 5005: + status = 0; break; case 2004: - is_exception = 2004 + is_exception = 2004; break; case 4005: - is_exception = 4005 + is_exception = 4005; break; case 2005: - is_outof_maxfollow = 2005 + is_outof_maxfollow = 2005; break; case 1001: + case 1010: case 1004: case 2001: - case 3001: case 5001: - is_shutdown = true + is_shutdown = true; break; default: break; } - if (is_shutdown) break + if (is_shutdown) break; - d_storage.updateDyid(lottery.dyid) + d_storage.updateDyid(lottery.dyid); await delay(wait * (Math.random() + 0.5)); } log.info('抽奖', `本轮共处理${total_nums}条,成功参与${relayed_nums}条`); return is_exception || is_outof_maxfollow - || status + || status; } else { log.info('抽奖', '无未转发抽奖'); - return 0 + return 0; } } @@ -254,6 +255,7 @@ class Monitor extends Searcher { * @property {string} [rid] 评论标识 * @property {number} chat_type 评论类型 * @property {string} [chat] 评论词 + * @property {boolean} [isAiChat] 是否为AI生成的评论 */ /** * @returns {Promise} @@ -280,7 +282,7 @@ class Monitor extends Searcher { reserve_lottery_wait, sneaktower, key_words, model, chatmodel, chat: chats, relay: relays, block_dynamic_type, max_create_time, is_imitator, - only_followed, at_users, blockword, blacklist, + only_followed, at_users, blockword, blacklist, ai_comments_parm } = config, now_ts = Date.now() / 1000; @@ -292,10 +294,10 @@ class Monitor extends Searcher { /**去重 */ protoLotteryInfo = protoLotteryInfo.filter(({ dyid }) => { if (dyids_map.has(dyid)) { - return false + return false; } dyids_map.set(dyid, false); - return true + return true; }); log.info('筛选动态', `去重后(${protoLotteryInfo.length})`); @@ -308,35 +310,35 @@ class Monitor extends Searcher { .searchDyid(it) .then(hasIt => dyids_map.set(it, hasIt)) ) - ) - log.info('筛选动态', `并发查询本地dyid完毕`); + ); + log.info('筛选动态', '并发查询本地dyid完毕'); } - if (lottery_param[0] !== "APIs" && save_lottery_info_to_file && protoLotteryInfo.length) { - log.info("保存抽奖信息", "保存开始") - await appendLotteryInfoFile(lottery_param[1].toString(), protoLotteryInfo) + if (lottery_param[0] !== 'APIs' && save_lottery_info_to_file && protoLotteryInfo.length) { + log.info('保存抽奖信息', '保存开始'); + await appendLotteryInfoFile(lottery_param[1].toString(), protoLotteryInfo); } - if (lottery_param[0] !== "APIs" && set_lottery_info_url && protoLotteryInfo.length) { - log.info("上传抽奖信息", "上传开始") + if (lottery_param[0] !== 'APIs' && set_lottery_info_url && protoLotteryInfo.length) { + log.info('上传抽奖信息', '上传开始'); await new Promise((resolve) => { send({ url: set_lottery_info_url, - method: "POST", + method: 'POST', headers: { - "content-type": "application/json" + 'content-type': 'application/json' }, contents: protoLotteryInfo, success: ({ body }) => { - log.info("发送获取到的动态数据", body) - resolve() + log.info('发送获取到的动态数据', body); + resolve(); }, failure: err => { - log.error("发送获取到的动态数据", err) - resolve() + log.error('发送获取到的动态数据', err); + resolve(); } - }) - }) + }); + }); } /* 检查动态是否满足要求 */ @@ -353,28 +355,33 @@ class Monitor extends Searcher { log.debug('正在筛选的动态信息', lottery_info); + if (des === '') { + log.info('筛选动态', `获取动态内容为空(https://t.bilibili.com/${dyid})风控`); + return false; + } + if (lottery_info_type.startsWith('sneak') && sneaktower) { - log.info("筛选动态", `偷塔模式不检查是否已转发(https://t.bilibili.com/${dyid})`) + log.info('筛选动态', `偷塔模式不检查是否已转发(https://t.bilibili.com/${dyid})`); } else { /* 遇到转发过就退出 */ if ( ((!check_if_duplicated || check_if_duplicated >= 2) && is_liked) || ((check_if_duplicated >= 1) && dyids_map.get(dyid)) ) { - log.info("筛选动态", `已转发(https://t.bilibili.com/${dyid})`) - return false + log.info('筛选动态', `已转发(https://t.bilibili.com/${dyid})`); + return false; } } /* 超过指定时间退出 */ - if (now_ts - create_time > max_create_time * 86400) { - log.info("筛选动态", `过时动态(https://t.bilibili.com/${dyid})`) - return false + if (create_time && now_ts - create_time > max_create_time * 86400) { + log.info('筛选动态', `过时动态(https://t.bilibili.com/${dyid})`); + return false; } if (is_charge_lottery) { - log.info("筛选动态", `充电抽奖(https://t.bilibili.com/${dyid})`) - return false + log.info('筛选动态', `充电抽奖(https://t.bilibili.com/${dyid})`); + return false; } const @@ -400,61 +407,61 @@ class Monitor extends Searcher { || (!hasOfficialLottery && chatmodel[1] === '1'), keys = [dyid, m_uid, ori_uid]; - log.debug("筛选动态", { real_uid, mIsFollowed, oriIsFollowed, realIsFollowed, needAt, needTopic, type, isRelayDynamic, key_words, has_key_words, blockword, isBlock, isLottery, isSendChat }) + log.debug('筛选动态', { real_uid, mIsFollowed, oriIsFollowed, realIsFollowed, needAt, needTopic, type, isRelayDynamic, key_words, has_key_words, blockword, isBlock, isLottery, isSendChat }); if ( blacklist.split(',').some(id => keys.some(key => { if (key + '' === id) { - log.info("筛选动态", `黑名单匹配(${id})(https://t.bilibili.com/${dyid})`) - return true + log.info('筛选动态', `黑名单匹配(${id})(https://t.bilibili.com/${dyid})`); + return true; } else { - return false + return false; } })) ) { - return false + return false; } if (block_dynamic_type.includes(type)) { - log.warn("筛选动态", `屏蔽动态类型 ${type}`) - return false + log.warn('筛选动态', `屏蔽动态类型 ${type}`); + return false; } /**屏蔽词 */ if (isBlock) { - log.info("筛选动态", `包含屏蔽词(https://t.bilibili.com/${dyid})`) - return false + log.info('筛选动态', `包含屏蔽词(https://t.bilibili.com/${dyid})`); + return false; } if (reserve_id) { if (disable_reserve_lottery) { - log.info("已关闭预约抽奖功能") + log.info('已关闭预约抽奖功能'); } else { - log.info("预约抽奖", "开始"); - log.info("预约抽奖", `奖品: ${reserve_lottery_text}`); + log.info('预约抽奖', '开始'); + log.info('预约抽奖', `奖品: ${reserve_lottery_text}`); if (hasEnv('NOT_GO_LOTTERY')) { log.info('NOT_GO_LOTTERY', 'ON'); } else { await delay(reserve_lottery_wait); - await bili.reserve_lottery(reserve_id) + await bili.reserve_lottery(reserve_id); } } if (is_not_relay_reserve_lottery === true) { - log.info("预约抽奖", "已关闭预约抽奖转关功能"); - return false + log.info('预约抽奖', '已关闭预约抽奖转关功能'); + return false; } } if (!hasOfficialLottery && model[1] === '1' && !has_key_words && description) { - log.warn("筛选动态", `无关键词动态的描述: ${description}\n\n考虑是否修改设置key_words:\n${key_words.join('\n且满足: ')}`) + log.warn('筛选动态', `无关键词动态的描述: ${description}\n\n考虑是否修改设置key_words:\n${key_words.join('\n且满足: ')}`); } /**若勾选只转已关注 */ if (only_followed && (!mIsFollowed || !oriIsFollowed) ) { - log.info("筛选动态", `只转已关注(https://t.bilibili.com/${dyid})`) - return false + log.info('筛选动态', `只转已关注(https://t.bilibili.com/${dyid})`); + return false; } if (isLottery) { @@ -463,7 +470,7 @@ class Monitor extends Searcher { onelotteryinfo.isOfficialLottery = hasOfficialLottery; /**初始化待关注列表 */ - onelotteryinfo.uid = [] + onelotteryinfo.uid = []; if (!realIsFollowed) { onelotteryinfo.uid.push(real_uid); @@ -473,14 +480,14 @@ class Monitor extends Searcher { let /**转发评语 */ - RandomStr = (getRandomOne(relays) || "!!!") + RandomStr = (getRandomOne(relays) || '!!!') .replace(/\$\{uname\}/g, uname), /**控制字段 */ new_ctrl = []; /* 是否需要带话题 */ if (needTopic) { - RandomStr += needTopic + RandomStr += needTopic; } /* 是否需要@ */ @@ -491,9 +498,9 @@ class Monitor extends Searcher { location: RandomStr.length, length: it[0].length + 1, type: 1 - }) - RandomStr += '@' + it[0] - }) + }); + RandomStr += '@' + it[0]; + }); } /* 是否是转发的动态 */ @@ -506,11 +513,11 @@ class Monitor extends Searcher { location: RandomStr.length + 2, length: uname.length + 1, type: 1 - }) + }); ctrl.map(item => { item.location += addlength; return item; - }).forEach(it => new_ctrl.push(it)) + }).forEach(it => new_ctrl.push(it)); if (!oriIsFollowed) { onelotteryinfo.uid.push(ori_uid); } @@ -525,16 +532,31 @@ class Monitor extends Searcher { /* 是否评论 */ if (isSendChat) { - onelotteryinfo.rid = rid - onelotteryinfo.chat = (getRandomOne(chats) || "!!!") - .replace(/\$\{uname\}/g, uname) + onelotteryinfo.rid = rid; + if (hasEnv('ENABLE_AI_COMMENTS')) { + try { + log.info('开始获取Ai评论', `(https://t.bilibili.com/${dyid})`); + onelotteryinfo.chat = await getAiContent( + ai_comments_parm.url, + ai_comments_parm.body, + ai_comments_parm.prompt, + lottery_info.des) || '!!!'; + log.info('获取到Ai评论内容', `${onelotteryinfo.chat}`); + onelotteryinfo.isAiChat = true; + } catch (e) { + log.error('获取AI评论失败,使用随机评论', e); + onelotteryinfo.chat = (getRandomOne(chats) || '!!!').replace(/\$\{uname\}/g, uname); + } + } else { + onelotteryinfo.chat = (getRandomOne(chats) || '!!!').replace(/\$\{uname\}/g, uname); + } } alllotteryinfo.push(onelotteryinfo); } else { - log.info("筛选动态", `非抽奖动态(https://t.bilibili.com/${dyid})`) + log.info('筛选动态', `非抽奖动态(https://t.bilibili.com/${dyid})`); } - }) + }); return alllotteryinfo; } @@ -569,17 +591,18 @@ class Monitor extends Searcher { * - 转发 该动态不能转发分享 5002 * - 转发 请求数据发生错误,请刷新或稍后重试 5003 * - 转发 操作太频繁了,请稍后重试 5004 + * - 转发 源动态禁止转发 5005 */ async go(option) { log.debug('正在转发的动态信息', option); if (hasEnv('NOT_GO_LOTTERY')) { log.info('NOT_GO_LOTTERY', 'ON'); - return 0 + return 0; } let status = 0, - { uid, dyid, chat_type, rid, relay_chat, ctrl, chat } = option, + { uid, dyid, chat_type, rid, relay_chat, ctrl, chat, isAiChat } = option, { check_if_duplicated, is_copy_chat, copy_blockword, is_repost_then_chat, is_not_create_partition } = config; /* 评论 */ @@ -588,11 +611,19 @@ class Monitor extends Searcher { const copy_chat = getRandomOne( (await bili.getChat(rid, chat_type)) .filter(it => !(new RegExp(copy_blockword.join('|')).test(it[1]))) - ) || [";;;;;;;;;", chat || "!!!"]; + ) || [';;;;;;;;;', chat || '!!!']; chat = copy_chat[1] .replace( new RegExp(copy_chat[0], 'g'), - global_var.get("myUNAME") || '') + global_var.get('myUNAME') || ''); + } else { + if (is_repost_then_chat) { + if (isAiChat) { + relay_chat = chat; + } else { + chat = chat + relay_chat; + } + } } status = await retryfn( @@ -600,53 +631,53 @@ class Monitor extends Searcher { [4], () => bili.sendChatWithOcr( rid, - is_repost_then_chat ? relay_chat.split('//')[0] : chat, + chat, chat_type ) - ) + ); if (status === 8 || status === 9) { status = await bili.sendChatWithOcr( rid, - "[doge][doge][doge][doge][doge]", + '[doge][doge][doge][doge][doge]', chat_type - ) + ); } if (status) { - log.warn("抽奖信息", `dyid: ${dyid}, rid: ${rid}, chat_type: ${chat_type}`) - return 1000 + status + log.warn('抽奖信息', `dyid: ${dyid}, rid: ${rid}, chat_type: ${chat_type}`); + return 1000 + status; } } /* 关注 */ if (uid.length) { await try_for_each(uid, async (u) => { - status = await bili.autoAttention(u) + status = await bili.autoAttention(u); if (status === 6) { status = 0; - return false + return false; } if (status) { - log.warn("抽奖信息", `dyid: ${dyid}, uid: ${u}`) - return true + log.warn('抽奖信息', `dyid: ${dyid}, uid: ${u}`); + return true; } else { if (is_not_create_partition !== true) { if (await bili.movePartition(u, this.tagid)) { - log.warn("抽奖信息", `dyid: ${dyid}, uid: ${u} tagid: ${this.tagid}`) + log.warn('抽奖信息', `dyid: ${dyid}, uid: ${u} tagid: ${this.tagid}`); /* 3000系错误 */ - status = 1001 - return true + status = 1001; + return true; } else { - return false + return false; } } else { - return false + return false; } } - }) - if (status) return 2000 + status + }); + if (status) return 2000 + status; } /* 点赞 */ @@ -655,11 +686,11 @@ class Monitor extends Searcher { 5, [3], () => bili.autolike(dyid) - ) + ); if (status) { - log.warn("抽奖信息", `dyid: ${dyid}`) - return 4000 + status + log.warn('抽奖信息', `dyid: ${dyid}`); + return 4000 + status; } } @@ -668,16 +699,20 @@ class Monitor extends Searcher { status = await retryfn( 5, [3, 4], - () => bili.autoRelay(global_var.get("myUID"), dyid, relay_chat, ctrl) - ) + () => bili.autoRelay( + global_var.get('myUID'), + dyid, + relay_chat, + ctrl) + ); if (status) { - log.warn("抽奖信息", `dyid: ${dyid}`) - return 5000 + status + log.warn('抽奖信息', `dyid: ${dyid}`); + return 5000 + status; } } - return status + return status; } } diff --git a/lib/core/searcher.js b/lib/core/searcher.js index 07833b4dad..27a28c5b7c 100644 --- a/lib/core/searcher.js +++ b/lib/core/searcher.js @@ -1,10 +1,10 @@ const utils = require('../utils'); const bili = require('../net/bili'); -const { send } = require("../net/http"); -const { check_if_duplicated, article_scan_page, article_create_time, not_check_article, get_dynamic_detail_wait, uid_scan_page, search_wait, tag_scan_page } = require("../data/config"); -const d_storage = require("../helper/d_storage") +const { send } = require('../net/http'); +const { check_if_duplicated, article_scan_page, article_create_time, not_check_article, get_dynamic_detail_wait, uid_scan_page, search_wait, tag_scan_page } = require('../data/config'); +const d_storage = require('../helper/d_storage'); -const { log } = utils +const { log } = utils; /** * 解析dynamic_detail_card @@ -55,96 +55,207 @@ const { log } = utils * @property {number} type * @property {boolean} hasOfficialLottery 是否官方 * - * @param {object} dynamic_detail_card + * @param {object} data * @return {UsefulDynamicInfo} */ -function parseDynamicCard(dynamic_detail_card) { - const { strToJson } = utils; +function parseDynamicCard(data) { + if (data?.card?.desc?.uid) { + return oldParseDynamicCard(data?.card); + } + + // 如果是多个 items,返回一个数组 + if (Array.isArray(data?.items)) { + return data.items.map(item => parseDynamicCard({ item })); + } + + let ditem = data?.item; /**临时储存单个动态中的信息 */ let obj = {}; - const { desc, card, extension, extend_json = "{}", display = {} } = dynamic_detail_card - , { is_liked = 1, user_profile = {} } = desc - , { info = {} } = user_profile - , cardToJson = strToJson(card) - , extendjsonToJson = strToJson(extend_json) - , { add_on_card_info = [] } = display - , { item } = cardToJson; - const dy_type2chat_type = new Map([[1, 17], [2, 11], [4, 17], [8, 1], [64, 12]]); - /* 转发者的UID */ - obj.uid = desc.uid - /* 转发者的name */ - obj.uname = info.uname || '' - /* 动态是否点过赞 */ - obj.is_liked = is_liked > 0 - /* 动态的ts10 */ - obj.create_time = desc.timestamp - /* 动态类型 */ - obj.type = desc.type - /* 用于发送评论 */ - obj.rid_str = desc.rid_str.length > 12 ? desc.dynamic_id_str : desc.rid_str; - /* 用于发送评论 */ - obj.chat_type = dy_type2chat_type.get(obj.type) || 0; - /* 转发者的动态ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ - obj.dynamic_id = desc.dynamic_id_str; - /* 定位@信息 */ - obj.ctrl = (extendjsonToJson.ctrl) || []; - /* 预约抽奖信息 */ - if (add_on_card_info.length > 0) { - const [status, oid_str, text] = add_on_card_info - .filter(it => typeof it.reserve_attach_card !== 'undefined' - && typeof it.reserve_attach_card.reserve_lottery !== 'undefined' - && typeof it.reserve_attach_card.reserve_button !== 'undefined') - .map(({ reserve_attach_card }) => [ - reserve_attach_card.reserve_button.status, - reserve_attach_card.oid_str, - reserve_attach_card.reserve_lottery.text])[0] || []; - if (status === 1) { - obj.reserve_id = oid_str; - obj.reserve_lottery_text = text; + try { + const dy_typeenum2num = new Map([ + ['DYNAMIC_TYPE_FORWARD', 1], + ['DYNAMIC_TYPE_DRAW', 2], + ['DYNAMIC_TYPE_WORD', 4], + ['DYNAMIC_TYPE_AV', 8], + ['DYNAMIC_TYPE_ARTICLE', 64] + ]); + const dy_type2chat_type = new Map([ + ['DYNAMIC_TYPE_FORWARD', 17], + ['DYNAMIC_TYPE_DRAW', 11], + ['DYNAMIC_TYPE_WORD', 17], + ['DYNAMIC_TYPE_AV', 1], + ['DYNAMIC_TYPE_ARTICLE', 12] + ]); + /* 转发者的UID */ + obj.uid = ditem?.modules?.module_author?.mid || 0; + /* 转发者的name */ + obj.uname = ditem?.modules?.module_author?.name || ''; + /* 动态是否点过赞 */ + obj.is_liked = ditem?.modules?.module_stat?.like?.status || false; + /* 动态的ts10 */ + obj.create_time = ditem?.modules?.module_author?.pub_ts || 0; + /* 动态类型 */ + obj.type = dy_typeenum2num.get(ditem?.type) || 0; + /* 用于发送评论 */ + obj.rid_str = ditem?.basic?.comment_id_str || ''; + /* 用于发送评论 */ + obj.chat_type = ditem?.basic?.comment_type || 0; + /* 转发者的动态ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ + obj.dynamic_id = ditem?.id_str || ''; + /* 定位@信息 */ + obj.ctrl = []; + /* 是否有官方抽奖 */ + obj.hasOfficialLottery = false; + /* 转发描述 */ + obj.description = ''; + let _total_len = 0; + if (Array.isArray(ditem?.modules?.module_dynamic?.desc?.rich_text_nodes)) { + ditem?.modules?.module_dynamic?.desc?.rich_text_nodes.forEach(node => { + if (node.type === 'RICH_TEXT_NODE_TYPE_AT') { + obj.ctrl.push({ + data: node.rid, + location: _total_len, + length: node.text.length, + type: 1 + }); + } + /* 是否有官方抽奖 */ + if (node.type === 'RICH_TEXT_NODE_TYPE_LOTTERY') { + obj.hasOfficialLottery = true; + } + obj.description += node.orig_text; + _total_len += node.text.length; + }); + } else { + ditem?.modules?.module_dynamic?.major?.opus?.summary?.rich_text_nodes.forEach(node => { + if (node.type === 'RICH_TEXT_NODE_TYPE_AT') { + obj.ctrl.push({ + data: node.rid, + location: _total_len, + length: node.text.length, + type: 1 + }); + } + /* 是否有官方抽奖 */ + if (node.type === 'RICH_TEXT_NODE_TYPE_LOTTERY') { + obj.hasOfficialLottery = true; + } + obj.description += node.orig_text; + _total_len += node.text.length; + }); } - } - if (extendjsonToJson[""]) { - let r = extendjsonToJson[""].reserve || {}; - let { reserve_id, reserve_lottery } = r; - if (reserve_lottery === 1) { - obj.reserve_id = reserve_id + ""; - obj.reserve_lottery_text = "信息丢失"; + /* 预约抽奖信息 */ + obj.reserve_id = ditem?.modules?.module_dynamic?.additional?.reserve?.rid || 0; + obj.reserve_lottery_text = ditem?.modules?.module_dynamic?.additional?.reserve?.title || '信息丢失'; + /* 充电抽奖 */ + obj.is_charge_lottery = false; + if (ditem?.modules?.module_dynamic?.additional?.type === 'ADDITIONAL_TYPE_UPOWER_LOTTERY') { + obj.is_charge_lottery = true; } + /* 转发 */ + if (obj.type === 1) { + /* 被转发者的UID */ + obj.origin_uid = ditem?.orig?.modules?.module_author?.mid || 0; + /* 被转发者的name */ + obj.origin_uname = ditem?.orig?.modules?.module_author?.name || ''; + /* 源动态的ts10 */ + obj.origin_create_time = ditem?.orig?.modules?.module_author?.pub_ts || 0; + /* 源动态类型 */ + obj.origin_type = dy_typeenum2num.get(ditem?.orig?.type) || 0; + /* 被转发者的rid(用于发评论) */ + switch (ditem?.orig?.type) { + case 'DYNAMIC_TYPE_DRAW': + obj.origin_rid_str = ditem?.orig?.modules?.module_dynamic?.major?.draw?.id?.toString() || ''; + break; + case 'DYNAMIC_TYPE_AV': + obj.origin_rid_str = ditem?.orig?.modules?.module_dynamic?.major?.archive?.aid || ''; + break; + case 'DYNAMIC_TYPE_ARTICLE': + obj.origin_rid_str = ditem?.orig?.modules?.module_dynamic?.major?.article?.id?.toString() || ''; + break; + default: + obj.origin_rid_str = ditem?.orig?.id_str || ''; + break; + } + /* 用于发送评论 */ + obj.origin_chat_type = dy_type2chat_type.get(ditem?.orig?.type) || 0; + /* 被转发者的动态的ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ + obj.origin_dynamic_id = ditem?.orig?.id_str || ''; + /* 预约抽奖信息 */ + obj.origin_reserve_id = ditem?.orig?.modules?.module_dynamic?.additional?.reserve?.rid || 0; + obj.origin_reserve_lottery_text = ditem?.orig?.modules?.module_dynamic?.additional?.reserve?.title || '信息丢失'; + /* 充电抽奖 */ + obj.origin_is_charge_lottery = false; + if (ditem?.orig?.modules?.module_dynamic?.additional?.type === 'ADDITIONAL_TYPE_UPOWER_LOTTERY') { + obj.origin_is_charge_lottery = true; + } + /* 是否有官方抽奖 */ + obj.origin_hasOfficialLottery = false; + /* 转发描述 */ + obj.origin_description = ''; + if (Array.isArray(ditem?.orig?.modules?.module_dynamic?.desc?.rich_text_nodes)) { + ditem?.orig?.modules?.module_dynamic?.desc?.rich_text_nodes.forEach(node => { + /* 是否有官方抽奖 */ + if (node.type === 'RICH_TEXT_NODE_TYPE_LOTTERY') { + obj.origin_hasOfficialLottery = true; + } + obj.origin_description += node.orig_text; + }); + } else { + ditem?.orig.modules?.module_dynamic?.major?.opus?.summary?.rich_text_nodes.forEach(node => { + /* 是否有官方抽奖 */ + if (node.type === 'RICH_TEXT_NODE_TYPE_LOTTERY') { + obj.origin_hasOfficialLottery = true; + } + obj.origin_description += node.orig_text; + }); + } + } + } catch (e) { + log.error('动态卡片解析', e); } - if (extend_json.match(/"":\{"lottery/)) { - obj.is_charge_lottery = true - } - /* 是否有官方抽奖 */ - obj.hasOfficialLottery = extension && extension.lott && true; - /* 转发者的描述 纯文字内容 图片动态描述 后两个分别是视频动态的描述和视频本身的描述*/ - obj.description = - (item && ((item.content || '') + (item.description || ''))) - || ( - (cardToJson.dynamic || '') - + (cardToJson.desc || '') - + (cardToJson.vest && cardToJson.vest.content || '') - ) - || ''; - /* 转发 */ - if (obj.type === 1) { - const { origin_extension, origin, origin_extend_json = "{}" } = cardToJson - , originToJson = strToJson(origin) - , { add_on_card_info = [] } = display.origin || {} - , originExtendjsonToJson = strToJson(origin_extend_json) - , { user, item } = originToJson; - /* 源动态的ts10 */ - obj.origin_create_time = desc.origin.timestamp; - /* 被转发者的UID */ - obj.origin_uid = desc.origin.uid; - /* 源动态类型 */ - obj.origin_type = desc.orig_type - /* 被转发者的rid(用于发评论) */ - obj.origin_rid_str = desc.origin.rid_str.length > 12 ? desc.origin.dynamic_id_str : desc.origin.rid_str; + + return obj; +} + +/** + * @param {object} dynamic_detail_card + * @return {UsefulDynamicInfo} + */ +function oldParseDynamicCard(dynamic_detail_card) { + const { strToJson } = utils; + /**临时储存单个动态中的信息 */ + let obj = {}; + try { + const { desc, card, extension, extend_json = '{}', display = {} } = dynamic_detail_card + , { is_liked = 1, user_profile = {} } = desc + , { info = {} } = user_profile || {} + , cardToJson = strToJson(card) + , extendjsonToJson = strToJson(extend_json) + , { add_on_card_info = [] } = display || {} + , { item } = cardToJson; + const dy_type2chat_type = new Map([[1, 17], [2, 11], [4, 17], [8, 1], [64, 12]]); + /* 转发者的UID */ + obj.uid = desc.uid; + /* 转发者的name */ + obj.uname = info.uname || ''; + /* 动态是否点过赞 */ + obj.is_liked = is_liked > 0; + /* 动态的ts10 */ + obj.create_time = desc.timestamp; + /* 动态类型 */ + obj.type = desc.type; + /* 用于发送评论 */ + obj.rid_str = desc.rid_str.length > 12 ? desc.dynamic_id_str : desc.rid_str; /* 用于发送评论 */ - obj.origin_chat_type = dy_type2chat_type.get(obj.origin_type) || 0 - /* 被转发者的动态的ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ - obj.origin_dynamic_id = desc.orig_dy_id_str; + obj.chat_type = dy_type2chat_type.get(obj.type) || 0; + /* 转发者的动态ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ + obj.dynamic_id = desc.dynamic_id_str; + /* 定位@信息 */ + obj.ctrl = (extendjsonToJson.ctrl) || []; /* 预约抽奖信息 */ + obj.reserve_id = ''; + obj.reserve_lottery_text = '信息丢失'; if (add_on_card_info.length > 0) { const [status, oid_str, text] = add_on_card_info .filter(it => typeof it.reserve_attach_card !== 'undefined' @@ -155,33 +266,102 @@ function parseDynamicCard(dynamic_detail_card) { reserve_attach_card.oid_str, reserve_attach_card.reserve_lottery.text])[0] || []; if (status === 1) { - obj.origin_reserve_id = oid_str; - obj.origin_reserve_lottery_text = text; + obj.reserve_id = oid_str; + obj.reserve_lottery_text = text; } } - if (originExtendjsonToJson[""]) { - let r = originExtendjsonToJson[""].reserve || {}; + if (extendjsonToJson['']) { + let r = extendjsonToJson[''].reserve || {}; let { reserve_id, reserve_lottery } = r; if (reserve_lottery === 1) { - obj.origin_reserve_id = reserve_id + ""; - obj.origin_reserve_lottery_text = "信息丢失"; + obj.reserve_id = reserve_id + ''; + obj.reserve_lottery_text = '信息丢失'; } } - if (origin_extend_json.match(/"":\{"lottery/)) { - obj.origin_is_charge_lottery = true + obj.is_charge_lottery = false; + if (extend_json.match(/"":\{"lottery/)) { + obj.is_charge_lottery = true; } /* 是否有官方抽奖 */ - obj.origin_hasOfficialLottery = origin_extension && origin_extension.lott; - /* 被转发者的name */ - obj.origin_uname = (user && (user.name || user.uname)) || ''; - /* 被转发者的描述 */ - obj.origin_description = - (item && (item.content || '' + item.description || '')) - || (originToJson.dynamic || '' + originToJson.desc || '') + obj.hasOfficialLottery = extension && extension.lott && true || false; + /* 转发者的描述 纯文字内容 图片动态描述 后两个分别是视频动态的描述和视频本身的描述*/ + obj.description = + (item && ((item.content || '') + (item.description || ''))) + || ( + (cardToJson.dynamic || '') + + (cardToJson.desc || '') + + (cardToJson.vest && cardToJson.vest.content || '') + ) || ''; + if (obj.description.startsWith('互动抽奖 ')) { + obj.hasOfficialLottery = true; + } + /* 转发 */ + if (obj.type === 1) { + const { origin_extension, origin, origin_extend_json = '{}' } = cardToJson + , originToJson = strToJson(origin) + , { add_on_card_info = [] } = display.origin || {} + , originExtendjsonToJson = strToJson(origin_extend_json) + , { user, item } = originToJson; + /* 源动态的ts10 */ + obj.origin_create_time = desc.origin.timestamp; + /* 被转发者的UID */ + obj.origin_uid = desc.origin.uid; + /* 源动态类型 */ + obj.origin_type = desc.orig_type; + /* 被转发者的rid(用于发评论) */ + obj.origin_rid_str = desc.origin.rid_str.length > 12 ? desc.origin.dynamic_id_str : desc.origin.rid_str; + /* 用于发送评论 */ + obj.origin_chat_type = dy_type2chat_type.get(obj.origin_type) || 0; + /* 被转发者的动态的ID !!!!此为大数需使用字符串值,不然JSON.parse()会有丢失精度 */ + obj.origin_dynamic_id = desc.orig_dy_id_str; + /* 预约抽奖信息 */ + obj.origin_reserve_id = ''; + obj.origin_reserve_lottery_text = '信息丢失'; + if (add_on_card_info.length > 0) { + const [status, oid_str, text] = add_on_card_info + .filter(it => typeof it.reserve_attach_card !== 'undefined' + && typeof it.reserve_attach_card.reserve_lottery !== 'undefined' + && typeof it.reserve_attach_card.reserve_button !== 'undefined') + .map(({ reserve_attach_card }) => [ + reserve_attach_card.reserve_button.status, + reserve_attach_card.oid_str, + reserve_attach_card.reserve_lottery.text])[0] || []; + if (status === 1) { + obj.origin_reserve_id = oid_str; + obj.origin_reserve_lottery_text = text; + } + } + if (originExtendjsonToJson['']) { + let r = originExtendjsonToJson[''].reserve || {}; + let { reserve_id, reserve_lottery } = r; + if (reserve_lottery === 1) { + obj.origin_reserve_id = reserve_id + ''; + obj.origin_reserve_lottery_text = '信息丢失'; + } + } + obj.origin_is_charge_lottery = false; + if (origin_extend_json.match(/"":\{"lottery/)) { + obj.origin_is_charge_lottery = true; + } + /* 是否有官方抽奖 */ + obj.origin_hasOfficialLottery = origin_extension && origin_extension.lott || false; + /* 被转发者的name */ + obj.origin_uname = (user && (user.name || user.uname)) || ''; + /* 被转发者的描述 */ + obj.origin_description = + (item && (item.content || '' + item.description || '')) + || (originToJson.dynamic || '' + originToJson.desc || '') + || ''; + if (obj.origin_description.startsWith('互动抽奖 ')) { + obj.origin_hasOfficialLottery = true; + } + } + } catch (e) { + log.error('动态卡片解析', e); } - return obj + return obj; } /** @@ -190,21 +370,24 @@ function parseDynamicCard(dynamic_detail_card) { * @returns {{modifyDynamicResArray: UsefulDynamicInfo[], nextinfo: {has_more: number, next_offset: string}} | UsefulDynamicInfo |null} */ function modifyDynamicRes(res) { - const + let { data, code } = utils.strToJson(res), - { cards = [], has_more, offset } = data || {}; + { items, has_more, offset } = data || {}; if (code !== 0) { log.error('处理动态数据', '获取动态数据出错,可能是访问太频繁 \n' + res); return null; } - - if (!cards.length) { - log.warn('处理动态数据', '未找到任何动态信息') + /** + * !cards已经能涵盖cards == null,你在想什么? + */ + if (!items || !items.length) { + log.warn('处理动态数据', '未找到任何动态信息'); + items = []; } - if (typeof has_more === "undefined" - && typeof offset === "undefined") { + if (typeof has_more === 'undefined' + && typeof offset === 'undefined') { log.error('处理动态数据', '该功能已失效'); return null; } @@ -215,23 +398,21 @@ function modifyDynamicRes(res) { */ next = { has_more, - next_offset: typeof offset === 'string' - ? offset - : /(?<=next_offset":)[0-9]+/.exec(res)[0] + next_offset: offset }, /** * 储存获取到的一组动态中的信息 */ array = next.has_more === 0 ? [] - : cards.map(parseDynamicCard) + : items.map(item => parseDynamicCard({ item })); - log.info('处理动态数据', `动态数据读取完毕(${cards.length})(${next.has_more})`); + log.info('处理动态数据', `动态数据读取完毕(${items.length})(${next.has_more})`); return { modifyDynamicResArray: array, nextinfo: next - } + }; } /** @@ -247,7 +428,7 @@ class Searcher { * @param {string} [offset] 默认'0' * @returns {Promise<{allModifyDynamicResArray: UsefulDynamicInfo[], offset: string} | null>} 获取前 `pages*12` 个动态信息 */ - static async checkAllDynamic(hostuid, pages, time = 0, offset = '0') { + static async checkAllDynamic(host_mid, pages, time = 0, offset = '0') { log.info('检查所有动态', `准备读取${pages}页动态`); const { getOneDynamicInfoByUID } = bili, @@ -258,7 +439,7 @@ class Searcher { /** * 储存了特定UID的请求函数 */ - hadUidGetOneDynamicInfoByUID = curriedGetOneDynamicInfoByUID(hostuid); + hadUidGetOneDynamicInfoByUID = curriedGetOneDynamicInfoByUID(host_mid); /** * 储存所有经过整理后信息 @@ -269,12 +450,15 @@ class Searcher { for (let i = 0; i < pages; i++) { log.info('检查所有动态', `正在读取其中第${i + 1}页动态`); - const - OneDynamicInfo = await hadUidGetOneDynamicInfoByUID(offset), - mDRdata = modifyDynamicRes(OneDynamicInfo); + // 当 offset 为 '0'(初始页)时,传入 offset 会报错 + const OneDynamicInfo = offset === '0' + ? await hadUidGetOneDynamicInfoByUID() + : await hadUidGetOneDynamicInfoByUID(offset); + + const mDRdata = modifyDynamicRes(OneDynamicInfo); if (mDRdata === null) { - return null + return null; } const @@ -286,7 +470,7 @@ class Searcher { if (nextinfo.has_more === 0) { offset = nextinfo.next_offset; - log.info('检查所有动态', `已经是最后一页了故无法读取更多`); + log.info('检查所有动态', '已经是最后一页了故无法读取更多'); break; } else { /**合并 */ @@ -297,7 +481,7 @@ class Searcher { await utils.delay(time); } - log.info('检查所有动态', `${pages}页信息读取完成`) + log.info('检查所有动态', `${pages}页信息读取完成`); return ({ allModifyDynamicResArray, offset }); } @@ -312,7 +496,7 @@ class Searcher { log.info('获取动态', `开始获取用户${UID}的动态信息`); const AllDynamic = await Searcher.checkAllDynamic(UID, uid_scan_page, search_wait); - if (AllDynamic === null) return null + if (AllDynamic === null) return null; let { allModifyDynamicResArray } = AllDynamic, { length } = allModifyDynamicResArray; @@ -322,10 +506,10 @@ class Searcher { const fomatdata = await allModifyDynamicResArray .filter(d => { if (d.type === 1) { - return true + return true; } else { - length-- - return false + length--; + return false; } }) .reduce(async (pre, cur) => { @@ -335,12 +519,12 @@ class Searcher { is_liked = false; if (!check_if_duplicated || check_if_duplicated >= 2) { - const card = await bili.getOneDynamicByDyid(origin_dynamic_id) - log.info('获取动态', `查看源动态(${origin_dynamic_id})是否点赞 (${length--})`) + const card = await bili.getOneDynamicByDyid(origin_dynamic_id); + log.info('获取动态', `查看源动态(${origin_dynamic_id})是否点赞 (${length--})`); if (card) { - ({ is_liked } = parseDynamicCard(card)) + ({ is_liked } = parseDynamicCard(card)); } - await utils.delay(get_dynamic_detail_wait) + await utils.delay(get_dynamic_detail_wait); } results.push({ @@ -359,10 +543,10 @@ class Searcher { des: cur.origin_description, type: cur.origin_type, hasOfficialLottery: cur.origin_hasOfficialLottery - }) + }); - return results - }, Promise.resolve([])) + return results; + }, Promise.resolve([])); log.info('获取动态', `成功获取用户${UID}的动态信息`); @@ -424,10 +608,10 @@ class Searcher { type: o.type, hasOfficialLottery: o.hasOfficialLottery }; - }) + }); log.info('获取动态', `成功获取带话题#${tag_name}#的动态信息`); - return fomatdata + return fomatdata; } /** @@ -445,12 +629,12 @@ class Searcher { for (const { id, pub_time } of cvs) { let now_time = Math.floor(Date.now() / 1000); if ((now_time - pub_time) / 86400 > article_create_time) { - log.warn("获取动态", `该专栏(${id})创建时间大于设定天数(${article_create_time}天)`) - continue + log.warn('获取动态', `该专栏(${id})创建时间大于设定天数(${article_create_time}天)`); + continue; } const - content = (await bili.getOneArticleByCv(id) || "").split("推荐文章")[0], - dyids = content.match(/[0-9]{18}/g) || [], + content = (await bili.getOneArticleByCv(id) || '').split('推荐文章')[0], + dyids = content.match(/[0-9]{18,}/g) || [], short_ids = content.match(/(?<=b23.tv\/)[a-zA-Z0-9]{7}/g) || [], short_id_set = [...new Set(short_ids)], short_ids_to_dyids = await Promise.all(short_id_set.map(bili.shortDynamicIdToDyid)), @@ -463,45 +647,41 @@ class Searcher { _weight = 0, /**单个专栏中的dyid */ _dyinfos = []; - log.info('获取动态', `提取专栏(${id})中提及的dyid(${length})`) + log.info('获取动态', `提取专栏(${id})中提及的dyid(${length})`); /**遍历某专栏中的dyids */ for (const dyid of dyid_set) { - if (typeof dyid === "string" - && dyid.length === utils.dyid_length) { + log.info('获取动态', `查看专栏中所提及动态(${dyid}) (${length--})`); + const card = await bili.getOneDynamicByDyid(dyid); - log.info('获取动态', `查看专栏中所提及动态(${dyid}) (${length--})`) - const card = await bili.getOneDynamicByDyid(dyid) + if (card) { + await utils.delay(get_dynamic_detail_wait); - if (card) { - await utils.delay(get_dynamic_detail_wait) - - const parsed_card = parseDynamicCard(card) - , { is_liked } = parsed_card; - - if ( - ((!check_if_duplicated || check_if_duplicated >= 2) - && is_liked) - || ((check_if_duplicated >= 1) - && await d_storage.searchDyid(dyid)) - ) { - log.info('获取动态', `动态(${dyid})已转发过`) - _weight += 1; - } + const parsed_card = parseDynamicCard(card) + , { is_liked } = parsed_card; - if (_weight >= weight && !not_check_article) { - log.warn('获取动态', `1/2动态曾经转过,该专栏或已查看,故中止`) - _dyinfos = [] - break - } + if ( + ((!check_if_duplicated || check_if_duplicated >= 2) + && is_liked) + || ((check_if_duplicated >= 1) + && await d_storage.searchDyid(dyid)) + ) { + log.info('获取动态', `动态(${dyid})已转发过`); + _weight += 1; + } - _dyinfos.push(parsed_card); + if (_weight >= weight && !not_check_article) { + log.warn('获取动态', '1/2动态曾经转过,该专栏或已查看,故中止'); + _dyinfos = []; + break; } + + _dyinfos.push(parsed_card); } else { - log.warn('获取动态', `动态(${dyid})无效 (${length--})`) + log.warn('获取动态', `动态细节获取失败(${dyid})`); } } - dyinfos.push(..._dyinfos) + dyinfos.push(..._dyinfos); } const fomatdata = dyinfos.map(o => { return { @@ -521,10 +701,10 @@ class Searcher { type: o.type, hasOfficialLottery: o.hasOfficialLottery }; - }) + }); log.info('获取动态', `成功获取含关键词${key_words}的专栏信息`); - return fomatdata + return fomatdata; } /** @@ -536,9 +716,9 @@ class Searcher { return new Promise((resolve) => { if (api) { const { strToJson } = utils; - log.info('获取动态', `开始获取链接(${api})中的抽奖信息`) - if (api.startsWith("file://")) { - utils.readLotteryInfoFile(api.substring(7)).then(resolve) + log.info('获取动态', `开始获取链接(${api})中的抽奖信息`); + if (api.startsWith('file://')) { + utils.readLotteryInfoFile(api.substring(7)).then(resolve); } else { send({ url: api, @@ -548,8 +728,8 @@ class Searcher { method: 'GET', success: ({ body }) => { if (body.err_msg) { - log.error("从API响应数据中获取抽奖信息", body.err_msg) - resolve(null) + log.error('从API响应数据中获取抽奖信息', body.err_msg); + resolve(null); } else { const raw_lottery_info = strToJson(body).lottery_info; @@ -562,46 +742,46 @@ class Searcher { , { dyid } = cur; if (!check_if_duplicated || check_if_duplicated >= 2) { - log.info('获取动态', `查看动态(${dyid})是否点赞 (${length--})`) - const card = await bili.getOneDynamicByDyid(dyid) + log.info('获取动态', `查看动态(${dyid})是否点赞 (${length--})`); + const card = await bili.getOneDynamicByDyid(dyid); if (card) { - await utils.delay(get_dynamic_detail_wait) + await utils.delay(get_dynamic_detail_wait); - const { is_liked } = parseDynamicCard(card) + const { is_liked } = parseDynamicCard(card); if (is_liked) { - log.info('获取动态', `动态(${dyid})已转发过`) + log.info('获取动态', `动态(${dyid})已转发过`); } else { - cur.is_liked = is_liked - results.push(cur) + cur.is_liked = is_liked; + results.push(cur); } } } else { - results.push(cur) + results.push(cur); } - return results + return results; - }, Promise.resolve([])) + }, Promise.resolve([])); - resolve(lottery_info) - return + resolve(lottery_info); + return; } } - log.error("从API响应数据中获取抽奖信息", "非Json数据或没有lottery_info或lottery为空") - resolve(null) + log.error('从API响应数据中获取抽奖信息', '非Json数据或没有lottery_info或lottery为空'); + resolve(null); } }, failure: err => { - log.error("从API响应数据中获取抽奖信息", err) - resolve(null) + log.error('从API响应数据中获取抽奖信息', err); + resolve(null); } - }) + }); } } else { - log.warn('获取动态', `链接为空`) - resolve(null) + log.warn('获取动态', '链接为空'); + resolve(null); } }); } @@ -619,33 +799,30 @@ class Searcher { dyinfos = []; for (const dyid of dyids) { - if (typeof dyid === "string" - && dyid.length === utils.dyid_length) { - - log.info('获取动态', `查看Txt中所提及动态(${dyid}) (${length--})`) - const card = await bili.getOneDynamicByDyid(dyid) - - if (card) { - await utils.delay(get_dynamic_detail_wait) - - const parsed_card = parseDynamicCard(card) - , { is_liked } = parsed_card; - - if ( - ((!check_if_duplicated || check_if_duplicated >= 2) - && is_liked) - || ((check_if_duplicated >= 1) - && await d_storage.searchDyid(dyid)) - ) { - log.info('获取动态', `动态(${dyid})已转发过`) - continue - } - - dyinfos.push(parsed_card); + log.info('获取动态', `查看Txt中所提及动态(${dyid}) (${length--})`); + const card = await bili.getOneDynamicByDyid(dyid); + + if (card) { + await utils.delay(get_dynamic_detail_wait); + + const parsed_card = parseDynamicCard(card) + , { is_liked } = parsed_card; + + if ( + ((!check_if_duplicated || check_if_duplicated >= 2) + && is_liked) + || ((check_if_duplicated >= 1) + && await d_storage.searchDyid(dyid)) + ) { + log.info('获取动态', `动态(${dyid})已转发过`); + continue; } + + dyinfos.push(parsed_card); } else { - log.warn('获取动态', `动态(${dyid})无效 (${length--})`) + log.warn('获取动态', `动态细节获取失败(${dyid})`); } + } const fomatdata = dyinfos.map(o => { return { @@ -665,10 +842,10 @@ class Searcher { type: o.type, hasOfficialLottery: o.hasOfficialLottery }; - }) - log.info('获取动态', `成功获取txt信息`); + }); + log.info('获取动态', '成功获取txt信息'); - return fomatdata + return fomatdata; } } diff --git a/lib/data/config.js b/lib/data/config.js index d4390c1c7f..e92f185881 100644 --- a/lib/data/config.js +++ b/lib/data/config.js @@ -75,15 +75,15 @@ const config = { * API发送数据类型 {LotteryInfo[]} * 上传抽奖信息的链接字符串 */ - set_lottery_info_url: "", + set_lottery_info_url: '', /** * 动态中的关键词(表示须同时满足以下条件) * 符合js正则表达式的字符串 */ key_words: [ - "[抽奖送揪]|福利", - "[转关评粉]|参与" + '[抽奖送揪]|福利', + '[转关评粉]|参与' ], /** @@ -308,7 +308,7 @@ const config = { /** * 屏蔽词 */ - blockword: ["脚本", "抽奖号", "钓鱼"], + blockword: ['脚本', '抽奖号', '钓鱼'], /** * 转发并评论 @@ -332,6 +332,12 @@ const config = { '坚持不懈,迎难而上,开拓创新!', '[OK][OK]', '我来抽个奖', '中中中中中中', '[doge][doge][doge]', '我我我', ], + ai_comments_parm: { + url: '', + body: {}, + prompt: '' + }, + /** * 是否抄热评 */ @@ -370,10 +376,10 @@ const config = { * - 优先级递增 */ notice_key_words: [ - "~预约成功|预约主题", - "中奖|获得|填写|写上|提供|收货地址|支付宝账号|码|大会员", - "~你的账号在新设备或平台登录成功", - "~你预约的直播已开始" + '~预约成功|预约主题', + '中奖|获得|填写|写上|提供|收货地址|支付宝账号|码|大会员', + '~你的账号在新设备或平台登录成功', + '~你预约的直播已开始' ], /** @@ -457,23 +463,23 @@ const config = { */ raw_config() { delete require.cache[config_file]; - return require(config_file) + return require(config_file); }, /** * @param {string} n */ updata(n) { - this.init() - this.setObject(this.raw_config()[`config_${n}`]) + this.init(); + this.setObject(this.raw_config()[`config_${n}`]); }, init() { - this.setObject(this.raw_config()["default_config"]) + this.setObject(this.raw_config()['default_config']); }, setObject(o = {}) { - Object.entries(o).forEach(([k, v]) => this[k] = v) + Object.entries(o).forEach(([k, v]) => this[k] = v); } }; diff --git a/lib/data/env.js b/lib/data/env.js index e3e677150e..70d040bd72 100644 --- a/lib/data/env.js +++ b/lib/data/env.js @@ -1,4 +1,4 @@ -const { env_file } = require("../utils"); +const { env_file } = require('../utils'); const env = { /** @@ -7,28 +7,29 @@ const env = { */ raw_env() { delete require.cache[env_file]; - return require(env_file) + return require(env_file); }, init() { - const raw_env = this.raw_env() + const raw_env = this.raw_env(); this.setEnv({ - ...raw_env["account_parm"], - ...raw_env["push_parm"] - }) + ...raw_env['account_parm'], + ...raw_env['push_parm'], + ...raw_env['ai_parm'] + }); }, /** * @returns {Object[]} */ get_multiple_account() { - return this.raw_env()["multiple_account_parm"] + return this.raw_env()['multiple_account_parm']; }, setEnv(o) { process.env = { ...process.env, ...o - } + }; } -} +}; module.exports = env; \ No newline at end of file diff --git a/lib/data/global_var.js b/lib/data/global_var.js index ee45c3b757..b1b0cb5094 100644 --- a/lib/data/global_var.js +++ b/lib/data/global_var.js @@ -1,13 +1,13 @@ -const { getRandomOne, createDir, createFile, dyids_dir } = require("../utils"); -const config = require("../data/config"); +const { getRandomOne, createDir, createFile, dyids_dir } = require('../utils'); +const config = require('../data/config'); let global_var = { inner: {}, get(key) { - return this.inner[key] + return this.inner[key]; }, set(key, value) { - this.inner[key] = value + this.inner[key] = value; }, /** * 全局变量初始化 @@ -22,28 +22,28 @@ let global_var = { ['DedeUserID', 'myUID'], ['bili_jct', 'csrf']]), LotteryOrderMap = new Map([ - [0, "UIDs"], - [1, "TAGs"], - [2, "Articles"], - [3, "APIs"], - [4, "TxT"], + [0, 'UIDs'], + [1, 'TAGs'], + [2, 'Articles'], + [3, 'APIs'], + [4, 'TxT'], ]); config.updata(num); if (!/buvid3/.test(cookie)) { - const charset = "0123456789ABCDEF".split(""); - const buvid3 = "x".repeat(8).split("").map(() => getRandomOne(charset)).join("") - + "-" - + "x".repeat(4).split("").map(() => getRandomOne(charset)).join("") - + "-" - + "x".repeat(4).split("").map(() => getRandomOne(charset)).join("") - + "-" - + "x".repeat(4).split("").map(() => getRandomOne(charset)).join("") - + "-" - + "x".repeat(17).split("").map(() => getRandomOne(charset)).join("") - + "infoc"; - this.set('cookie', cookie + ";" + buvid3); + const charset = '0123456789ABCDEF'.split(''); + const buvid3 = 'x'.repeat(8).split('').map(() => getRandomOne(charset)).join('') + + '-' + + 'x'.repeat(4).split('').map(() => getRandomOne(charset)).join('') + + '-' + + 'x'.repeat(4).split('').map(() => getRandomOne(charset)).join('') + + '-' + + 'x'.repeat(4).split('').map(() => getRandomOne(charset)).join('') + + '-' + + 'x'.repeat(17).split('').map(() => getRandomOne(charset)).join('') + + 'infoc'; + this.set('cookie', cookie + ';' + buvid3); } else { this.set('cookie', cookie); } @@ -58,13 +58,13 @@ let global_var = { this.set('Lottery', LotteryOrder .map(it => LotteryOrderMap.get(it)) - .filter(it => typeof it === "string") + .filter(it => typeof it === 'string') .map(lottery_option => config[lottery_option].map(it => [lottery_option, it])) .flat() ); } await createDir('dyids'); - await createFile(dyids_dir, num < 2 ? 'dyid.txt' : `dyid${num}.txt`, '', 'a') + await createFile(dyids_dir, num < 2 ? 'dyid.txt' : `dyid${num}.txt`, '', 'a'); } }; diff --git a/lib/helper/d_storage.js b/lib/helper/d_storage.js index c57710a44f..3f308ed3b6 100644 --- a/lib/helper/d_storage.js +++ b/lib/helper/d_storage.js @@ -1,4 +1,4 @@ -const { log, readDyidFile, writeDyidFile, dyid_length } = require("../utils"); +const { log, readDyidFile, writeDyidFile } = require('../utils'); const d_storage = { /** @@ -6,24 +6,35 @@ const d_storage = { * @param {string} dyid * @returns {Promise} */ - searchDyid: (dyid) => { - return new Promise((resolve) => { - const Rdyid = new RegExp(dyid); - const rs = readDyidFile(Number(process.env.NUMBER)); - let status = false; - rs.on('data', chunk => { - if (Rdyid.test(chunk)) { - status = true - } - }) - rs.on('end', () => { - resolve(status) - }) - rs.on('error', err => { - log.error('搜索dyid', err) - resolve(status) - }) - }) + searchDyid: async (dyid) => { + let buffer = ''; + let found = false; + + const stream = readDyidFile(Number(process.env.NUMBER)); + + for await (const chunk of stream) { + buffer += chunk; + + while ((buffer.indexOf(dyid)) !== -1) { + found = true; + stream.destroy(); + return found; + } + + const lastCommaIdx = buffer.lastIndexOf(','); + if (lastCommaIdx !== -1) { + buffer = buffer.slice(lastCommaIdx); + } + } + + if (!found && buffer.length > 0) { + if (buffer.includes(dyid)) { + found = true; + } + } + + stream.destroy(); + return found; }, /** * 更新dyid @@ -31,22 +42,19 @@ const d_storage = { */ updateDyid: (dyid) => { log.info('更新dyid', `写入${dyid}`); - if (dyid.length !== dyid_length) { - log.error('更新dyid', `dyid(${dyid})长度不为18 若出现此问题请即时通知开发者`) - } return new Promise((resolve) => { const ws = writeDyidFile(Number(process.env.NUMBER)); ws.write(dyid + ',', () => { ws.destroy(); - resolve() - }) + resolve(); + }); ws.on('error', err => { - log.error('更新dyid', err) - resolve() - }) + log.error('更新dyid', err); + resolve(); + }); }); } -} +}; module.exports = d_storage; \ No newline at end of file diff --git a/lib/helper/event_bus.js b/lib/helper/event_bus.js index 69cf334d0e..af4e8164cc 100644 --- a/lib/helper/event_bus.js +++ b/lib/helper/event_bus.js @@ -15,11 +15,11 @@ const event_bus = { }, flush() { this.event_list.forEach(event => { - this.ee.removeAllListeners(event) + this.ee.removeAllListeners(event); }); this.event_list = []; } -} +}; module.exports = event_bus; diff --git a/lib/helper/notify.js b/lib/helper/notify.js index 23bd753ddf..bd268d60f4 100644 --- a/lib/helper/notify.js +++ b/lib/helper/notify.js @@ -1,6 +1,6 @@ -const { log } = require("../utils"); -const { send } = require("../net/http"); -const { createTransport } = require("nodemailer"); +const { log } = require('../utils'); +const { send } = require('../net/http'); +const { createTransport } = require('nodemailer'); // =======================================微信server酱通知设置区域=========================================== //此处填你申请的SCKEY. @@ -70,6 +70,7 @@ let PUSH_PLUS_TOKEN = ''; let PUSH_PLUS_USER = ''; // ===========================================QMSG=========================================== +let QMSG_SOCKET = ''; let QMSG_KEY = ''; let QMSG_QQ = ''; @@ -87,6 +88,14 @@ let GOTIFY_URL = ''; // 此处填你想推送的Application的Token(不包含推送时额外添加的前缀 Bearer ) let GOTIFY_APPKEY = ''; +// =======================================飞书机器人通知设置区域=========================================== +// 此处填你飞书机器人的 webhook(详见文档 https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot) +// 注:此处设置github action用户填写到Settings-Secrets里面(Name输入FS_BOT_WEBHOOK) +let FS_BOT_WEBHOOK = ''; +// 签名密钥(如果在飞书机器人安全设置里开启了“签名校验”) +// 注:此处设置github action用户填写到Settings-Secrets里面(Name输入FS_BOT_SECRET) +let FS_BOT_SECRET = ''; + //==========================云端环境变量的判断与接收========================= if (process.env.SCKEY) { SCKEY = process.env.SCKEY; @@ -107,24 +116,24 @@ if (process.env.QQ_MODE) { if (process.env.BARK_PUSH) { if (process.env.BARK_PUSH.indexOf('https') > -1 || process.env.BARK_PUSH.indexOf('http') > -1) { //兼容BARK自建用户 - BARK_PUSH = process.env.BARK_PUSH + BARK_PUSH = process.env.BARK_PUSH; } else { - BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}` + BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}`; } if (process.env.BARK_SOUND) { - BARK_SOUND = process.env.BARK_SOUND + BARK_SOUND = process.env.BARK_SOUND; } } else { if (BARK_PUSH && BARK_PUSH.indexOf('https') === -1 && BARK_PUSH.indexOf('http') === -1) { //兼容BARK本地用户只填写设备码的情况 - BARK_PUSH = `https://api.day.app/${BARK_PUSH}` + BARK_PUSH = `https://api.day.app/${BARK_PUSH}`; } } if (process.env.PUSHDEER_URL) { - PUSHDEER_URL = process.env.PUSHDEER_URL + PUSHDEER_URL = process.env.PUSHDEER_URL; } else { - PUSHDEER_URL = "https://api2.pushdeer.com/message/push"; + PUSHDEER_URL = 'https://api2.pushdeer.com/message/push'; } if (process.env.PUSHDEER_PUSHKEY) { @@ -163,7 +172,7 @@ if (process.env.QYWX_KEY) { } if (process.env.IGOT_PUSH_KEY) { - IGOT_PUSH_KEY = process.env.IGOT_PUSH_KEY + IGOT_PUSH_KEY = process.env.IGOT_PUSH_KEY; } if (process.env.PUSH_PLUS_TOKEN) { @@ -173,6 +182,12 @@ if (process.env.PUSH_PLUS_USER) { PUSH_PLUS_USER = process.env.PUSH_PLUS_USER; } +if (process.env.QMSG_SOCKET) { + QMSG_SOCKET = process.env.QMSG_SOCKET; +} else { + QMSG_SOCKET = 'qmsg.zendee.cn'; +} + if (process.env.QMSG_KEY) { QMSG_KEY = process.env.QMSG_KEY; } @@ -200,15 +215,23 @@ if (process.env.SMTP_TO_USER) { if (process.env.GOTIFY_URL) { GOTIFY_URL = process.env.GOTIFY_URL; if (process.env.GOTIFY_APPKEY) { - GOTIFY_APPKEY = process.env.GOTIFY_APPKEY + GOTIFY_APPKEY = process.env.GOTIFY_APPKEY; } } +if (process.env.FS_BOT_WEBHOOK) { + FS_BOT_WEBHOOK = process.env.FS_BOT_WEBHOOK; +} + +if (process.env.FS_BOT_SECRET) { + FS_BOT_SECRET = process.env.FS_BOT_SECRET; +} + //==========================云端环境变量的判断与接收========================= async function sendNotify(text, desp, params = {}) { if (process.env.NOTE) { - desp = `帐号备注: ${process.env.NOTE}\n${desp}` + desp = `帐号备注: ${process.env.NOTE}\n${desp}`; } //提供多种通知方式 await Promise.all([ @@ -239,8 +262,10 @@ async function sendNotify(text, desp, params = {}) { //电子邮件 email(text, desp), // Gotify - gotifyNotify(text, desp) - ]) + gotifyNotify(text, desp), + // 飞书机器人 + feishuNotify(text, desp) + ]); } function serverNotify(text, desp) { @@ -263,29 +288,29 @@ function serverNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.errno === 0) { - log.info('发送通知', 'server酱发送通知消息成功') + log.info('发送通知', 'server酱发送通知消息成功'); } else if (data.errno === 1024) { // 一分钟内发送相同的内容会触发 - log.error('发送通知', `server酱发送通知消息异常: ${data.errmsg}`) + log.error('发送通知', `server酱发送通知消息异常: ${data.errmsg}`); } else { - log.error('发送通知', `server酱发送通知消息异常\n${JSON.stringify(data)}`) + log.error('发送通知', `server酱发送通知消息异常\n${JSON.stringify(data)}`); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { - log.error('发送通知', 'server酱 发送通知调用API失败!!' + err) - resolve() + log.error('发送通知', 'server酱 发送通知调用API失败!!' + err); + resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供server酱的SCKEY,取消微信推送消息通知'); - resolve() + resolve(); } - }) + }); } function serverNotifyTurbo(text, desp) { @@ -308,26 +333,26 @@ function serverNotifyTurbo(text, desp) { try { const data = JSON.parse(res.body); if (data.code === 0) { - log.info('发送通知', 'server酱(Turbo版)发送通知消息成功') + log.info('发送通知', 'server酱(Turbo版)发送通知消息成功'); } else { - log.error('发送通知', `server酱(Turbo版)发送通知消息异常\n${JSON.stringify(data)}`) + log.error('发送通知', `server酱(Turbo版)发送通知消息异常\n${JSON.stringify(data)}`); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { - log.error('发送通知', 'server酱(Turbo版) 发送通知调用API失败!!' + err) - resolve() + log.error('发送通知', 'server酱(Turbo版) 发送通知调用API失败!!' + err); + resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供server酱(Turbo版)的SCKEY,取消微信推送消息通知'); - resolve() + resolve(); } - }) + }); } function coolPush(text, desp) { @@ -335,18 +360,18 @@ function coolPush(text, desp) { if (QQ_SKEY) { let pushMode = function (t) { switch (t) { - case "send": - return "个人"; - case "group": - return "QQ群"; - case "wx": - return "微信"; - case "ww": - return "企业微信"; + case 'send': + return '个人'; + case 'group': + return 'QQ群'; + case 'wx': + return '微信'; + case 'ww': + return '企业微信'; default: - return "未知方式" + return '未知方式'; } - } + }; send({ method: 'POST', url: `https://push.xuthus.cc/${QQ_MODE}/${QQ_SKEY}`, @@ -356,34 +381,34 @@ function coolPush(text, desp) { }, headers: { accept: 'application/json, text/plain, */*', - "content-type": "text/plain" + 'content-type': 'text/plain' }, success: res => { try { const data = JSON.parse(res.body); if (data.code === 200) { - log.info('发送通知', `酷推发送${pushMode(QQ_MODE)}通知消息成功`) + log.info('发送通知', `酷推发送${pushMode(QQ_MODE)}通知消息成功`); } else if (data.code === 400) { - log.error('发送通知', `QQ酷推(Cool Push)发送${pushMode(QQ_MODE)}推送失败:${data}`) + log.error('发送通知', `QQ酷推(Cool Push)发送${pushMode(QQ_MODE)}推送失败:${data}`); } else { log.error('发送通知', `酷推推送异常: ${data.msg}`); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { - log.error('发送通知', `酷推 发送${pushMode(QQ_MODE)}通知调用API失败!!${err}`) - resolve() + log.error('发送通知', `酷推 发送${pushMode(QQ_MODE)}通知调用API失败!!${err}`); + resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供酷推的SKEY,取消QQ推送消息通知'); - resolve() + resolve(); } - }) + }); } function barkNotify(text, desp, params = {}) { @@ -406,26 +431,26 @@ function barkNotify(text, desp, params = {}) { try { const data = JSON.parse(res.body); if (data.code === 200) { - log.info('发送通知', 'Bark APP发送通知消息成功') + log.info('发送通知', 'Bark APP发送通知消息成功'); } else { log.error('发送通知', `${data.message}`); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { log.error('发送通知', 'Bark APP发送通知调用API失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供Bark的APP推送BARK_PUSH,取消Bark推送消息通知'); - resolve() + resolve(); } - }) + }); } function pushdeerNotify(text, desp) { @@ -438,7 +463,7 @@ function pushdeerNotify(text, desp) { pushkey: PUSHDEER_PUSHKEY, text, desp, - type: "markdown" + type: 'markdown' }, config: { retry: false @@ -447,26 +472,26 @@ function pushdeerNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.code === 0) { - log.info('发送通知', 'Pushdeer推送发送通知消息成功') + log.info('发送通知', 'Pushdeer推送发送通知消息成功'); } else { - log.error('发送通知', `Pushdeer推送发送通知消息异常\n${JSON.stringify(data)}`) + log.error('发送通知', `Pushdeer推送发送通知消息异常\n${JSON.stringify(data)}`); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { - log.error('发送通知', 'Pushdeer推送发送通知调用API失败!!' + err) - resolve() + log.error('发送通知', 'Pushdeer推送发送通知调用API失败!!' + err); + resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供Pushdeer推送所需的PUSHDEER_URL 和 PUSHDEER_PUSHKEY, 取消Pushdeer推送消息通知'); - resolve() + resolve(); } - }) + }); } function tgBotNotify(text, desp) { @@ -492,35 +517,35 @@ function tgBotNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.ok) { - log.info('发送通知', 'Telegram发送通知消息完成。') + log.info('发送通知', 'Telegram发送通知消息完成。'); } else if (data.error_code === 400) { - log.error('发送通知', '请主动给bot发送一条消息并检查接收用户ID是否正确。') + log.error('发送通知', '请主动给bot发送一条消息并检查接收用户ID是否正确。'); } else if (data.error_code === 401) { - log.error('发送通知', 'Telegram bot token 填写错误。') + log.error('发送通知', 'Telegram bot token 填写错误。'); } } catch (error) { log.error('发送通知', error); } finally { - resolve() + resolve(); } }, failure: err => { - log.error('发送通知', 'telegram发送通知消息失败!!' + err) - resolve() + log.error('发送通知', 'telegram发送通知消息失败!!' + err); + resolve(); } - } + }; if (TG_PROXY_HOST && TG_PROXY_PORT) { options.proxy = { - url: "http://" + TG_PROXY_HOST + ":" + TG_PROXY_PORT, + url: 'http://' + TG_PROXY_HOST + ':' + TG_PROXY_PORT, auth_headers: [] - } + }; } - send(options) + send(options); } else { log.debug('发送通知', '您未提供telegram机器人推送所需的TG_BOT_TOKEN和TG_USER_ID,取消telegram推送消息通知'); - resolve() + resolve(); } - }) + }); } function ddBotNotify(text, desp) { @@ -533,7 +558,7 @@ function ddBotNotify(text, desp) { const result = encodeURIComponent(hmac.digest().toString('base64')); send({ method: 'POST', - url: `https://oapi.dingtalk.com/robot/send`, + url: 'https://oapi.dingtalk.com/robot/send', query: { access_token: DD_BOT_TOKEN, timestamp: dateNow, @@ -543,7 +568,7 @@ function ddBotNotify(text, desp) { retry: false }, contents: { - msgtype: "text", + msgtype: 'text', text: { content: `${text}\n\n${desp}` } @@ -556,9 +581,9 @@ function ddBotNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.errcode === 0) { - log.info('发送通知', '钉钉发送通知消息完成。') + log.info('发送通知', '钉钉发送通知消息完成。'); } else { - log.error('发送通知', `${data.errmsg}`) + log.error('发送通知', `${data.errmsg}`); } } catch (e) { log.error('发送通知', e); @@ -567,14 +592,14 @@ function ddBotNotify(text, desp) { } }, failure: err => { - log.error('发送通知', '钉钉发送通知消息失败!!' + err) - resolve() + log.error('发送通知', '钉钉发送通知消息失败!!' + err); + resolve(); } - }) + }); } else if (DD_BOT_TOKEN) { send({ method: 'POST', - url: `https://oapi.dingtalk.com/robot/send`, + url: 'https://oapi.dingtalk.com/robot/send', query: { access_token: DD_BOT_TOKEN }, @@ -582,7 +607,7 @@ function ddBotNotify(text, desp) { retry: false }, contents: { - msgtype: "text", + msgtype: 'text', text: { content: `${text}\n\n${desp}` } @@ -595,9 +620,9 @@ function ddBotNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.errcode === 0) { - log.info('发送通知', '钉钉发送通知消息完成。') + log.info('发送通知', '钉钉发送通知消息完成。'); } else { - log.error('发送通知', `${data.errmsg}`) + log.error('发送通知', `${data.errmsg}`); } } catch (e) { log.error('发送通知', e); @@ -609,21 +634,22 @@ function ddBotNotify(text, desp) { log.error('发送通知', '钉钉发送通知消息失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供钉钉机器人推送所需的DD_BOT_TOKEN或者DD_BOT_SECRET,取消钉钉推送消息通知'); - resolve() + resolve(); } - }) + }); } function qywxAmNotify(text, desp) { return new Promise(resolve => { + desp = desp.replace(/\n/g, '
'); if (QYWX_AM) { const QYWX_AM_AY = QYWX_AM.split(','); send({ method: 'POST', - url: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`, + url: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken', contents: { corpid: `${QYWX_AM_AY[0]}`, corpsecret: `${QYWX_AM_AY[1]}`, @@ -646,9 +672,15 @@ function qywxAmNotify(text, desp) { touser: `${QYWX_AM_AY[2]}`, agentid: `${QYWX_AM_AY[3]}`, safe: '0', - msgtype: 'text', - text: { - content: `${text}\n\n${desp}`, + msgtype: 'mpnews', + mpnews: { + articles: [ + { + title: `${text}`, + thumb_media_id: `${QYWX_AM_AY[4]}`, + content: `${desp}`, + } + ] }, }, config: { @@ -676,7 +708,7 @@ function qywxAmNotify(text, desp) { log.error('发送通知', '企业微信应用发送通知消息失败!!' + err); resolve(); } - }) + }); } catch (e) { log.error('发送通知', e); } finally { @@ -687,7 +719,7 @@ function qywxAmNotify(text, desp) { log.error('发送通知', '企业微信应用发送通知消息失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供企业微信应用所需的QYWX_AM,取消企业微信应用推送消息通知'); resolve(); @@ -732,7 +764,7 @@ function qywxBotNotify(text, desp) { log.error('发送通知', '企业微信发送通知消息失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供企业微信机器人推送所需的QYWX_KEY,取消企业微信推送消息通知'); resolve(); @@ -744,11 +776,11 @@ function iGotNotify(text, desp, params = {}) { return new Promise(resolve => { if (IGOT_PUSH_KEY) { // 校验传入的IGOT_PUSH_KEY是否有效 - const IGOT_PUSH_KEY_REGX = new RegExp("^[a-zA-Z0-9]{24}$") + const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$'); if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) { - log.error('发送通知', '您所提供的IGOT_PUSH_KEY无效') - resolve() - return + log.error('发送通知', '您所提供的IGOT_PUSH_KEY无效'); + resolve(); + return; } send({ method: 'POST', @@ -769,9 +801,9 @@ function iGotNotify(text, desp, params = {}) { try { const data = JSON.parse(res.body); if (data.ret === 0) { - log.info('发送通知', 'iGot发送通知消息成功') + log.info('发送通知', 'iGot发送通知消息成功'); } else { - log.error('发送通知', `iGot发送通知消息失败:${data.errMsg}`) + log.error('发送通知', `iGot发送通知消息失败:${data.errMsg}`); } } catch (e) { log.error('发送通知', e); @@ -780,15 +812,15 @@ function iGotNotify(text, desp, params = {}) { } }, failure: err => { - log.error('发送通知', 'iGot 发送通知调用API失败!!' + err) + log.error('发送通知', 'iGot 发送通知调用API失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供iGot的推送IGOT_PUSH_KEY,取消iGot推送消息通知'); - resolve() + resolve(); } - }) + }); } function pushPlusNotify(text, desp) { @@ -814,9 +846,9 @@ function pushPlusNotify(text, desp) { try { const data = JSON.parse(res.body); if (data.code === 200) { - log.info('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息完成。`) + log.info('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息完成。`); } else { - log.error('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败:${data.msg}`) + log.error('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败:${data.msg}`); } } catch (e) { log.error('发送通知', e); @@ -825,15 +857,15 @@ function pushPlusNotify(text, desp) { } }, failure: err => { - log.error('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败!!${err}`) + log.error('发送通知', `push+发送${PUSH_PLUS_USER ? '一对多' : '一对一'}通知消息失败!!${err}`); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供push+推送所需的PUSH_PLUS_TOKEN,取消push+推送消息通知'); - resolve() + resolve(); } - }) + }); } function gotifyNotify(text, desp) { @@ -856,19 +888,78 @@ function gotifyNotify(text, desp) { }, success: () => { // HTTP 响应码 200 就说明成功了 - log.info('发送通知', 'Gotify 发送通知消息成功') + log.info('发送通知', 'Gotify 发送通知消息成功'); resolve(); }, failure: err => { - log.error('发送通知', 'Gotify 发送通知调用API失败!!' + err) + log.error('发送通知', 'Gotify 发送通知调用API失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供Gotify推送所需的GOTIFY_APPKEY,取消Gotify推送消息通知'); - resolve() + resolve(); + } + }); +} + +function feishuNotify(text, desp) { + return new Promise(resolve => { + if (FS_BOT_WEBHOOK) { + const payload = { + msg_type: 'text', + content: { + text: `${text}\n\n${desp}` + } + }; + + if (FS_BOT_SECRET) { + const crypto = require('crypto'); + const timestamp = Math.floor(Date.now() / 1000); + const signStr = `${timestamp}\n${FS_BOT_SECRET}`; + const sign = crypto + .createHmac('sha256', FS_BOT_SECRET) + .update(signStr) + .digest('base64'); + payload.timestamp = `${timestamp}`; + payload.sign = sign; + } + + send({ + method: 'POST', + url: FS_BOT_WEBHOOK, + contents: payload, + config: { + retry: false + }, + headers: { + accept: 'application/json, text/plain, */*', + 'content-type': 'application/json', + }, + success: res => { + try { + const data = JSON.parse(res.body); + if (data.code === 0) { + log.info('发送通知', '飞书机器人发送通知消息完成。'); + } else { + log.error('发送通知', `${data.msg || '飞书机器人发送通知异常'}`); + } + } catch (e) { + log.error('发送通知', e); + } finally { + resolve(); + } + }, + failure: err => { + log.error('发送通知', '飞书机器人发送通知消息失败!!' + err); + resolve(); + } + }); + } else { + log.debug('发送通知', '您未提供飞书机器人推送所需的FS_BOT_WEBHOOK,取消飞书机器人推送消息通知'); + resolve(); } - }) + }); } async function qmsg(text, desp) { @@ -876,9 +967,9 @@ async function qmsg(text, desp) { if (QMSG_KEY) { send({ method: 'POST', - url: 'https://qmsg.zendee.cn/send/' + QMSG_KEY, + url: `https://${QMSG_SOCKET}/send/${QMSG_KEY}`, contents: { - msg: text + "\n\n" + desp, + msg: text + '\n\n' + desp, qq: QMSG_QQ }, config: { @@ -892,9 +983,9 @@ async function qmsg(text, desp) { try { const data = JSON.parse(res.body); if (data.code === 200) { - log.info('发送通知', `qmsg发送通知消息完成。`) + log.info('发送通知', 'qmsg发送通知消息完成。'); } else { - log.error('发送通知', `qmsg通知消息失败: ${data.reason}`) + log.error('发送通知', `qmsg通知消息失败: ${data.reason}`); } } catch (e) { log.error('发送通知', e); @@ -903,15 +994,15 @@ async function qmsg(text, desp) { } }, failure: err => { - log.error('发送通知', `qmsg通知消息失败!!` + err) + log.error('发送通知', 'qmsg通知消息失败!!' + err); resolve(); } - }) + }); } else { log.debug('发送通知', '您未提供qmsg推送所需的QMSG_KEY,取消qmsg推送消息通知'); - resolve() + resolve(); } - }) + }); } async function email(text, desp) { @@ -929,10 +1020,10 @@ async function email(text, desp) { to: SMTP_TO_USER, subject: text, text: desp, - }) + }); } catch (e) { log.error('发送通知', `email发送失败 原因: ${e.message}`); - return + return; } log.info('发送通知', 'email发送成功'); } else { @@ -940,4 +1031,4 @@ async function email(text, desp) { } } -module.exports = { sendNotify } +module.exports = { sendNotify }; diff --git a/lib/helper/randomDynamic.js b/lib/helper/randomDynamic.js index 2a93bea324..23811aabbd 100644 --- a/lib/helper/randomDynamic.js +++ b/lib/helper/randomDynamic.js @@ -1,6 +1,6 @@ -const config = require("../data/config"); -const bili = require("../net/bili"); -const utils = require("../utils"); +const config = require('../data/config'); +const bili = require('../net/bili'); +const utils = require('../utils'); /** * 随机动态 @@ -8,22 +8,22 @@ const utils = require("../utils"); * @returns */ async function randomDynamic(num) { - let dynamics = [] + let dynamics = []; const { create_dy_type, dy_contents, random_dynamic_wait } = config, hasShareVideo = create_dy_type === -1 || create_dy_type === 1, hasRandomCreate = create_dy_type === -1 || create_dy_type === 0 || typeof create_dy_type === 'undefined'; if (hasShareVideo) { - dynamics = await bili.getTopRcmd() + dynamics = await bili.getTopRcmd(); for (let index = 0; dynamics.length < num; index++) { - dynamics.push(...await bili.getTopRcmd()) + dynamics.push(...await bili.getTopRcmd()); } } if (hasRandomCreate) { for (let index = 0; index < num; index++) { - dynamics.push(utils.getRandomOne(dy_contents)) + dynamics.push(utils.getRandomOne(dy_contents)); } } @@ -31,13 +31,13 @@ async function randomDynamic(num) { utils.shuffle(dynamics).slice(0, num), async (dynamic) => { await utils.delay(random_dynamic_wait); - if (dynamic instanceof Array && dynamic.length === 2 && typeof dynamic[0] === "number") { - await bili.shareVideo(...dynamic) + if (dynamic instanceof Array && dynamic.length === 2 && typeof dynamic[0] === 'number') { + await bili.shareVideo(...dynamic); } else { - await bili.createDynamic(dynamic) + await bili.createDynamic(dynamic); } } - ) + ); } -module.exports = { randomDynamic } \ No newline at end of file +module.exports = { randomDynamic }; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index f345c4fd9b..e672b36ffc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,10 +2,10 @@ const { login } = require('./login'); const { isMe } = require('./check'); const { clear } = require('./clear'); const { start } = require('./lottery'); -const { account } = require("./account"); -const global_var = require('./data/global_var') -const bili = require('./net/bili') -const { log } = require('./utils') +const { account } = require('./account'); +const global_var = require('./data/global_var'); +const bili = require('./net/bili'); +const { log } = require('./utils'); const { sendNotify } = require('./helper/notify'); /** @@ -13,16 +13,16 @@ const { sendNotify } = require('./helper/notify'); * @param {string} num */ async function checkCookie(num) { - const My_UID = global_var.get("myUID"); + const My_UID = global_var.get('myUID'); if (await bili.getMyinfo()) { log.info('Cookie有效性检测', `成功登录 UID:${My_UID}`); return true; } else { log.error('Cookie有效性检测', `登录失败 COOKIE${num} 已失效 UID:${My_UID}`); - await sendNotify('动态抽奖出错-登录失败', `COOKIE${num} 已失效 UID:${My_UID}`) + await sendNotify('动态抽奖出错-登录失败', `COOKIE${num} 已失效 UID:${My_UID}`); return false; } } -module.exports = { login, start, isMe, clear, checkCookie, account } \ No newline at end of file +module.exports = { login, start, isMe, clear, checkCookie, account }; \ No newline at end of file diff --git a/lib/login.js b/lib/login.js index b55b33102b..784ebd7f18 100644 --- a/lib/login.js +++ b/lib/login.js @@ -1,5 +1,5 @@ const { readFileSync, writeFileSync } = require('fs'); -const { log, env_file } = require("./utils"); +const { log, env_file } = require('./utils'); /** * 扫码登陆 @@ -10,17 +10,17 @@ async function login(num) { const { pcLogin } = require('@catlair/blogin'); const loginInfo = await pcLogin(); if (!loginInfo) { - log.error("扫码登陆", "失败/取消"); + log.error('扫码登陆', '失败/取消'); return; } - log.info("扫码登陆", "登录成功"); + log.info('扫码登陆', '登录成功'); JSON.stringify(loginInfo, null, 2); const uid = `${loginInfo.mid}`; const cookie = `${loginInfo.cookie}`; - log.info("账号UID", uid); - log.info("Cookie", cookie); + log.info('账号UID', uid); + log.info('Cookie', cookie); if (replaceCookie(env_file, uid, cookie)) { - log.info("扫码登陆", `账号${num}已进行cookie自动更新,如未能生效请手动复制在env.js内替换。路径:${env_file}`); + log.info('扫码登陆', `账号${num}已进行cookie自动更新,如未能生效请手动复制在env.js内替换。路径:${env_file}`); } } catch (error) { log.error(error); @@ -41,7 +41,7 @@ function replaceCookie(filePath, uid, oldCK) { const newCK = content.replaceAll(reg, substring => { let quote = substring.at(0) || ''; /['"]/.test(quote) || (quote = ''); - const quote2 = oldCK.includes("'") + const quote2 = oldCK.includes('\'') ? '"' : substring.match(/^['"]?COOKIE['"]?:\s?(['"])/)?.[1] || '"'; return `${quote}COOKIE${quote}: ${quote2}${oldCK}${quote2}`; @@ -50,9 +50,9 @@ function replaceCookie(filePath, uid, oldCK) { writeFileSync(filePath, newCK); return true; } catch (error) { - log.error("扫码登陆", error); + log.error('扫码登陆', error); } return false; } -module.exports = { login } \ No newline at end of file +module.exports = { login }; \ No newline at end of file diff --git a/lib/lottery.js b/lib/lottery.js index b6f5d9cf5f..0d11c1ab16 100644 --- a/lib/lottery.js +++ b/lib/lottery.js @@ -15,11 +15,11 @@ async function createRandomDynamic(num) { { allModifyDynamicResArray } = (await utils.retryfn( 3, [null], - () => Searcher.checkAllDynamic(global_var.get("myUID"), 1) + () => Searcher.checkAllDynamic(global_var.get('myUID'), 1) )) || { allModifyDynamicResArray: [] }, { type, origin_type } = allModifyDynamicResArray[0] || {}; if (type === 1 && origin_type !== 8) { - await randomDynamic(num) + await randomDynamic(num); } else { log.info('随机动态', '已有非抽奖动态故无需创建'); } @@ -36,16 +36,16 @@ function start(num) { let times = utils.counter(); /* 注册事件 */ event_bus.on('Turn_on_the_Monitor', async () => { - const lotterys = global_var.get("Lottery"); + const lotterys = global_var.get('Lottery'); if (lotterys.length === 0) { log.info('抽奖', '抽奖信息为空'); - event_bus.emit('Turn_off_the_Monitor', '抽奖信息为空') + event_bus.emit('Turn_off_the_Monitor', '抽奖信息为空'); return; } if (times.value() === lotterys.length) { log.info('抽奖', '所有动态转发完毕'); times.clear(); - event_bus.emit('Turn_off_the_Monitor', '目前无抽奖信息,过一会儿再来看看吧') + event_bus.emit('Turn_off_the_Monitor', '目前无抽奖信息,过一会儿再来看看吧'); return; } const lottery = lotterys[times.next()]; @@ -63,10 +63,10 @@ function start(num) { await createRandomDynamic(config.create_dy_num); event_bus.flush(); resolve(); - }) + }); event_bus.emit('Turn_on_the_Monitor'); }); } -module.exports = { start } \ No newline at end of file +module.exports = { start }; \ No newline at end of file diff --git a/lib/net/api.bili.js b/lib/net/api.bili.js index 59b1269da8..6b352c711f 100644 --- a/lib/net/api.bili.js +++ b/lib/net/api.bili.js @@ -5,14 +5,8 @@ module.exports = Object.freeze({ DYNAMIC_REPOST_SHARE: 'https://api.vc.bilibili.com/dynamic_repost/v1/dynamic_repost/share', DYNAMIC_SVR_CREATE_DRAW: 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create_draw', DYNAMIC_SVR_CREATE: 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V2: 'https://api.vc.bilibili.com/dynamic_svr/v2/dynamic_svr/get_dynamic_detail', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V3: 'https://api.vc.bilibili.com/dynamic_svr/v3/dynamic_svr/get_dynamic_detail', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V4: 'https://api.vc.bilibili.com/dynamic_svr/v4/dynamic_svr/get_dynamic_detail', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V5: 'https://api.vc.bilibili.com/dynamic_svr/v5/dynamic_svr/get_dynamic_detail', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V6: 'https://api.vc.bilibili.com/dynamic_svr/v6/dynamic_svr/get_dynamic_detail', - DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V7: 'https://api.vc.bilibili.com/dynamic_svr/v7/dynamic_svr/get_dynamic_detail', + DYNAMIC_SVR_GET_DYNAMIC_DETAIL: 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail', DYNAMIC_SVR_RM_DYNAMIC: 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/rm_dynamic', - DYNAMIC_SVR_SPACE_HISTORY: 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history', FEED_GET_ATTENTION_LIST: 'https://api.vc.bilibili.com/feed/v1/feed/get_attention_list', FEED_SETUSERFOLLOW: 'https://api.vc.bilibili.com/feed/v1/feed/SetUserFollow', FETCH_SESSION_MSGS: 'https://api.vc.bilibili.com/svr_sync/v1/svr_sync/fetch_session_msgs', @@ -35,12 +29,14 @@ module.exports = Object.freeze({ SHORTLINK: 'https://b23.tv/{{short_id}}', SPACE_MYINFO: 'https://api.bilibili.com/x/space/myinfo', TAG_INFO: 'https://api.bilibili.com/x/tag/info', - TOP_FEED_RCMD: "https://api.bilibili.com/x/web-interface/index/top/feed/rcmd", - TOP_RCMD: "https://api.bilibili.com/x/web-interface/index/top/rcmd", + TOP_FEED_RCMD: 'https://api.bilibili.com/x/web-interface/index/top/feed/rcmd', + TOP_RCMD: 'https://api.bilibili.com/x/web-interface/index/top/rcmd', TOPIC_SVR_TOPIC_HISTORY: 'https://api.vc.bilibili.com/topic_svr/v1/topic_svr/topic_history', TOPIC_SVR_TOPIC_NEW: 'https://api.vc.bilibili.com/topic_svr/v1/topic_svr/topic_new', - V2_REPLAY: "https://api.bilibili.com/x/v2/reply", + V2_REPLAY: 'https://api.bilibili.com/x/v2/reply', WEB_INTERFACE_CARD: 'https://api.bilibili.com/x/web-interface/card', - WEB_INTERFACE_NAV_STAT: "https://api.bilibili.com/x/web-interface/nav/stat", + WEB_INTERFACE_NAV_STAT: 'https://api.bilibili.com/x/web-interface/nav/stat', WEB_INTERFACE_SEARCH_TYPE: 'https://api.bilibili.com/x/web-interface/search/type', -}) \ No newline at end of file + X_POLYMER_WEB_DYNAMIC_V1_DETAIL: 'https://api.bilibili.com/x/polymer/web-dynamic/v1/detail', + X_POLYMER_WEB_DYNAMIC_V1_FEED_SPACE: 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space', +}); diff --git a/lib/net/bili.js b/lib/net/bili.js index 3982c57a58..7b7e7cf625 100644 --- a/lib/net/bili.js +++ b/lib/net/bili.js @@ -14,15 +14,15 @@ class Line { * @param {(responseText: string) => ResResult} [pub_handler] */ constructor(line_name, requests, pub_handler) { - this.line_name = line_name - this.requests = requests - this.valid_line = 0 - this.switch_times = 0 - if (pub_handler) this.pub_handler = pub_handler + this.line_name = line_name; + this.requests = requests; + this.valid_line = 0; + this.switch_times = 0; + if (pub_handler) this.pub_handler = pub_handler; /** * @type {ResResult} */ - this.res_result = [false, null, ''] + this.res_result = [false, null, '']; } /** @@ -30,13 +30,13 @@ class Line { * @returns {boolean} */ switchLine() { - const { valid_line, requests: { length }, switch_times } = this - this.valid_line = (valid_line + 1) % length - this.switch_times += 1 + const { valid_line, requests: { length }, switch_times } = this; + this.valid_line = (valid_line + 1) % length; + this.switch_times += 1; if (switch_times > length) { - return false + return false; } else { - return true + return true; } } @@ -45,8 +45,8 @@ class Line { * @param {number} line */ storeLine(line) { - this.valid_line = line - this.switch_times = 0 + this.valid_line = line; + this.switch_times = 0; } /** @@ -59,24 +59,24 @@ class Line { { line_name, requests, valid_line } = this, resp = await requests[valid_line](...args); if (typeof resp === 'string') { - this.res_result = this.pub_handler(resp) + this.res_result = this.pub_handler(resp); } else { - this.res_result = resp + this.res_result = resp; } - const [i_switch, value, msg] = this.res_result + const [i_switch, value, msg] = this.res_result; if (!i_switch) { - log.info(line_name, msg) - this.storeLine(valid_line) - return value + log.info(line_name, msg); + this.storeLine(valid_line); + return value; } if (this.switchLine()) { - log.warn(line_name, msg) - log.warn(line_name, `切换线路(${valid_line + 1}/${requests.length})`) - return await this.run() + log.warn(line_name, msg); + log.warn(line_name, `切换线路(${valid_line + 1}/${requests.length})`); + return await this.run(); } else { - log.error(line_name, msg) - log.error(line_name, '所有备用线路均连接失败') - return value + log.error(line_name, msg); + log.error(line_name, '所有备用线路均连接失败'); + return value; } } } @@ -93,15 +93,15 @@ function get({ url, config, contents, query }) { method: 'GET', config, headers: { - "accept": 'application/json, text/plain, */*', - "cookie": GlobalVar.get("cookie") + 'accept': 'application/json, text/plain, */*', + 'cookie': GlobalVar.get('cookie') }, query, contents, success: res => resolve(res.body), failure: err => resolve(err) - }) - }) + }); + }); } /** @@ -116,16 +116,16 @@ function post({ url, config, contents, query }) { method: 'POST', config, headers: { - "accept": 'application/json, text/plain, */*', - "content-type": 'application/x-www-form-urlencoded; charset=UTF-8', - "cookie": GlobalVar.get("cookie") + 'accept': 'application/json, text/plain, */*', + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'cookie': GlobalVar.get('cookie') }, query, contents, success: res => resolve(res.body), failure: err => resolve(err) - }) - }) + }); + }); } /** @@ -143,7 +143,7 @@ const bili_client = { }), res = strToJson(responseText); if (res.code === 0) { - GlobalVar.set("myUNAME", res.data.name) + GlobalVar.set('myUNAME', res.data.name); return res.data; } else { return null; @@ -212,7 +212,7 @@ const bili_client = { }), res = strToJson(responseText); if (res.code === 0) { - log.info('获取未读信息', `成功`); + log.info('获取未读信息', '成功'); return res.data; } else { log.error('获取未读信息', `失败\n${responseText}`); @@ -243,11 +243,11 @@ const bili_client = { source: it.item.source_content, uri: it.item.uri, timestamp: it.reply_time - } + }; }); } else { log.error('获取一页回复', '失败'); - return [] + return []; } }, /** @@ -292,7 +292,7 @@ const bili_client = { }); return { has_more, data }; } else if (res.code === 2) { - log.error('获取私信', `API抽风...请再次尝试`); + log.error('获取私信', 'API抽风...请再次尝试'); return { has_more: 0, data: [] }; } else { log.error('获取私信', `失败\n${responseText}`); @@ -316,16 +316,16 @@ const bili_client = { }), res = strToJson(responseText); if (res.code === 0) { - const msgs = res.data.messages + const msgs = res.data.messages; if (msgs instanceof Array) { - log.info('私信细节', `${talker_id}有${size}条未读私信`) - return msgs.map(it => it.content).join('\n') + log.info('私信细节', `${talker_id}有${size}条未读私信`); + return msgs.filter(it => ![8].includes(it.msg_source)).map(it => it.content).join('\n'); } else { - log.warn('私信细节', `${talker_id}无私信`) + log.warn('私信细节', `${talker_id}无私信`); } } - log.error('私信细节', `获取失败`) - return '' + log.error('私信细节', '获取失败'); + return ''; }, /** * 获取未读私信数量 @@ -363,8 +363,8 @@ const bili_client = { talker_id, session_type, ack_seqno: msg_seqno, - csrf_token: GlobalVar.get("csrf"), - csrf: GlobalVar.get("csrf") + csrf_token: GlobalVar.get('csrf'), + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -407,79 +407,48 @@ const bili_client = { redirect: false, } }).then(a => { - const dyid = (a.match(/[0-9]{18}/) || [])[0]; - log.info("短连接转换", `${short_id} -> ${dyid}`) - return dyid - }) + const dyid = (a.match(/[0-9]{18,}/) || [])[0]; + log.info('短连接转换', `${short_id} -> ${dyid}`); + return dyid; + }); }, - _getOneDynamicByDyid: new Line('获取一个动态的细节', [ - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V2, - config: { retry: false }, - query: { - dynamic_id - } - }), - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V3, - config: { retry: false }, - query: { - dynamic_id - } - }), - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V4, - config: { retry: false }, - query: { - dynamic_id - } - }), - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V5, - config: { retry: false }, - query: { - dynamic_id - } - }), - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V6, - config: { retry: false }, - query: { - dynamic_id - } - }), - (dynamic_id) => get({ - url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL_V7, - config: { retry: false }, - query: { - dynamic_id + _getOneDynamicByDyid: new Line( + '获取一个动态的细节', + [ + (id) => get({ + url: API.X_POLYMER_WEB_DYNAMIC_V1_DETAIL, + config: { retry: false }, + query: { + id, + features: 'itemOpusStyle' + } + }), + (dynamic_id) => get({ + url: API.DYNAMIC_SVR_GET_DYNAMIC_DETAIL, + config: { retry: false }, + query: { + dynamic_id + } + }), + ] + , responseText => { + const + res = strToJson(responseText), + { code, data } = res; + switch (code) { + case 0: + return [false, data, 'ok']; + default: + return [true, undefined, `获取动态数据出错:\n${responseText}`]; } }), - ], responseText => { - const - res = strToJson(responseText), - { code, data } = res, - { card } = data || {}; - switch (code) { - case 0: - if (card) { - return [false, card, `ok`]; - } else { - return [false, undefined, `动态不存在`]; - } - case 500207: - return [false, undefined, `该动态为包月充电专属可以给UP主充电后观看`]; - default: - return [true, undefined, `获取动态数据出错:\n${responseText}`] - } - }), /** * 获取一个动态的细节 * @param {string} dynamic_id * @return {Promise} 失败返回undefined */ async getOneDynamicByDyid(dynamic_id) { - return this._getOneDynamicByDyid.run(dynamic_id) + return this._getOneDynamicByDyid.run(dynamic_id); }, /** * 获取一组动态的信息 @@ -487,19 +456,19 @@ const bili_client = { * @param {string} offset_dynamic_id 此动态偏移量 初始为 0 * @returns {Promise} */ - getOneDynamicInfoByUID(host_uid, offset_dynamic_id) { + getOneDynamicInfoByUID(host_mid, offset) { /* 鉴别工作交由modifyDynamicRes完成 */ + /* 新版似乎没有 visitor_uid */ return get({ - url: API.DYNAMIC_SVR_SPACE_HISTORY, + url: API.X_POLYMER_WEB_DYNAMIC_V1_FEED_SPACE, query: { - visitor_uid: GlobalVar.get("myUID"), - host_uid, - offset_dynamic_id, + host_mid, + offset, }, config: { retry: false } - }) + }); }, /** * 通过tag名获取tag的id @@ -536,7 +505,7 @@ const bili_client = { query: { topic_id: tagid } - }) + }); }, /** * 获取tag下的最新动态 @@ -554,7 +523,7 @@ const bili_client = { config: { retry: false } - }) + }); }, /** * 搜索专栏 @@ -581,7 +550,7 @@ const bili_client = { return { pub_time: it.pub_time, id: it.id - } + }; }); } catch (error) { log.error('搜索专栏', '失败 原因:\n' + responseText); @@ -600,8 +569,11 @@ const bili_client = { */ getOneArticleByCv(cv) { return get({ - url: API.READ_CV.replace('{{cv}}', cv) - }) + url: API.READ_CV.replace('{{cv}}', cv), + config: { + redirect: true + } + }); }, /** * 获取粉丝数的所有有效方式 @@ -639,7 +611,7 @@ const bili_client = { * @returns {Promise} */ (uid) => get({ - url: "https://tenapi.cn/bilibilifo/", + url: 'https://tenapi.cn/bilibilifo/', query: { uid } @@ -649,7 +621,7 @@ const bili_client = { if (res.code === 0) { return [false, res.data.follower, 'ok']; } else { - return [true, -1, `出错 可能是访问过频繁\n${responseText}`] + return [true, -1, `出错 可能是访问过频繁\n${responseText}`]; } }), /** @@ -658,7 +630,7 @@ const bili_client = { * @returns {Promise} */ getUserInfo(uid) { - return this._getUserInfo.run(uid) + return this._getUserInfo.run(uid); }, /** * 获取开奖信息 @@ -704,7 +676,7 @@ const bili_client = { fid: uid, act: 1, re_src: 0, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), (uid) => post({ @@ -712,7 +684,7 @@ const bili_client = { contents: { type: 1, follow: uid, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), (uid) => post({ @@ -721,26 +693,26 @@ const bili_client = { fids: uid, act: 1, re_src: 1, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }) ], responseText => { const res = strToJson(responseText); switch (res.code) { case 0: - return [false, 0, '关注+1'] + return [false, 0, '关注+1']; case 22002: - return [false, 2, '您已被对方拉入黑名单'] + return [false, 2, '您已被对方拉入黑名单']; case 22003: - return [false, 3, '黑名单用户无法关注'] + return [false, 3, '黑名单用户无法关注']; case 22015: - return [false, 4, '账号异常'] + return [false, 4, '账号异常']; case 22009: - return [false, 5, '关注已达上限'] + return [false, 5, '关注已达上限']; case 22014: - return [false, 6, '已经关注用户,无法重复关注'] + return [false, 6, '已经关注用户,无法重复关注']; default: - return [true, 1, `未知错误\n${responseText}`] + return [true, 1, `未知错误\n${responseText}`]; } }), /** @@ -759,7 +731,7 @@ const bili_client = { * - 已经关注用户 6 */ autoAttention(uid) { - return this._autoAttention.run(uid) + return this._autoAttention.run(uid); }, /** * 移动分区 @@ -775,7 +747,7 @@ const bili_client = { contents: { fids: uid, tagids: tagid, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }); /* 重复移动code also equal 0 */ @@ -783,7 +755,7 @@ const bili_client = { log.info('移动分区', 'up主分区移动成功'); return 0; } else { - log.error('移动分区', `up主分区移动失败\n${responseText}`); + log.error('移动分区', `up主分区移动失败分区 可于设置处关闭移动分区\n${responseText}`); return 1; } }, @@ -803,7 +775,7 @@ const bili_client = { fid: uid, act: 2, re_src: 0, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -831,10 +803,10 @@ const bili_client = { responseText = await post({ url: API.DYNAMIC_LIKE_THUMB, contents: { - uid: GlobalVar.get("myUID"), + uid: GlobalVar.get('myUID'), dynamic_id: dyid, up: 1, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -873,11 +845,12 @@ const bili_client = { * - 该动态不能转发分享 2 * - 请求数据发生错误,请刷新或稍后重试 3 * - 操作太频繁了,请稍后重试 4 + * - 源动态禁止转发 5 */ async autoRelay(uid, dyid, msg = '转发动态', ctrl = '[]') { const len = msg.length; if (len > 233) { - msg = msg.slice(0, 233 - len) + msg = msg.slice(0, 233 - len); } const responseText = await post({ @@ -890,7 +863,7 @@ const bili_client = { dynamic_id: dyid, content: msg, ctrl, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -907,6 +880,9 @@ const bili_client = { case 1101008: log.warn('转发动态', '操作太频繁了,请稍后重试'); return 4; + case 4126117: + log.warn('转发动态', '源动态禁止转发'); + return 5; default: log.error('转发动态', `未知错误\n${responseText}`); return 1; @@ -922,9 +898,9 @@ const bili_client = { responseText = await post({ url: API.DYNAMIC_MIX_RESERVE_ATTACH_CARD_BUTTON, contents: { - cur_btn_status: "1", + cur_btn_status: '1', reserve_id, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -952,25 +928,25 @@ const bili_client = { async createDynamic(content) { let contents = { - csrf: GlobalVar.get("csrf"), + csrf: GlobalVar.get('csrf'), extension: '{"emoji_type":1,"from":{"emoji_type":1},"flag_cfg":{}}', }, url = ''; if (content instanceof Array) { - url = API.DYNAMIC_SVR_CREATE_DRAW + url = API.DYNAMIC_SVR_CREATE_DRAW; contents = { ...contents, biz: 3, category: 3, pictures: JSON.stringify(content) - } + }; } else { - url = API.DYNAMIC_SVR_CREATE + url = API.DYNAMIC_SVR_CREATE; contents = { ...contents, type: 4, content, - } + }; } const responseText = await post({ url, @@ -978,10 +954,10 @@ const bili_client = { }); if (/^{"code":0/.test(responseText)) { log.info('发布动态', `成功创建一条随机内容的动态\n${JSON.stringify(content)}\n`); - return false + return false; } else { log.error('发布动态', `发布动态失败\n${JSON.stringify(content)}\n${responseText}`); - return true + return true; } }, _getTopRcmd: new Line('获取推荐', [ @@ -996,7 +972,7 @@ const bili_client = { if (res.code === 0) { return [false, res.data.item.map(it => { return [it.owner.mid, it.id]; - }), "成功"]; + }), '成功']; } else { return [true, [], `获取推荐失败\n${responseText}`]; } @@ -1006,7 +982,7 @@ const bili_client = { * @returns {Promise>} */ async getTopRcmd() { - return this._getTopRcmd.run() + return this._getTopRcmd.run(); }, /** * 分享视频 @@ -1019,26 +995,26 @@ const bili_client = { responseText = await post({ url: API.DYNAMIC_REPOST_SHARE, contents: { - platform: "pc", + platform: 'pc', uid, type: 8, - content: "分享视频", + content: '分享视频', repost_code: 20000, rid: aid, - csrf_token: GlobalVar.get("csrf") + csrf_token: GlobalVar.get('csrf') } }), res = strToJson(responseText); switch (res.code) { case 0: log.info('转发视频', `成功转发视频(av${aid})`); - return false + return false; case 1101015: log.warn('转发视频', `该动态不能转发分享(av${aid})`); - return false + return false; default: log.error('转发视频', `转发失败\n${responseText}`); - return true + return true; } }, /** @@ -1051,7 +1027,7 @@ const bili_client = { url: API.DYNAMIC_SVR_RM_DYNAMIC, contents: { dynamic_id: dyid, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') }, config: { retry: false @@ -1099,7 +1075,7 @@ const bili_client = { type: type, message: msg, code, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), res = strToJson(responseText); @@ -1117,7 +1093,7 @@ const bili_client = { log.error('自动评论', '需要输入验证码'); return res.data.url; case 12035: - log.error('自动评论', `已被对方拉入黑名单`); + log.error('自动评论', '已被对方拉入黑名单'); return 5; case 12053: log.error('自动评论', '黑名单用户无法互动'); @@ -1171,13 +1147,13 @@ const bili_client = { */ async sendChatWithOcr(rid, msg, type) { let need_captcha = false; - let url = ""; + let url = ''; let status = 0; do { if (need_captcha) { const code = await ocr(url); if (code) { - log.info("验证码识别", `${url} -> ${code}`); + log.info('验证码识别', `${url} -> ${code}`); status = await bili_client.sendChat( rid, msg, @@ -1185,24 +1161,27 @@ const bili_client = { code ); if (status === 0) { - need_captcha = false + need_captcha = false; } else if (status === 12) { - need_captcha = true + need_captcha = true; } else { - need_captcha = false + need_captcha = false; } + } else { + log.error('验证码识别', '失败'); + break; } } else { url = await bili_client.sendChat( rid, msg, type - ) - if (typeof url === "string" - && hasEnv("ENABLE_CHAT_CAPTCHA_OCR")) { - need_captcha = true + ); + if (typeof url === 'string' + && hasEnv('ENABLE_CHAT_CAPTCHA_OCR')) { + need_captcha = true; } else { - status = url + status = url; } } } while (need_captcha); @@ -1226,14 +1205,14 @@ const bili_client = { res = strToJson(responseText); switch (res.code) { case 0: - log.info('查询评论', `成功`); + log.info('查询评论', '成功'); try { const upmid = res.data.upper.mid; return res.data.replies .filter(it => it.mid !== upmid) .map(it => [it.member.uname, it.content.message]); } catch (_) { - return [] + return []; } default: log.error('查询评论', `未知错误\n${responseText}`); @@ -1248,7 +1227,7 @@ const bili_client = { * @returns {Promise} */ async checkMyPartition(name) { - if (!name) name = '此处存放因抽奖临时关注的up' + if (!name) name = '此处存放因抽奖临时关注的up'; const responseText = await get({ url: API.RELATION_TAGS @@ -1288,7 +1267,7 @@ const bili_client = { url: API.RELATION_TAG_CREATE, contents: { tag: partition_name, - csrf: GlobalVar.get("csrf") + csrf: GlobalVar.get('csrf') } }), obj = strToJson(responseText); @@ -1313,7 +1292,7 @@ const bili_client = { responseText = await get({ url: API.RELATION_TAG, query: { - mid: GlobalVar.get("myUID"), + mid: GlobalVar.get('myUID'), tagid: tagid, pn: n, ps: 50 @@ -1335,4 +1314,4 @@ const bili_client = { }; -module.exports = bili_client +module.exports = bili_client; diff --git a/lib/net/http.js b/lib/net/http.js index 37e8e4e05b..0acd22c5fc 100644 --- a/lib/net/http.js +++ b/lib/net/http.js @@ -56,7 +56,7 @@ const DEFAULT_REDIRECT = false; /**错误尝试次数 */ const DEFAULT_RETRY_TIMES = 6; /**Google Chrome */ -const DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'; +const DEFAULT_UA = process.env.ACCOUNT_UA || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'; /**默认url编码 */ const DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=UTF-8'; /** @@ -69,7 +69,7 @@ function send(detail) { const { method, url, stream, config, proxy, query, contents, headers, success, failure } = detail; const { timeout, wait, retry, redirect, retry_times } = config; const thisURL = new URL(url) - , content = formatContents(headers["content-type"], contents) + , content = formatContents(headers['content-type'], contents) , request = (thisURL.protocol === 'https:') ? https_request : http_request; /** * @type {import("https").RequestOptions} @@ -82,10 +82,10 @@ function send(detail) { path: thisURL.pathname + thisURL.search + thisURL.hash, headers, }; - if (!headers["user-agent"]) options.headers["user-agent"] = DEFAULT_UA; + if (!headers['user-agent']) options.headers['user-agent'] = DEFAULT_UA; if (query) options.path += (thisURL.search ? '&' : '?') + stringify(query); if (content) { - if (!headers["content-type"]) options.headers["content-type"] = DEFAULT_CONTENT_TYPE; + if (!headers['content-type']) options.headers['content-type'] = DEFAULT_CONTENT_TYPE; options.headers['content-length'] = Buffer.byteLength(content, 'utf-8').toString(); } if (proxy) { @@ -93,28 +93,28 @@ function send(detail) { ? new HttpsProxyAgent(proxy.url) : new HttpProxyAgent(proxy.url); for (const ah of proxy.auth_headers) { - options.headers[ah[0]] = ah[1] + options.headers[ah[0]] = ah[1]; } - options.rejectUnauthorized = false + options.rejectUnauthorized = false; } const req = request(options, res => { let protodata = ''; const { statusCode, headers } = res; if (redirect && ~~(statusCode / 100) === 3) { try { - detail.url = new URL(headers.location).href + detail.url = new URL(headers.location).href; } catch (error) { - detail.url = new URL(thisURL.origin + headers.location).href + detail.url = new URL(thisURL.origin + headers.location).href; } console.log(`[重定向]状态码:${statusCode} location:${detail.url}`); - send(detail) - return + send(detail); + return; } if (stream) { - success({ headers: headers, resStream: res }) + success({ headers: headers, resStream: res }); } else { res.setEncoding(DEFAULT_DECODE) - .on('data', chunk => { protodata += chunk }) + .on('data', chunk => { protodata += chunk; }) .on('error', async err => { if (retry && retry_times) { console.log(`[不期待响应]原因:${err.message} 尝试重新请求中...`); @@ -122,18 +122,18 @@ function send(detail) { detail.config.retry_times = retry_times - 1; send(detail); } else { - failure(`[响应错误]${err.message} 响应数据:\n${protodata}`) + failure(`[响应错误]${err.message} 响应数据:\n${protodata}`); } }) .on('end', () => { if (statusCode < 400) { - success({ headers: headers, body: protodata }) + success({ headers: headers, body: protodata }); } else { - res.emit('error', new Error(`HTTP状态码: ${statusCode}`)) + res.emit('error', new Error(`HTTP状态码: ${statusCode}`)); } - }) + }); } - }) + }); req.on('error', async err => { if (retry && retry_times) { console.log(`[请求失败]原因:${err.message} 尝试重新连接中...`); @@ -143,9 +143,9 @@ function send(detail) { } else { failure(`[请求失败]: ${err.message}`); } - }) - req.on('timeout', () => { req.destroy(new Error('请求超时')) }) - req.end(content) + }); + req.on('timeout', () => { req.destroy(new Error('请求超时')); }); + req.end(content); } /** @@ -173,10 +173,10 @@ function setDefault(detail) { stream: false, proxy: '', headers: {}, - success: (res) => { console.log(res.headers) }, - failure: (err) => { console.log(err) }, + success: (res) => { console.log(res.headers); }, + failure: (err) => { console.log(err); }, ...detail - } + }; detail.config = { timeout: DEFAULT_TIMEOUT, wait: DEFAULT_WAIT, @@ -184,7 +184,7 @@ function setDefault(detail) { redirect: DEFAULT_REDIRECT, retry_times: DEFAULT_RETRY_TIMES, ...detail.config - } + }; return detail; } @@ -202,4 +202,4 @@ function delay(time) { } -module.exports = { send } \ No newline at end of file +module.exports = { send }; \ No newline at end of file diff --git a/lib/update.js b/lib/update.js index 72bd93dee3..2109c26308 100644 --- a/lib/update.js +++ b/lib/update.js @@ -1,6 +1,6 @@ -const { send } = require('./net/http') -const { strToJson, download, try_for_each } = require('./utils') -const { version, checkVersion, log } = require('./utils') +const { send } = require('./net/http'); +const { strToJson, download, try_for_each } = require('./utils'); +const { version, checkVersion, log } = require('./utils'); const platform = new Map([ @@ -25,7 +25,7 @@ function getLatestRelease(owner, repo) { send({ url: `https://api.github.com/repos/${owner}/${repo}/releases/latest`, headers: { - "accept": 'application/vnd.github.v3+json' + 'accept': 'application/vnd.github.v3+json' }, config: { retry: false @@ -33,15 +33,15 @@ function getLatestRelease(owner, repo) { success: ({ body }) => { const release = strToJson(body); if (release.tag_name) { - resolve(release) + resolve(release); } else { - reject(body) + reject(body); } }, failure: error => { - reject(error) + reject(error); } - }) + }); }); } @@ -51,7 +51,7 @@ function getLatestRelease(owner, repo) { */ function checkPlatform(releases) { return releases.includes(platform) - && releases.includes(arch) + && releases.includes(arch); } /** @@ -60,40 +60,43 @@ function checkPlatform(releases) { */ async function update(isDdownload) { try { - const { tag_name, assets, body: text } = await getLatestRelease('shanmiteko', 'LotteryAutoScript') + const { tag_name, assets, body: text } = await getLatestRelease('shanmiteko', 'LotteryAutoScript'); if (checkVersion(version) < checkVersion(tag_name)) { - const download_url = assets + /** + * @type {{browser_download_url:string, size:number}[]} + */ + const download_item = assets .filter(({ name }) => checkPlatform(name)) - .map(({ browser_download_url }) => browser_download_url) - if (download_url.length) { + .map(({ browser_download_url, size }) => { return { browser_download_url, size }; }); + if (download_item.length) { if (isDdownload) { - await try_for_each(download_url.entries(), async ([i, url]) => { - let proxy_url = "https://ghproxy.com/"; - proxy_url += url - log.warn('自动下载', `切换代理${proxy_url}`) - await download(proxy_url, `latest_version${i}.zip`) + await try_for_each(download_item.entries(), async ([i, { browser_download_url, size }]) => { + let proxy_url = 'https://ghfast.top/'; + proxy_url += browser_download_url; + log.warn('自动下载', `切换代理${proxy_url}`); + await download(proxy_url, `latest_version${i}.zip`, size) .catch(async err => { - log.error('自动下载', err) - proxy_url = url - log.warn('自动下载', `使用原始链接${proxy_url}`) - await download(proxy_url, `latest_version${i}.zip`) - }) - return false - }) - log.info('自动下载', '成功下载到当前目录') + log.error('自动下载', err); + proxy_url = browser_download_url; + log.warn('自动下载', `使用原始链接${proxy_url}`); + await download(proxy_url, `latest_version${i}.zip`, size); + }); + return false; + }); + log.info('自动下载', '成功下载到当前目录'); log.info('检查更新', '请手动解压替换可执行文件'); } - log.info('更新说明', '\n' + text + '\n') + log.notice('更新说明', '\n' + text + '\n'); } else { - throw `未找到能在此平台(${process.platform})-(${process.arch})上运行的版本,建议以源码运行` + throw `未找到能在此平台(${process.platform})-(${process.arch})上运行的版本,建议以源码运行`; } } else { - throw '当前已是最新版本' + throw '当前已是最新版本'; } } catch (error) { - log.warn('更新脚本', error) + log.warn('更新脚本', error); } } -module.exports = { update } \ No newline at end of file +module.exports = { update }; \ No newline at end of file diff --git a/lib/utils.js b/lib/utils.js index 7eb6ba7efd..6fc2b3636a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,7 @@ const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); -const { send } = require("./net/http"); +const { send } = require('./net/http'); const { version } = require('../package.json'); /** @@ -10,17 +10,15 @@ const { version } = require('../package.json'); const utils = { version, /**环境变量设置文件 */ - env_file: path.join(process.cwd(), "env.js"), + env_file: path.join(process.cwd(), 'env.js'), /**配置文件 */ - config_file: path.join(process.cwd(), "my_config.js"), + config_file: path.join(process.cwd(), 'my_config.js'), /**dyids存放目录 */ - dyids_dir: path.join(process.cwd(), "dyids"), + dyids_dir: path.join(process.cwd(), 'dyids'), /**lottery_info存放目录 */ - lottery_info_dir: path.join(process.cwd(), "lottery_info"), + lottery_info_dir: path.join(process.cwd(), 'lottery_info'), /**本地抽奖信息存放目录 */ - lottery_dyids: path.join(process.cwd(), "lottery_dyids"), - /**dyid长度 */ - dyid_length: 18, + lottery_dyids: path.join(process.cwd(), 'lottery_dyids'), /** * 将版本号转为数字 * @example @@ -29,7 +27,7 @@ const utils = { * @returns {Number} */ checkVersion(version) { - return (version.match(/\d.*/)[0]).split('.').reduce((a, v, i) => a + (0.01 ** i) * Number(v), 0) + return (version.match(/\d.*/)[0]).split('.').reduce((a, v, i) => a + (0.01 ** i) * Number(v), 0); }, /** * 安全的将JSON字符串转为对象 @@ -43,16 +41,16 @@ const utils = { if (typeof str === 'string') { try { const obj = JSON.parse(str); - return typeof obj === 'object' ? obj : false + return typeof obj === 'object' ? obj : false; } catch (e) { - utils.log.error("json解析", e) + utils.log.error('json解析', e + '\n' + params); return false; } } else { return false; } })(params); - return isJSON ? isJSON : {} + return isJSON ? isJSON : {}; }, /** * @template T @@ -61,7 +59,7 @@ const utils = { */ async try_for_each(iter, fn) { for (const item of iter) { - if (await fn(item)) break + if (await fn(item)) break; } }, /** @@ -74,12 +72,12 @@ const utils = { async retryfn(max_times, unexpected, fn) { let ret = null; for (let times = 0; times < max_times; times++) { - ret = await fn() + ret = await fn(); if (unexpected.includes(ret)) { - utils.log.warn('自动重试', `将在 ${times + 1} 分钟后再次尝试(${times + 1}/${max_times})`) - await utils.delay(60 * 1000 * (times + 1)) + utils.log.warn('自动重试', `将在 ${times + 1} 分钟后再次尝试(${times + 1}/${max_times})`); + await utils.delay(60 * 1000 * (times + 1)); } else { - break + break; } } return ret; @@ -100,6 +98,7 @@ const utils = { return _c(restNum - 1, argsList.concat(x)); }; } + return _c(func.length, []); }, /** @@ -123,18 +122,20 @@ const utils = { let c = { i: 0, next: () => c.i++, - clear: () => { c.i = 0 }, + clear: () => { + c.i = 0; + }, value: () => c.i - } - return c + }; + return c; }, /** * 无限序列 * `[0..]` */ - *infiniteNumber() { + * infiniteNumber() { for (let index = 0; ; index++) { - yield index + yield index; } }, /** @@ -148,7 +149,7 @@ const utils = { if (Array.isArray(arr) && arr.length) { RandomOne = arr[parseInt(Math.random() * arr.length)]; } - return RandomOne + return RandomOne; }, /** * Fisher–Yates shuffle洗牌 @@ -173,14 +174,14 @@ const utils = { return key_words.reduce((acc, word) => { word.startsWith('~') ? RegExp(word.slice(1)).test(text) && (acc = false) - : RegExp(word).test(text) && (acc = true) - return acc - }, false) + : RegExp(word).test(text) && (acc = true); + return acc; + }, false); }, /** * 是否有指定环境变量 * @param {string} env_name - * @returns + * @returns */ hasEnv(env_name) { return process.env[env_name] ? true : false; @@ -190,7 +191,7 @@ const utils = { _level: 3, _colors: [ chalk.hex('#64B3FF'), chalk.grey, chalk.hex('#FFA500'), - chalk.hex('#0070BB'), chalk.hex('#48BB31'), chalk.hex('#BBBB23'), chalk.hex('#FF0006') + chalk.hex('#0070BB'), chalk.hex('#48BB31'), chalk.hex('#BFFF00'), chalk.hex('#BBBB23'), chalk.hex('#FF0006') ], _iso_time: () => new Date(Date.now() + 288e5).toISOString().slice(0, -1) + '+08', _cache: [], @@ -207,16 +208,16 @@ const utils = { */ proPrint(msg, split = ' ') { if (msg instanceof Array) { - msg = msg.join(split) + msg = msg.join(split); } - console.log(msg) + console.log(msg); }, /** * @param {Array} msg * @returns */ rainbow(msg) { - this.proPrint(msg.map(it => it.split('').map(l => chalk.hex("#89cff0")(l)).join('')), '\n') + this.proPrint(msg.map(it => it.split('').map(l => chalk.hex('#89cff0')(l)).join('')), '\n'); }, /** * @param {number} done @@ -226,88 +227,101 @@ const utils = { progress_bar(done, total, size = 30) { let perc = done >= total ? 1 : done / total, bar = ~~(perc * size), - status_bar = `\r[${"=".repeat(bar) + ">" + " ".repeat(size - bar)}] ${(perc * 100 + ' ').slice(0, 4)}%` - process.stdout.write(status_bar) + status_bar = `\r[${'='.repeat(bar) + '>' + ' '.repeat(size - bar)}] ${(perc * 100 + ' ').slice(0, 4)}%`; + process.stdout.write(status_bar); }, debug(context, msg) { - if (this._level > 3) { + if (this._level >= 4) { if (msg instanceof Object) msg = JSON.stringify(msg, null, 4); let color_text_pair = [ [this._colors[0], `[${this._iso_time()}]`], - [this._colors[1], "[Debug]"], - [this._colors[2], `[帐号${process.env["NUMBER"]} ${context}]`], + [this._colors[1], '[Debug]'], + [this._colors[2], `[帐号${process.env['NUMBER']} ${context}]`], [this._colors[3], `[\n${msg}\n]`], ]; - this.proPrint(color_text_pair.map(([color, text]) => color(text))) + this.proPrint(color_text_pair.map(([color, text]) => color(text))); } }, info(context, msg) { - if (this._level > 2) { + if (this._level >= 3) { let color_text_pair = [ [this._colors[0], `[${this._iso_time()}]`], - [this._colors[1], "[Info]"], - [this._colors[2], `[帐号${process.env["NUMBER"]} ${context}]`], + [this._colors[1], '[Info]'], + [this._colors[2], `[帐号${process.env['NUMBER']} ${context}]`], [this._colors[4], `[${msg}]`], ]; this._cache.push(color_text_pair.map(it => it[1]).join(' ')); - this.proPrint(color_text_pair.map(([color, text]) => color(text))) + this.proPrint(color_text_pair.map(([color, text]) => color(text))); + } + }, + notice(context, msg) { + if (this._level >= 2) { + let color_text_pair = [ + [this._colors[0], `[${this._iso_time()}]`], + [this._colors[1], '[Notice]'], + [this._colors[2], `[帐号${process.env['NUMBER']} ${context}]`], + [this._colors[5], `[${msg}]`], + ]; + this._cache.push(color_text_pair.map(it => it[1]).join(' ')); + this.proPrint(color_text_pair.map(([color, text]) => color(text))); } }, warn(context, msg) { - if (this._level > 1) { + if (this._level >= 1) { let color_text_pair = [ [this._colors[0], `[${this._iso_time()}]`], - [this._colors[1], "[Warn]"], - [this._colors[2], `[帐号${process.env["NUMBER"]} ${context}]`], - [this._colors[5], `[\n${msg}\n]`], + [this._colors[1], '[Warn]'], + [this._colors[2], `[帐号${process.env['NUMBER']} ${context}]`], + [this._colors[6], `[\n${msg}\n]`], ]; this._cache.push(color_text_pair.map(it => it[1]).join(' ')); - this.proPrint(color_text_pair.map(([color, text]) => color(text))) + this.proPrint(color_text_pair.map(([color, text]) => color(text))); } }, error(context, msg) { - if (this._level > 0) { + if (this._level >= 0) { let color_text_pair = [ [this._colors[0], `[${this._iso_time()}]`], - [this._colors[1], "[Error]"], - [this._colors[2], `[帐号${process.env["NUMBER"]} ${context}]`], - [this._colors[6], `[\n${msg}\n]`], + [this._colors[1], '[Error]'], + [this._colors[2], `[帐号${process.env['NUMBER']} ${context}]`], + [this._colors[7], `[\n${msg}\n]`], ]; this._cache.push(color_text_pair.map(it => it[1]).join(' ')); - this.proPrint(color_text_pair.map(([color, text]) => color(text))) + this.proPrint(color_text_pair.map(([color, text]) => color(text))); } } }, /** * 验证码识别 - * @param {string} url + * @param {string} url * @returns {Promise} */ ocr(url) { return new Promise((resolve) => { send({ method: 'POST', - url: 'http://127.0.0.1:9898/ocr/url/text', + url: process.env['CHAT_CAPTCHA_OCR_URL'] || 'http://127.0.0.1:9898/ocr/url/text', headers: { - "content-type": 'application/x-www-form-urlencoded; charset=UTF-8', + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', }, contents: { url }, success: res => { - resolve(res.body) + resolve(res.body); }, failure: () => { - resolve() + resolve(null); } - }) - }) + }); + }); }, /** * 下载文件 * @param {string} url * @param {string} file_name + * @param {number} size * @returns {Promise} */ - download(url, file_name) { + download(url, file_name, size) { return new Promise((resolve, reject) => { send({ url, @@ -317,40 +331,43 @@ const utils = { retry: false }, success: ({ headers, resStream }) => { - const total_len = Number(headers["content-length"]) || 16000000; + const total_len = Number(headers['content-length']) || 16000000; let recv_length = 0; const wtbs = fs.createWriteStream(file_name); resStream.on('data', chuck => { - recv_length += chuck.length - utils.log.progress_bar(recv_length, total_len) - }) - resStream.pipe(wtbs) + recv_length += chuck.length; + utils.log.progress_bar(recv_length, total_len); + }); + resStream.pipe(wtbs); wtbs.on('finish', () => { - utils.log.proPrint('下载完成') - resolve() + utils.log.proPrint('下载完成'); + if (recv_length < size) { + reject(`未正确下载文件: ${recv_length}B < ${size}B`); + } + resolve(); }).on('error', error => { - wtbs.destroy() - reject(error) - }) + wtbs.destroy(); + reject(error); + }); }, failure: error => { - reject(error) + reject(error); } - }) + }); }); }, /** * 是否存在文件或目录 * @param {string} path - * @returns + * @returns */ hasFileOrDir(path) { try { - fs.accessSync(path, fs.constants.F_OK) + fs.accessSync(path, fs.constants.F_OK); } catch (_) { - return false + return false; } - return true + return true; }, /** * 生成文件夹 @@ -361,10 +378,10 @@ const utils = { return new Promise((resolve) => { fs.stat(dirname, (err) => { if (err) { - fs.mkdirSync(dirname) + fs.mkdirSync(dirname); } - resolve() - }) + resolve(); + }); }); }, /** @@ -381,18 +398,18 @@ const utils = { return new Promise((resolve, rejects) => { fs.open(fpath, flag, (err, fd) => { if (err) { - rejects(err) + rejects(err); } else { fs.write(fd, buffer, 0, buffer.length, 0, err => { - fs.close(fd) + fs.close(fd); if (err) { - rejects(err) + rejects(err); } else { resolve(); } - }) + }); } - }) + }); }); }, /** @@ -402,7 +419,7 @@ const utils = { */ readDyidFile(num) { const fpath = num < 2 ? path.join(utils.dyids_dir, 'dyid.txt') : path.join(utils.dyids_dir, `dyid${num}.txt`); - return fs.createReadStream(fpath, { encoding: 'utf8', highWaterMark: (utils.dyid_length + 1) * 1000 }) + return fs.createReadStream(fpath, { encoding: 'utf8' }); }, /** * 追加dyid @@ -411,44 +428,44 @@ const utils = { */ writeDyidFile(num) { const fpath = num < 2 ? path.join(utils.dyids_dir, 'dyid.txt') : path.join(utils.dyids_dir, `dyid${num}.txt`); - return fs.createWriteStream(fpath, { flags: 'a' }) + return fs.createWriteStream(fpath, { flags: 'a' }); }, /** * 追加lotteryinfo * @param {string} from - * @param {import("./core/searcher").LotteryInfo[]} lottery_info + * @param {import('./core/searcher').LotteryInfo[]} lottery_info * @return {Promise} */ async appendLotteryInfoFile(from, lottery_info) { let all_lottery_info = {}; try { - all_lottery_info = utils.strToJson(fs.readFileSync(path.join(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`)).toString()) + all_lottery_info = utils.strToJson(fs.readFileSync(path.join(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`)).toString()); } catch (_) { - all_lottery_info = {} + all_lottery_info = {}; } await utils.createDir(utils.lottery_info_dir); if (all_lottery_info[from] instanceof Array) { - all_lottery_info[from].push(...lottery_info) + all_lottery_info[from].push(...lottery_info); } else { - all_lottery_info[from] = lottery_info + all_lottery_info[from] = lottery_info; } - await utils.createFile(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`, JSON.stringify(all_lottery_info), "w") + await utils.createFile(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`, JSON.stringify(all_lottery_info), 'w'); }, /** * 读取lottery_info * @param {string} filename - * @return {Promise} + * @return {Promise} */ readLotteryInfoFile(filename) { return new Promise((resolve) => { fs.readFile(path.join(utils.lottery_info_dir, filename), (err, data) => { if (err) { - resolve([]) + resolve([]); } else { let all_lottery_info = utils.strToJson(data.toString('utf8')); - resolve(Object.values(all_lottery_info).flat()) + resolve(Object.values(all_lottery_info).flat()); } - }) + }); }); }, /** @@ -456,7 +473,7 @@ const utils = { */ async clearLotteryInfo() { await utils.createDir(utils.lottery_info_dir); - await utils.createFile(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`, "{}", "w") + await utils.createFile(utils.lottery_info_dir, `lottery_info_${Number(process.env.NUMBER)}.json`, '{}', 'w'); }, /** * 获取含抽奖dyids @@ -467,13 +484,74 @@ const utils = { return new Promise((resolve) => { fs.readFile(path.join(utils.lottery_dyids, filename), (err, data) => { if (err) { - resolve([]) + resolve([]); } else { - resolve(data.toString("utf8").split(/[^0123456789]+/)) + resolve(data.toString('utf8').split(/[^0123456789]+/)); } - }) + }); + }); + }, + getIpInfo() { + return new Promise((resolve) => { + send({ + url: 'https://myip.qq.com/', + method: 'GET', + success: res => resolve(res.body), + failure: err => resolve(err) + }); + }); + }, + printIpInfo(beforeProxy) { + const printMessage = beforeProxy ? '当前IP----->' : '代理后IP=======>'; + utils.getIpInfo().then(res => { + console.log(printMessage + res); + }).catch((err) => { + console.error('获取' + printMessage + '地址失败', err); }); - } + }, + /** + * 获取ai评论 + * @param {string} url + * @param {object} body + * @param {string} prompt + * @param {string} content + * @returns {Promise} + */ + getAiContent(url, body, prompt, content) { + return new Promise((resolve) => { + send({ + method: 'POST', + url, + headers: { + 'authorization': 'Bearer ' + process.env.AI_API_KEY, + 'content-type': 'application/json' + }, + contents: { + ...body, + 'stream': false, + 'enable_thinking': true, + 'response_format': { 'type': 'text' }, + 'messages': [ + { + 'role': 'system', + 'content': prompt + }, + { + 'role': 'user', + 'content': content + } + ] + }, + success: res => { + const data = utils.strToJson(res.body); + resolve(data?.choices?.[0]?.message?.content || null); + }, + failure: () => { + resolve(null); + } + }); + }); + }, }; diff --git a/main.js b/main.js index 30b86561c9..5fd6ffba96 100644 --- a/main.js +++ b/main.js @@ -1,24 +1,34 @@ -const { version: ve, env_file, config_file, log, hasEnv, delay, hasFileOrDir, clearLotteryInfo } = require("./lib/utils"); - +const { + version: ve, + env_file, + config_file, + log, + hasEnv, + delay, + hasFileOrDir, + clearLotteryInfo, printIpInfo +} = require('./lib/utils'); +const { HttpsProxyAgent } = require('https-proxy-agent'); +const request = require('https'); const metainfo = [ - ` _ _ _ _____ _ _ `, - ` | | | | | | / ____| (_) | | `, - ` | | ___ | |_| |_ ___ _ __ _ _| (___ ___ _ __ _ _ __ | |_ `, - ` | | / _ \\| __| __/ _ \\ '__| | | |\\___ \\ / __| '__| | '_ \\| __|`, - ` | |___| (_) | |_| || __/ | | |_| |____) | (__| | | | |_) | |_ `, - ` |______\\___/ \\__|\\__\\___|_| \\__, |_____/ \\___|_| |_| .__/ \\__|`, - ` __/ | | | `, - ` |___/ |_| `, - ` `, + ' _ _ _ _____ _ _ ', + ' | | | | | | / ____| (_) | | ', + ' | | ___ | |_| |_ ___ _ __ _ _| (___ ___ _ __ _ _ __ | |_ ', + ' | | / _ \\| __| __/ _ \\ \'__| | | |\\___ \\ / __| \'__| | \'_ \\| __|', + ' | |___| (_) | |_| || __/ | | |_| |____) | (__| | | | |_) | |_ ', + ' |______\\___/ \\__|\\__\\___|_| \\__, |_____/ \\___|_| |_| .__/ \\__|', + ' __/ | | | ', + ' |___/ |_| ', + ' ', ` This: v${ve} Nodejs: ${process.version} Written By shanmite`, -] +]; /**多账号存储 */ let multiple_account = []; /**循环等待时间 */ let loop_wait = 0; /**账号状态标记 1正常 -1失效 */ // eslint-disable-next-line no-unused-vars -let ck_flag = 0 +let ck_flag = 0; /** * @returns {Promise} 错误信息 @@ -31,15 +41,30 @@ async function main() { : JSON.parse(MULTIPLE_ACCOUNT_PARM); process.env.ENABLE_MULTIPLE_ACCOUNT = ''; + let localhost = request.globalAgent; for (const acco of muti_acco) { process.env.COOKIE = acco.COOKIE; process.env.NUMBER = acco.NUMBER; process.env.CLEAR = acco.CLEAR; process.env.NOTE = acco.NOTE; + process.env.ACCOUNT_UA = acco.ACCOUNT_UA; + if (acco.PROXY_HOST) { + printIpInfo(true); + //http://ip:port + //http://user:pwd@ip:port' + const proxyUrl = acco.PROXY_USER + ? 'http://' + acco.PROXY_USER + ':' + acco.PROXY_PASS + '@' + acco.PROXY_HOST + ':' + acco.PROXY_PORT + : 'http://' + acco.PROXY_HOST + ':' + acco.PROXY_PORT; + request.globalAgent = new HttpsProxyAgent(proxyUrl); + printIpInfo(false); + }else { + //未设置还原 + request.globalAgent = localhost; + } const err_msg = await main(); if (err_msg) { - return err_msg + return err_msg; } else { if (ck_flag === 1) { await delay(acco.WAIT); @@ -47,31 +72,36 @@ async function main() { await delay(3 * 1000); } } + request.globalAgent = localhost; } - /**多账号状态还原 */ process.env.ENABLE_MULTIPLE_ACCOUNT = ENABLE_MULTIPLE_ACCOUNT; } else if (COOKIE) { - const global_var = require("./lib/data/global_var"); + const global_var = require('./lib/data/global_var'); await global_var.init(COOKIE, NUMBER); /**引入基础功能 */ - const { start, isMe, clear, account, checkCookie, login } = require("./lib/index"); + const { start, isMe, clear, account, checkCookie, login } = require('./lib/index'); log.info('main', '当前为第' + NUMBER + '个账号'); - log._cache.length = 0 + log._cache.length = 0; const mode = process.env.lottery_mode; - const help_msg = "用法: lottery [OPTIONS]\n\nOPTIONS:\n\tstart 启动抽奖\n\tcheck 中奖检查\n\tacount 查看帐号信息\n\tclear 清理动态和关注\n\tlogin 扫码登录更新CK\n\tupdate 检查更新\n\thelp 帮助信息"; + const help_msg = '用法: lottery [OPTIONS]\n\nOPTIONS:\n\tstart 启动抽奖\n\tcheck 中奖检查\n\tacount 查看帐号信息\n\tclear 清理动态和关注\n\tlogin 扫码登录更新CK\n\tupdate 检查更新\n\thelp 帮助信息'; if (await checkCookie(NUMBER)) { - const { lottery_loop_wait, check_loop_wait, clear_loop_wait, save_lottery_info_to_file } = require("./lib/data/config"); + const { + lottery_loop_wait, + check_loop_wait, + clear_loop_wait, + save_lottery_info_to_file + } = require('./lib/data/config'); ck_flag = 1; switch (mode) { case 'start': log.info('抽奖', '开始运行'); loop_wait = lottery_loop_wait; if (save_lottery_info_to_file) { - await clearLotteryInfo() + await clearLotteryInfo(); } await start(NUMBER); break; @@ -91,27 +121,27 @@ async function main() { log.info('登录状态', '正常,跳过扫码'); break; case 'help': - return help_msg + return help_msg; case 'account': log.info('检查帐号信息', '开始运行'); await account(); break; case undefined: - return "未提供以下参数\n\t[OPTIONS]\n\n" + help_msg + return '未提供以下参数\n\t[OPTIONS]\n\n' + help_msg; default: - return `提供了错误的[OPTIONS] -> ${mode}\n\n` + help_msg + return `提供了错误的[OPTIONS] -> ${mode}\n\n` + help_msg; } } else { - log.error('Cookie已失效', '切换账号时不要点击退出账号而应直接删除Cookie退出') + log.error('Cookie已失效', '切换账号时不要点击退出账号而应直接删除Cookie退出'); ck_flag = -1; - if (mode === "login") { + if (mode === 'login') { log.info('登陆', '开始扫码'); await login(NUMBER); await delay(1000); } } } else { - return '请查看README文件, 在env.js指定位置填入cookie' + return '请查看README文件, 在env.js指定位置填入cookie'; } } @@ -122,7 +152,7 @@ async function main() { function initEnv() { if (hasFileOrDir(env_file)) { const - env = require("./lib/data/env"), + env = require('./lib/data/env'), multiple_account_parm = env.get_multiple_account(); if (multiple_account_parm) { @@ -138,10 +168,10 @@ function initEnv() { } else { log.init(); log.error('环境变量初始化', '未在当前目录下找到env.js文件或者在环境变量中设置所需参数'); - return true + return true; } - return false + return false; } /** @@ -150,32 +180,32 @@ function initEnv() { */ function initConfig() { if (hasFileOrDir(config_file)) { - const config = require("./lib/data/config"); + const config = require('./lib/data/config'); config.init(); log.info('配置文件初始化', '成功加载my_config.js文件'); } else { log.error('配置文件初始化', '未在当前目录下找到my_config.js文件'); - return true + return true; } - return false + return false; } (async function () { - log.rainbow(metainfo) + log.rainbow(metainfo); if (initEnv() || initConfig()) return; /**OPTIONS */ - process.env.lottery_mode = process.argv[2] + process.env.lottery_mode = process.argv[2]; - log.info('检查更新', '开始') + log.info('检查更新', '开始'); - if (process.env.lottery_mode === "update") { - await require("./lib/update").update(true) - return + if (process.env.lottery_mode === 'update') { + await require('./lib/update').update(true); + return; } else { - await require("./lib/update").update(false) + await require('./lib/update').update(false); } const err_msg = await main(); @@ -185,13 +215,13 @@ function initConfig() { await delay(5 * 1000); } else { while (loop_wait) { - log.info('程序休眠', `${loop_wait / 1000}秒后再次启动`) - await delay(loop_wait) + log.info('程序休眠', `${loop_wait / 1000}秒后再次启动`); + await delay(loop_wait); if (initEnv() || initConfig()) return; - await main() + await main(); } - log.info('结束运行', '未在my_config.js中设置休眠时间') + log.info('结束运行', '未在my_config.js中设置休眠时间'); } process.exit(0); -})() \ No newline at end of file +})(); \ No newline at end of file diff --git a/my_config.example.js b/my_config.example.js index f69550d619..3397af94d5 100644 --- a/my_config.example.js +++ b/my_config.example.js @@ -49,13 +49,13 @@ module.exports = Object.freeze({ * @example * ["file://lottery_info_1.json"] */ - APIs: ["file://lottery_info_1.json"], + APIs: ['file://lottery_info_1.json'], /** * lottery_dyids目录下抽奖动态文件名(如dyids.txt) * 一行一个dyids(非数字字符分割即可) */ - TxT: ["dyids.txt"], + TxT: ['dyids.txt'], /** * 抽奖参与顺序组合 @@ -79,15 +79,15 @@ module.exports = Object.freeze({ * API发送数据类型 {LotteryInfo[]} * 上传抽奖信息的链接字符串 */ - set_lottery_info_url: "", + set_lottery_info_url: '', /** * 动态中的关键词(表示须同时满足以下条件) * 符合js正则表达式的字符串 */ key_words: [ - "[抽奖送揪]|福利", - "[转关评粉]|参与" + '[抽奖送揪]|福利', + '[转关评粉]|参与' ], /** @@ -313,7 +313,7 @@ module.exports = Object.freeze({ /** * 屏蔽词 */ - blockword: ["脚本", "抽奖号", "钓鱼"], + blockword: ['脚本', '抽奖号', '钓鱼'], /** * 转发并评论 @@ -341,6 +341,19 @@ module.exports = Object.freeze({ '坚持不懈,迎难而上,开拓创新!', '[OK][OK]', '我来抽个奖', '中中中中中中', '[doge][doge][doge]', '我我我', ], + /** + * AI Chat completions参数 + * https://learn.microsoft.com/en-us/azure/ai-foundry/openai/reference#chat-completions + */ + ai_comments_parm: { + /** + * /chat/completions + */ + url: '', + body: {}, + prompt: '' + }, + /** * 是否抄热评 */ @@ -349,7 +362,7 @@ module.exports = Object.freeze({ /** * 热评屏蔽词 */ - copy_blockword: ["三不原则"], + copy_blockword: ['三不原则'], /** * - 抽奖UP用户分组id(网页端点击分区后地址栏中的tagid) @@ -379,10 +392,10 @@ module.exports = Object.freeze({ * - 优先级递增 */ notice_key_words: [ - "~预约成功|预约主题", - "中奖|获得|填写|写上|提供|收货地址|支付宝账号|码|大会员", - "~你的账号在新设备或平台登录成功", - "~你预约的直播已开始" + '~预约成功|预约主题', + '中奖|获得|填写|写上|提供|收货地址|支付宝账号|码|大会员', + '~你的账号在新设备或平台登录成功', + '~你预约的直播已开始' ], /** @@ -459,7 +472,7 @@ module.exports = Object.freeze({ * 1 * [1,2,4] */ - clear_dynamic_type: [1] + clear_dynamic_type: [1], }, /** @@ -470,15 +483,12 @@ module.exports = Object.freeze({ /** * 手动添加抽奖号UID * - 抽奖动态下的二级小号 + * + * 帐号1存储抽奖信息至文件 */ UIDs: [], - TAGs: [ - '互动抽奖', - '转发抽奖', - '动态抽奖', - '抽奖', - ], + TAGs: [], Articles: [ '抽奖合集' @@ -486,8 +496,30 @@ module.exports = Object.freeze({ APIs: [], + /** + * 默认为硅基流动,可以修改为其他AI服务 + */ + ai_comments_parm: { + url: 'https://api.siliconflow.cn/v1/chat/completions', + body: { + 'model': 'Qwen/Qwen3-32B', + 'max_tokens': 512, + 'thinking_budget': 4096, + 'min_p': 0.05, + 'temperature': 0.7, + 'top_p': 0.7, + 'top_k': 50, + 'frequency_penalty': 0.5, + 'n': 1, + }, + prompt: '请根据以下内容直接生成一条简短评论,无需说明信息,且不包含任何敏感词汇。' + }, + save_lottery_info_to_file: true, }, + /** + * 后续帐号从文件提取抽奖信息转抽 + */ config_2: {}, config_3: {} -}) +}); diff --git a/package.json b/package.json index e31ff2a94b..cf11829fd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lottery-auto-script", - "version": "2.8.11", + "version": "2.10.2", "description": "自动参与B站动态抽奖", "main": "main.js", "scripts": { @@ -13,7 +13,9 @@ "account": "node main.js account", "help": "node main.js help", "pkg": "bash script/build/pkg.sh", - "changelog": "bash script/build/changelog.sh" + "changelog": "bash script/build/changelog.sh", + "lint": "npx eslint lib/ test/ *.js", + "lint-fix": "npx eslint lib/ test/ *.js --fix" }, "files": [ "lib", diff --git a/script/build/pkg.sh b/script/build/pkg.sh index 6a9da41843..dbf6f02057 100755 --- a/script/build/pkg.sh +++ b/script/build/pkg.sh @@ -28,12 +28,12 @@ mkdir -p $TARGET_DIR npm install if [[ "$1" == *"arm"* ]]; then - OUTFILE="$TARGET_DIR/lottery-auto-script-node18-$1" + OUTFILE="$TARGET_DIR/lottery-auto-script-$1" sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes podman run -it \ --rm \ -v ${PWD}:/root/lottery \ - shanmite/pkg-arm64 -t "node18.5.0-$1" -o "$OUTFILE" . + shanmite/pkg-arm64 -t "node18-$1" -o "$OUTFILE" . elif [[ "$1" == *"x64"* ]]; then OUTFILE="$TARGET_DIR/lottery-auto-script-$1" npx pkg -t "$1" -o "$OUTFILE" . diff --git a/script/docker/init.sh b/script/docker/init.sh index a6f1e7848e..d367fe194a 100755 --- a/script/docker/init.sh +++ b/script/docker/init.sh @@ -37,7 +37,7 @@ CONFIG_FILE=my_config.js # docker仓库 DOCKER_REPO=shanmite/lottery_auto_docker # cdn -CDN=https://cdn.staticaly.com/gh/shanmiteko/LotteryAutoScript/main +CDN=https://gitlab.com/shanmiteko/LotteryAutoScript/-/raw/main # env.example.js文件 ENV_EXAMPLE="$CDN/env.example.js" # my_config.example.js文件 diff --git a/script/qinglong/init.sh b/script/qinglong/init.sh index 6cc50c7a5f..827f31a070 100644 --- a/script/qinglong/init.sh +++ b/script/qinglong/init.sh @@ -4,9 +4,7 @@ set -e NAME=LotteryAutoScript BRABCH=main -# 视网络情况选择链接 -GIT_REPO=https://github.com/shanmiteko/${NAME}.git -# GIT_REPO=https://ghproxy.com/https://github.com/shanmiteko/${NAME}.git +GIT_REPO=https://gitlab.com/shanmiteko/${NAME}.git if [ -d "$NAME" ]; then cd $NAME diff --git a/test/api.test.js b/test/api.test.js index 074feb5f3e..59f745e698 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,55 +1,55 @@ const assert = require('assert'); -const bili_client = require("../lib/net/bili"); +const bili_client = require('../lib/net/bili'); const util = require('./util'); const { parseDynamicCard } = require('../lib/core/searcher'); (async () => { assert.notEqual(await bili_client.getMyinfo(), null); - await util.par_run([0, 3], [ + await util.par_run([], [ // 0 async () => { - assert.equal((await bili_client.getTopRcmd()).length, 10) + assert.equal((await bili_client.getTopRcmd()).length, 10); }, // 1 async () => { assert.equal(await bili_client.sendChat( - parseDynamicCard(await bili_client.getOneDynamicByDyid("692193323569381399")).rid_str, - "test", + parseDynamicCard(await bili_client.getOneDynamicByDyid('692193323569381399')).rid_str, + 'test', 11), 7 - ) + ); }, // 2 async () => { assert.equal(await bili_client.sendChat( - parseDynamicCard(await bili_client.getOneDynamicByDyid("11229466874154064")).rid_str, - "test", + parseDynamicCard(await bili_client.getOneDynamicByDyid('11229466874154064')).rid_str, + 'test', 1), 3 - ) + ); }, // 3 async () => { - assert.notEqual((await bili_client.searchArticlesByKeyword("专栏")).length, 0) + assert.notEqual((await bili_client.searchArticlesByKeyword('专栏')).length, 0); }, // 4 async () => { - assert.notEqual(await bili_client.sendChat("703886913053917267", "t", 17), 1) + assert.notEqual(await bili_client.sendChat('703886913053917267', 't', 17), 1); }, // 5 async () => { - assert(!await bili_client.createDynamic("1")) + assert(!await bili_client.createDynamic('1')); }, // 6 async () => { - assert.equal(await bili_client.autolike("761391835139538967"), 4) + assert.equal(await bili_client.autolike('761391835139538967'), 4); }, // 7 async () => { - assert(await bili_client.rmDynamic("835102428771647513")) + assert(await bili_client.rmDynamic('835102428771647513')); }, - ]) + ]); - console.log("api.test ... ok!"); -})() \ No newline at end of file + console.log('api.test ... ok!'); +})(); \ No newline at end of file diff --git a/test/article.test.js b/test/article.test.js index a43b39854a..bff709ea93 100644 --- a/test/article.test.js +++ b/test/article.test.js @@ -1,15 +1,15 @@ const assert = require('assert'); -const bili_client = require("../lib/net/bili"); +const bili_client = require('../lib/net/bili'); const util = require('./util'); (async () => { - await util.par_run([0], [ + await util.par_run([], [ // 0 async () => { let info = await bili_client.getOneArticleByCv(22112353); let short_ids = [...new Set(info.match(/(?<=b23.tv\/)[a-zA-Z0-9]{7}/g) || [])]; - assert.equal((await Promise.all(short_ids.map(bili_client.shortDynamicIdToDyid)))[0], "767357823884460033"); + assert.equal((await Promise.all(short_ids.map(bili_client.shortDynamicIdToDyid)))[0], '767357823884460033'); }, - ]) - console.log("article.test ... ok!"); -})() \ No newline at end of file + ]); + console.log('article.test ... ok!'); +})(); \ No newline at end of file diff --git a/test/dyidSearch.test.js b/test/dyidSearch.test.js new file mode 100644 index 0000000000..d2a3821059 --- /dev/null +++ b/test/dyidSearch.test.js @@ -0,0 +1,14 @@ +const assert = require('assert'); +const util = require('./util'); +const d_storage = require('../lib/helper/d_storage'); + +(async () => { + await util.par_run([], [ + // 0 + async () => { + assert(await d_storage.searchDyid('1234567901234568')); + assert(!await d_storage.searchDyid('1234567901234569')); + }, + ]); + console.log('dyidSearch.test ... ok!'); +})(); \ No newline at end of file diff --git a/test/dynamic_card.test.js b/test/dynamic_card.test.js index e655464b56..ea307e398e 100644 --- a/test/dynamic_card.test.js +++ b/test/dynamic_card.test.js @@ -1,68 +1,70 @@ const assert = require('assert'); -const bili_client = require("../lib/net/bili"); -const searcher = require("../lib/core/searcher"); +const bili_client = require('../lib/net/bili'); +const searcher = require('../lib/core/searcher'); const util = require('./util'); (async () => { - await util.par_run([0, 1, 2, 3, 4, 5, 6, 7, 8], [ + await util.par_run([0, 1], [ // 0 async () => { - let info = await bili_client.getOneDynamicByDyid("728424890210713624"); + let info = await bili_client.getOneDynamicByDyid('728424890210713624'); + assert(searcher.parseDynamicCard(info).is_charge_lottery); + info = await bili_client.getOneDynamicByDyid('1143258210499559428'); assert(searcher.parseDynamicCard(info).is_charge_lottery); }, // 1 async () => { - let info = await bili_client.getOneDynamicByDyid("768874900850999300"); + let info = await bili_client.getOneDynamicByDyid('1150096953788334085'); assert(searcher.parseDynamicCard(info).origin_is_charge_lottery); }, // 2 async () => { - let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid("746824225190314008")); - let chats = await bili_client.getChat(card.rid_str, card.chat_type) - assert(chats.length > 0 && typeof chats[0][0] == "string") + let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid('746824225190314008')); + let chats = await bili_client.getChat(card.rid_str, card.chat_type); + assert(chats.length > 0 && typeof chats[0][0] == 'string'); }, // 3 async () => { - let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid("747169355882561625")); - assert(card.chat_type == 11) - card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid("747441158580338693")); - assert(card.chat_type == 17) - assert(card.origin_chat_type == 11) + let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid('900172162530279445')); + assert.equal(card.chat_type, 11); + card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid('926978638295859236')); + assert.equal(card.chat_type, 17); + assert.equal(card.origin_chat_type, 11); }, // 4 async () => { - assert.equal(await bili_client.getOneDynamicByDyid("111111111111111111"), undefined); - assert.notEqual(await bili_client.getOneDynamicByDyid("746824225190314008"), undefined); - assert.equal(await bili_client.getOneDynamicByDyid("761475750233636886"), undefined); + // assert.equal(await bili_client.getOneDynamicByDyid("111111111111111111"), undefined); + // assert.notEqual(await bili_client.getOneDynamicByDyid("746824225190314008"), undefined); + // assert.equal(await bili_client.getOneDynamicByDyid("761475750233636886"), undefined); }, // 5 async () => { - let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid("762591475338838053")); - let chats = await bili_client.getChat(card.rid_str, card.chat_type) - assert.equal(chats.length, 19) - card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid("762502724122050647")); - chats = await bili_client.getChat(card.rid_str, card.chat_type) - assert.equal(chats.filter(it => it[0] === '六的月').length, 0) + let card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid('762591475338838053')); + let chats = await bili_client.getChat(card.rid_str, card.chat_type); + assert.equal(chats.length, 19); + card = searcher.parseDynamicCard(await bili_client.getOneDynamicByDyid('762502724122050647')); + chats = await bili_client.getChat(card.rid_str, card.chat_type); + assert.equal(chats.filter(it => it[0] === '六的月').length, 0); }, // 6 async () => { - const dy = await bili_client.getOneDynamicByDyid("774973685666676768"); - const card = searcher.parseDynamicCard(dy) - assert.notEqual(card.description + "", undefined + ""); + const dy = await bili_client.getOneDynamicByDyid('774973685666676768'); + const card = searcher.parseDynamicCard(dy); + assert.notEqual(card.description + '', undefined + ''); }, // 7 async () => { - const dy = await bili_client.getOneDynamicByDyid("832208853440397352"); - const card = searcher.parseDynamicCard(dy) - assert.equal(card.reserve_id, "3106984"); + const dy = await bili_client.getOneDynamicByDyid('924676093465591832'); + const card = searcher.parseDynamicCard(dy); + assert.equal(card.reserve_id, '3715576'); }, // 8 async () => { - const dy = await bili_client.getOneDynamicByDyid("832966468497834066"); - const card = searcher.parseDynamicCard(dy) - assert.equal(card.origin_reserve_id, "3106984"); + const dy = await bili_client.getOneDynamicByDyid('925061227481137187'); + const card = searcher.parseDynamicCard(dy); + assert.equal(card.origin_reserve_id, '3715576'); }, - ]) + ]); - console.log("dynamic_card.test ... ok!"); -})() \ No newline at end of file + console.log('dynamic_card.test ... ok!'); +})(); \ No newline at end of file diff --git a/test/index.js b/test/index.js index 29d3210464..e9ce9be1c3 100644 --- a/test/index.js +++ b/test/index.js @@ -1,12 +1,12 @@ -const env = require("../lib/data/env"); -const global_var = require("../lib/data/global_var"); +const env = require('../lib/data/env'); +const global_var = require('../lib/data/global_var'); const { log } = require('../lib/utils'); const fs = require('fs'); -log._level = 4 -env.init() -global_var.init(process.env["COOKIE"], 1) +log._level = 4; +env.init(); +global_var.init(process.env['COOKIE'], 1); fs.readdirSync(module.path) - .filter(file => file.endsWith(".test.js")) - .forEach(file => require(`${module.path}/${file}`)) \ No newline at end of file + .filter(file => file.endsWith('.test.js')) + .forEach(file => require(`${module.path}/${file}`)); \ No newline at end of file diff --git a/test/ocr.test.js b/test/ocr.test.js index 91e460684a..d2fcde2387 100644 --- a/test/ocr.test.js +++ b/test/ocr.test.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const bili_client = require("../lib/net/bili"); +const bili_client = require('../lib/net/bili'); const util = require('./util'); const { parseDynamicCard } = require('../lib/core/searcher'); @@ -9,7 +9,7 @@ const { parseDynamicCard } = require('../lib/core/searcher'); async () => { assert.notEqual(await bili_client.getMyinfo(), null); - const rid = parseDynamicCard(await bili_client.getOneDynamicByDyid("551416252543796684")).rid_str; + const rid = parseDynamicCard(await bili_client.getOneDynamicByDyid('551416252543796684')).rid_str; for (let index = 0; index < 100; index++) { console.log(index); @@ -17,7 +17,7 @@ const { parseDynamicCard } = require('../lib/core/searcher'); rid, Date.now().toString(), 17, - ) + ); } },]); -})() +})(); diff --git a/test/util.js b/test/util.js index 9b10195ffe..861c362ada 100644 --- a/test/util.js +++ b/test/util.js @@ -5,9 +5,9 @@ function par_run(nums, fns) { return Promise.all( nums.map(num => fns[num]()) - ) + ); } module.exports = { par_run -} \ No newline at end of file +}; \ No newline at end of file