From 95e4f9293e640bae67199dd8c562e2677f187a90 Mon Sep 17 00:00:00 2001
From: Lkeme <19500576+lkeme@users.noreply.github.com>
Date: Fri, 3 Jun 2022 23:34:44 +0800
Subject: [PATCH] [refactor] all
---
.github/FUNDING.yml | 12 -
.github/ISSUE_TEMPLATE/Issue_Template_CN.md | 47 -
.github/ISSUE_TEMPLATE/Issue_Template_EN.md | 47 -
.github/workflows/build_image_bhp.yml | 22 -
.gitignore | 31 +-
ABOUT.md | 3 +-
CHANGELOG.md | 843 ------------------
DOC.md | 338 -------
NOTIFY.md | 132 ---
README.md | 153 ----
index.php => app.php | 29 +-
composer.json | 86 +-
conf/bili.yaml | 27 -
conf/device.yaml | 14 -
conf/user.ini.example | 313 -------
data/activity_infos.json | 36 -
data/capsule_infos.json | 43 -
data/latest_version.json | 14 -
data/reply_words.json | 175 ----
docker/Dockerfile | 46 -
docker/entrypoint.sh | 71 --
phpunit.xml | 16 +
plugin/CheckUpdate/CheckUpdate.php | 140 +++
plugin/Login/Login.php | 604 +++++++++++++
profile/example/config/user.ini | 123 +++
profile/example/device/device.yaml | 34 +
{data => resources}/filter_library.json | 1 +
resources/version.json | 14 +
src/Api/Passport/ApiCaptcha.php | 60 ++
src/Api/Passport/ApiLogin.php | 91 ++
src/Api/Passport/ApiOauth2.php | 118 +++
src/Api/PassportTv/ApiQrcode.php | 61 ++
src/Bootstrap/Bootstrap.php | 103 +++
src/Cache/Cache.php | 160 ++++
src/Config/Config.php | 42 +
src/Console/Command/AppCommand.php | 64 ++
src/Console/Command/RestoreCommand.php | 57 ++
src/Console/Command/ScriptCommand.php | 54 ++
src/Console/Console.php | 102 +++
src/Device/Device.php | 64 ++
src/Env/Env.php | 136 +++
src/FilterWords/FilterWords.php | 43 +
src/Helpers.php | 181 ++++
src/Log/Log.php | 222 +++++
src/{plugin => Notice}/Notice.php | 282 +++---
.../BasePlugin.php} | 34 +-
src/Plugin/BasePluginInfo.php | 141 +++
.../BasePluginRW.php} | 34 +-
src/Plugin/Plugin.php | 224 +++++
src/Request/MultiRequest.php | 164 ++++
src/Request/Request.php | 565 ++++++++++++
src/Sign/Sign.php | 125 +++
src/Task/Task.php | 69 ++
src/TimeLock/TimeLock.php | 195 ++++
src/User/User.php | 52 ++
src/Util/ArrayR/ArrayR.php | 42 +
src/{tool => Util/Common}/Common.php | 41 +-
src/Util/DesignPattern/SingleTon.php | 89 ++
.../Exceptions/GetConfigException.php} | 40 +-
.../Exceptions/HttpClientInitException.php | 34 +
.../Exceptions/MethodNotFoundException.php | 36 +
.../Exceptions/ResponseEmptyException.php} | 45 +-
.../Generator.php => Util/Fake/Fake.php} | 42 +-
src/{tool => Util/Fake}/Faker.php | 4 +-
src/Util/Os/Path.php | 59 ++
src/Util/Qrcode/Lib/Constants.php | 59 ++
src/Util/Qrcode/Lib/FrameFiller.php | 117 +++
src/Util/Qrcode/Lib/QrBitStream.php | 200 +++++
src/Util/Qrcode/Lib/QrCode.php | 164 ++++
src/Util/Qrcode/Lib/QrEncode.php | 76 ++
src/Util/Qrcode/Lib/QrImage.php | 89 ++
src/Util/Qrcode/Lib/QrInput.php | 492 ++++++++++
src/Util/Qrcode/Lib/QrInputItem.php | 280 ++++++
src/Util/Qrcode/Lib/QrMask.php | 347 +++++++
src/Util/Qrcode/Lib/QrRawCode.php | 128 +++
src/Util/Qrcode/Lib/QrRs.php | 44 +
src/Util/Qrcode/Lib/QrRsBlock.php | 37 +
src/Util/Qrcode/Lib/QrRsItem.php | 173 ++++
src/Util/Qrcode/Lib/QrSpec.php | 600 +++++++++++++
src/Util/Qrcode/Lib/QrSplit.php | 340 +++++++
src/Util/Qrcode/Lib/QrStr.php | 34 +
src/Util/Qrcode/Lib/QrTools.php | 39 +
src/Util/Qrcode/Qrcode.php | 88 ++
src/Util/Qrcode/VERSION | 3 +
src/Util/Resource/BaseResource.php | 154 ++++
src/Util/Resource/BaseResourcePoly.php | 153 ++++
src/Util/Resource/Collection.php | 218 +++++
src/Util/Resource/CollectionInterface.php | 62 ++
src/Util/Resource/Resource.php | 109 +++
src/core/App.php | 234 -----
src/core/BCommand.php | 59 --
src/core/Cache.php | 126 ---
src/core/Config.php | 94 --
src/core/Core.php | 106 +++
src/core/Curl.php | 362 --------
src/core/Device.php | 92 --
src/core/Env.php | 101 ---
src/core/Helpers.php | 141 ---
src/core/HttpClient.php | 169 ----
src/core/Log.php | 180 ----
src/core/Task.php | 114 ---
src/plugin/ActivityLottery.php | 246 -----
src/plugin/AloneTcpClient.php | 298 -------
src/plugin/AnchorRaffle.php | 259 ------
src/plugin/AwardRecord.php | 161 ----
src/plugin/Barrage.php | 117 ---
src/plugin/BpConsumption.php | 165 ----
src/plugin/CapsuleLottery.php | 315 -------
src/plugin/CheckUpdate.php | 130 ---
src/plugin/Competition.php | 185 ----
src/plugin/DailyBag.php | 84 --
src/plugin/DailyTask.php | 137 ---
src/plugin/DataTreating.php | 78 --
src/plugin/DoubleHeart.php | 94 --
src/plugin/Dynamic.php | 305 -------
src/plugin/Forward.php | 314 -------
src/plugin/GiftHeart.php | 99 --
src/plugin/GiftRaffle.php | 142 ---
src/plugin/GiftSend.php | 302 -------
src/plugin/GroupSignIn.php | 109 ---
src/plugin/GuardRaffle.php | 152 ----
src/plugin/Judge.php | 370 --------
src/plugin/Live.php | 581 ------------
src/plugin/LiveReservation.php | 153 ----
src/plugin/Login.php | 554 ------------
src/plugin/MainSite.php | 433 ---------
src/plugin/ManGa.php | 96 --
src/plugin/MaterialObject.php | 246 -----
src/plugin/PkRaffle.php | 140 ---
src/plugin/PolishTheMedal.php | 132 ---
src/plugin/RedPocketRaffle.php | 95 --
src/plugin/Schedule.php | 176 ----
src/plugin/Sign.php | 97 --
src/plugin/Silver.php | 133 ---
src/plugin/Silver2Coin.php | 112 ---
src/plugin/SmallHeart.php | 102 ---
src/plugin/Statistics.php | 306 -------
src/plugin/StormRaffle.php | 180 ----
src/plugin/User.php | 356 --------
src/plugin/VipPrivilege.php | 113 ---
src/plugin/ZoneTcpClient.php | 698 ---------------
src/plugins/BasePlugin.php | 120 ---
src/plugins/Plugins.php | 169 ----
src/script/BaseTask.php | 78 --
src/script/UnFollow.php | 162 ----
src/script/User.php | 72 --
src/tool/ArrayToTextTable.php | 222 -----
src/tool/BvToAv.php | 53 --
src/tool/DumpMemory.php | 25 -
src/tool/File.php | 278 ------
src/tool/Path.php | 37 -
src/tool/UserAgent.php | 410 ---------
src/util/AllotTasks.php | 110 ---
src/util/BaseRaffle.php | 233 -----
src/util/FilterWords.php | 53 --
src/util/Singleton.php | 126 ---
src/util/TimeLock.php | 153 ----
src/util/XliveHeartBeat.php | 324 -------
tests/ArrayToTextTableTest.php | 48 -
tool/ConfigGenerator.php | 73 --
160 files changed, 8740 insertions(+), 15844 deletions(-)
delete mode 100644 .github/FUNDING.yml
delete mode 100644 .github/ISSUE_TEMPLATE/Issue_Template_CN.md
delete mode 100644 .github/ISSUE_TEMPLATE/Issue_Template_EN.md
delete mode 100644 .github/workflows/build_image_bhp.yml
delete mode 100644 CHANGELOG.md
delete mode 100644 DOC.md
delete mode 100644 NOTIFY.md
delete mode 100644 README.md
rename index.php => app.php (70%)
delete mode 100644 conf/bili.yaml
delete mode 100644 conf/device.yaml
delete mode 100644 conf/user.ini.example
delete mode 100644 data/activity_infos.json
delete mode 100644 data/capsule_infos.json
delete mode 100644 data/latest_version.json
delete mode 100644 data/reply_words.json
delete mode 100644 docker/Dockerfile
delete mode 100644 docker/entrypoint.sh
create mode 100644 phpunit.xml
create mode 100644 plugin/CheckUpdate/CheckUpdate.php
create mode 100644 plugin/Login/Login.php
create mode 100644 profile/example/config/user.ini
create mode 100644 profile/example/device/device.yaml
rename {data => resources}/filter_library.json (99%)
create mode 100644 resources/version.json
create mode 100644 src/Api/Passport/ApiCaptcha.php
create mode 100644 src/Api/Passport/ApiLogin.php
create mode 100644 src/Api/Passport/ApiOauth2.php
create mode 100644 src/Api/PassportTv/ApiQrcode.php
create mode 100644 src/Bootstrap/Bootstrap.php
create mode 100644 src/Cache/Cache.php
create mode 100644 src/Config/Config.php
create mode 100644 src/Console/Command/AppCommand.php
create mode 100644 src/Console/Command/RestoreCommand.php
create mode 100644 src/Console/Command/ScriptCommand.php
create mode 100644 src/Console/Console.php
create mode 100644 src/Device/Device.php
create mode 100644 src/Env/Env.php
create mode 100644 src/FilterWords/FilterWords.php
create mode 100644 src/Helpers.php
create mode 100644 src/Log/Log.php
rename src/{plugin => Notice}/Notice.php (68%)
rename src/{exception/TaskException.php => Plugin/BasePlugin.php} (67%)
create mode 100644 src/Plugin/BasePluginInfo.php
rename src/{exception/SingletonException.php => Plugin/BasePluginRW.php} (65%)
create mode 100644 src/Plugin/Plugin.php
create mode 100644 src/Request/MultiRequest.php
create mode 100644 src/Request/Request.php
create mode 100644 src/Sign/Sign.php
create mode 100644 src/Task/Task.php
create mode 100644 src/TimeLock/TimeLock.php
create mode 100644 src/User/User.php
create mode 100644 src/Util/ArrayR/ArrayR.php
rename src/{tool => Util/Common}/Common.php (81%)
create mode 100644 src/Util/DesignPattern/SingleTon.php
rename src/{script/DelDynamic.php => Util/Exceptions/GetConfigException.php} (53%)
create mode 100644 src/Util/Exceptions/HttpClientInitException.php
create mode 100644 src/Util/Exceptions/MethodNotFoundException.php
rename src/{plugins/Example/Example.php => Util/Exceptions/ResponseEmptyException.php} (51%)
rename src/{tool/Generator.php => Util/Fake/Fake.php} (68%)
rename src/{tool => Util/Fake}/Faker.php (99%)
create mode 100644 src/Util/Os/Path.php
create mode 100644 src/Util/Qrcode/Lib/Constants.php
create mode 100644 src/Util/Qrcode/Lib/FrameFiller.php
create mode 100644 src/Util/Qrcode/Lib/QrBitStream.php
create mode 100644 src/Util/Qrcode/Lib/QrCode.php
create mode 100644 src/Util/Qrcode/Lib/QrEncode.php
create mode 100644 src/Util/Qrcode/Lib/QrImage.php
create mode 100644 src/Util/Qrcode/Lib/QrInput.php
create mode 100644 src/Util/Qrcode/Lib/QrInputItem.php
create mode 100644 src/Util/Qrcode/Lib/QrMask.php
create mode 100644 src/Util/Qrcode/Lib/QrRawCode.php
create mode 100644 src/Util/Qrcode/Lib/QrRs.php
create mode 100644 src/Util/Qrcode/Lib/QrRsBlock.php
create mode 100644 src/Util/Qrcode/Lib/QrRsItem.php
create mode 100644 src/Util/Qrcode/Lib/QrSpec.php
create mode 100644 src/Util/Qrcode/Lib/QrSplit.php
create mode 100644 src/Util/Qrcode/Lib/QrStr.php
create mode 100644 src/Util/Qrcode/Lib/QrTools.php
create mode 100644 src/Util/Qrcode/Qrcode.php
create mode 100644 src/Util/Qrcode/VERSION
create mode 100644 src/Util/Resource/BaseResource.php
create mode 100644 src/Util/Resource/BaseResourcePoly.php
create mode 100644 src/Util/Resource/Collection.php
create mode 100644 src/Util/Resource/CollectionInterface.php
create mode 100644 src/Util/Resource/Resource.php
delete mode 100644 src/core/App.php
delete mode 100644 src/core/BCommand.php
delete mode 100644 src/core/Cache.php
delete mode 100644 src/core/Config.php
create mode 100644 src/core/Core.php
delete mode 100644 src/core/Curl.php
delete mode 100644 src/core/Device.php
delete mode 100644 src/core/Env.php
delete mode 100644 src/core/Helpers.php
delete mode 100644 src/core/HttpClient.php
delete mode 100644 src/core/Log.php
delete mode 100644 src/core/Task.php
delete mode 100644 src/plugin/ActivityLottery.php
delete mode 100644 src/plugin/AloneTcpClient.php
delete mode 100644 src/plugin/AnchorRaffle.php
delete mode 100644 src/plugin/AwardRecord.php
delete mode 100644 src/plugin/Barrage.php
delete mode 100644 src/plugin/BpConsumption.php
delete mode 100644 src/plugin/CapsuleLottery.php
delete mode 100644 src/plugin/CheckUpdate.php
delete mode 100644 src/plugin/Competition.php
delete mode 100644 src/plugin/DailyBag.php
delete mode 100644 src/plugin/DailyTask.php
delete mode 100644 src/plugin/DataTreating.php
delete mode 100644 src/plugin/DoubleHeart.php
delete mode 100644 src/plugin/Dynamic.php
delete mode 100644 src/plugin/Forward.php
delete mode 100644 src/plugin/GiftHeart.php
delete mode 100644 src/plugin/GiftRaffle.php
delete mode 100644 src/plugin/GiftSend.php
delete mode 100644 src/plugin/GroupSignIn.php
delete mode 100644 src/plugin/GuardRaffle.php
delete mode 100644 src/plugin/Judge.php
delete mode 100644 src/plugin/Live.php
delete mode 100644 src/plugin/LiveReservation.php
delete mode 100644 src/plugin/Login.php
delete mode 100644 src/plugin/MainSite.php
delete mode 100644 src/plugin/ManGa.php
delete mode 100644 src/plugin/MaterialObject.php
delete mode 100644 src/plugin/PkRaffle.php
delete mode 100644 src/plugin/PolishTheMedal.php
delete mode 100644 src/plugin/RedPocketRaffle.php
delete mode 100644 src/plugin/Schedule.php
delete mode 100644 src/plugin/Sign.php
delete mode 100644 src/plugin/Silver.php
delete mode 100644 src/plugin/Silver2Coin.php
delete mode 100644 src/plugin/SmallHeart.php
delete mode 100644 src/plugin/Statistics.php
delete mode 100644 src/plugin/StormRaffle.php
delete mode 100644 src/plugin/User.php
delete mode 100644 src/plugin/VipPrivilege.php
delete mode 100644 src/plugin/ZoneTcpClient.php
delete mode 100644 src/plugins/BasePlugin.php
delete mode 100644 src/plugins/Plugins.php
delete mode 100644 src/script/BaseTask.php
delete mode 100644 src/script/UnFollow.php
delete mode 100644 src/script/User.php
delete mode 100644 src/tool/ArrayToTextTable.php
delete mode 100644 src/tool/BvToAv.php
delete mode 100644 src/tool/DumpMemory.php
delete mode 100644 src/tool/File.php
delete mode 100644 src/tool/Path.php
delete mode 100644 src/tool/UserAgent.php
delete mode 100644 src/util/AllotTasks.php
delete mode 100644 src/util/BaseRaffle.php
delete mode 100644 src/util/FilterWords.php
delete mode 100644 src/util/Singleton.php
delete mode 100644 src/util/TimeLock.php
delete mode 100644 src/util/XliveHeartBeat.php
delete mode 100644 tests/ArrayToTextTableTest.php
delete mode 100644 tool/ConfigGenerator.php
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index acbcfd4..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: [ 'https://i.loli.net/2019/07/13/5d2963e5cc1eb22973.png' ]
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/Issue_Template_CN.md b/.github/ISSUE_TEMPLATE/Issue_Template_CN.md
deleted file mode 100644
index ce2e41c..0000000
--- a/.github/ISSUE_TEMPLATE/Issue_Template_CN.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Issue: BiliHepler 程序问题
-
-
-
-### 版本、安装方式、系统
-
-1. 你在使用什么版本的 BiliHelper
-
-
-2. 你通过什么方式安装 BiliHepler
-
-
-3. 你所使用的操作系统
-
----
-
-### 描述问题
-
-
-
-
----
-
-### 复现问题的步骤
-
-
-
-
----
-
-### BiliHelper 运行日志:
-
-
-
-```shell
-# 此处贴入运行日志
-
-
-
-```
-
----
-
-### 截图
-
-
-
diff --git a/.github/ISSUE_TEMPLATE/Issue_Template_EN.md b/.github/ISSUE_TEMPLATE/Issue_Template_EN.md
deleted file mode 100644
index 081305c..0000000
--- a/.github/ISSUE_TEMPLATE/Issue_Template_EN.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Issue: Bug Report
-
-
-
-### Version, approach of installation, operating system
-
-1. What version of BiliHelper are you using?
-
-
-2. What approach of installation do you install BiliHelper?
-
-
-3. What is your operating system?
-
----
-
-### Description
-
-
-
-
----
-
-### Steps to reproduce
-
-
-
-
----
-
-### BiliHelper Logs:
-
-
-
-```shell
-# Paste running logs here
-
-
-
-```
-
----
-
-### Screenshot
-
-
-
diff --git a/.github/workflows/build_image_bhp.yml b/.github/workflows/build_image_bhp.yml
deleted file mode 100644
index 1bf3977..0000000
--- a/.github/workflows/build_image_bhp.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: "BiliHelper-personal Docker Image Buildx Stable Github"
-on:
- workflow_dispatch:
-
-jobs:
- build:
- runs-on: ubuntu-latest
- name: Build BiliHelper-personal Docker Image Build Stable Github
- steps:
- - name: Checkout master
- uses: actions/checkout@master
- - name: Build and publish image
- uses: ilteoood/docker_buildx@master
- with:
- publish: true
- imageName: lkeme/bilihelper-personal
- dockerFile: docker/Dockerfile
- platform: linux/amd64,linux/arm64,linux/arm/v7
- # platform: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
- tag: latest
- dockerUser: ${{ secrets.DOCKER_USERNAME }}
- dockerPassword: ${{ secrets.DOCKER_PASSWORD }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d302d7c..863f002 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,41 +5,30 @@
# IntelliJ project files
.idea
.idea/
-index1.php
-user/
-record/
temp/
tmp/
*.iml
out
gen
-/vendor/
composer.lock
-/configs/
-config
-/conf/user*.ini
-*.log
Traits/
README1.md
-conf/user.ini
-conf/user1.ini
-/conf/user.ini
-/conf/test.ini
-/conf/test1.ini
-/src/backup/
-script.php
-task/*
-cache/*
-log/*
+script.php
+test.php
+index1.php
# ignore all files in lib/
-#task/*
-#cache/*
-#log/*
+
# except for .gitkeep
!.gitkeep
# ignore TODO file in root directory,not subdir/TODO
+
/TODO
/Todo
+/backup
+/vendor
+# 保留忽略目录下的指定文件夹
+/profile/*
+!/profile/example
diff --git a/ABOUT.md b/ABOUT.md
index 50fdb24..8304748 100644
--- a/ABOUT.md
+++ b/ABOUT.md
@@ -1 +1,2 @@
-哔哩哔哩(Bilibili)B 站主站助手、直播助手、直播抽奖、挂机升级、贴心小棉袄脚本、Lv6 离你仅有一步之遥~ - PHP 版(Personal)
\ No newline at end of file
+哔哩哔哩(Bilibili)B 站主站助手、直播助手、直播抽奖、挂机升级、贴心小棉袄脚本、Lv6 离你仅有一步之遥~ - PHP 版(Personal)
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index f4dac13..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,843 +0,0 @@
-# Release Notes
-
-# 本项目Log
-
-[comment]: <> ()
-
-[comment]: <> ( 更新历史latest
)
-
-[comment]: <> ( )
-
-## v1.1.0.220218 alpha (2022-02-18)
-
-### Added
-
--
-
-### Changed
-
--
-
-### Fixed
-
--
-
-### Remarks
-
-- 注意文档文件、配置文件有些许改动
--
-
-## v1.0.2.211115 alpha (2021-11-15)
-
-### Added
-
-- 添加直播预约有奖
-- 添加企业微信群机器人推送
-- 添加企业微信应用消息推送
--
-
-### Changed
-
-- 清理部分无效活动
-- 更新系统环境参数
-- 更新敏感关键字
--
-
-### Fixed
-
-- 修复登录未知错误
--
-
-### Remarks
-
-- 注意文档文件、配置文件有些许改动
-- 注意需要重新进行`composer update`操作
-- 注意只支持PHP8.* 只支持PHP8.* 只支持PHP8.*
--
-
-## v1.0.1.211014 alpha (2021-10-14)
-
-### Added
-
--
-
-### Changed
-
-- 更新部分活动
-- 更新系统环境参数
--
-
-### Fixed
-
-- 修复中文用户名运行失败
-- 修复风纪(未完善,慎用)
--
-
-### Remarks
-
-- 注意文档文件有些许改动
-- 注意需要重新进行`composer update`操作
-- 注意只支持PHP8.* 只支持PHP8.* 只支持PHP8.*
--
-
-## v1.0.0.210822 alpha (2021-08-22)
-
-### Added
-
-- 支持自定义设备
-- 全面支持PHP8.0
-- 支持本地缓存
-- 对使用体验进行了一轮优化并解决了一些bug~~
--
-
-### Changed
-
-- PHP版本提升值8.*, 不向下兼容
-- 对使用体验进行了一轮优化并解决了一些bug~~
--
-
-### Fixed
-
-- 对使用体验进行了一轮优化并解决了一些bug~~
--
-
-### Remarks
-
-- 注意文档文件有些许改动
-- 注意需要重新进行`composer update`操作
-- 注意只支持PHP8.* 只支持PHP8.* 只支持PHP8.*
--
-
-## v0.9.9.210807 alpha (2021-08-07)
-
-### Added
-
--
-
-### Changed
-
-- 更新基础环境参数
--
-
-### Fixed
-
-- 修复独立监控内存溢出
-- 修复天选之人统计数据不正确
-- 修复扭蛋活动只观看第一个任务
-- 修复脚本模式部分设备环境不兼容
-- 修复清理动态错误
--
-
-## v0.9.7.210714 alpha (2021-07-14)
-
-### Added
-
-- 添加脚本模式 `php index.php -?`
--
-
-### Changed
-
-- 省略
--
-
-### Fixed
-
-- 省略
--
-
-### Remarks
-
-- 注意配置文件有些许改动
-- 注意需要重新进行`composer update`操作
--
-
-## v0.9.6.210625 alpha (2021-06-25)
-
-### Added
-
--
-
-### Changed
-
-- 优化动态过滤
-- 优化过滤关键字
-- 优化短信登录流程
--
-
-### Fixed
-
-- 修复动态转发
--
-
-### Remarks
-
-- 注意配置文件有些许改动
--
-
-## v0.9.5.210624 alpha (2021-06-24)
-
-### Added
-
--
-
-### Changed
-
-- 更新动态转发
-- 更新基础环境参数
--
-
-### Fixed
-
--
-
-### Remarks
-
-- 注意配置文件有些许改动
--
-
-## v0.9.4.210623 alpha (2021-06-23)
-
-### Added
-
--
-
-### Changed
-
-- 更新部分仓库文件
-- 更新基础环境参数
--
-
-### Fixed
-
-- 修复PC端银瓜子兑换硬币
-- 修复主站投币任务
-- 修复点亮勋章特殊情况
--
-
-### Remarks
-
--
-
-## v0.9.3.210616 alpha (2021-06-16)
-
-### Added
-
-- 任务排程
--
-
-### Changed
-
-- 配置热更新
--
-
-### Fixed
-
-- 修复弹幕
-- 修复独立监控
--
-
-### Remarks
-
--
-
-## v0.9.2.210602 alpha (2021-06-02)
-
-### Added
-
--
-
-### Changed
-
-- 更新DcokerFile
-- 更新每日点亮勋章(100亲密度)
--
-
-### Fixed
-
--
-
-### Remarks
-
--
-
-## v0.9.1.210518 alpha (2021-05-18)
-
-### Added
-
--
-
-### Changed
-
-- 删除过期活动
-- 更新風機委員
-- 更新银瓜子兑换硬币
-- 更新点亮勋章
-- 更新部分日志打印输出
--
-
-### Fixed
-
-- 修复已知BUG
--
-
-### Remarks
-
--
-
-## v0.9.0.210517 alpha (2021-05-17)
-
-### Added
-
-- 重大更新
--
-
-### Changed
-
-- 懒得写描述
--
-
-### Fixed
-
-- 配置文件有变动
--
-
-### Remarks
-
-- 请重新配置
--
-
-## v0.8.1.210423 alpha (2021-04-23)
-
-### Added
--
-
-### Changed
-
-- 更新天选敏感词
-- 更新活动列表
-- 优化直播间心跳
--
-
-### Fixed
--
-
-### Remarks
--
-
-## v0.8.0.210327 alpha (2021-03-27)
-
-### Added
-
-- 增加直播扭蛋抽奖活动(可自定义)
-- 增加主站九宫格抽奖活动(可自定义)
-- 增加多个推送消息通道
--
-
-### Changed
-
-- 更新过滤词独立
-- 更新请求中心
-- 更新日志打印
-- 更新基础库
-- 更新环境读取流程
-- 更新部分配置
-- 更新登录请求
-- 更新实物抽奖&天选抽奖
-- 支持PHP8.0+
-- 更新弹幕抽奖
-- 更新每日任务
-- 更新部分任务架构
-- 引入必要新包
--
-
-### Fixed
-
-- 修复小心心心跳错误
-- 修复部分推送错误
-- 修复银瓜子换银币日志错误
--
-
-### Remarks
-
-- 结构大更新,务必进行Composer等操作
--
-
-## v0.6.7.201117 alpha (2020-11-17)
-
-### Added
--
-
-### Changed
-
-- 调整WS读取数据大小
-- 调整弹幕发送时间
-- 更新天选之子日志打印
-- 更新基础库
--
-
-### Fixed
-
-- 修复小心心心跳错误
--
-
-## v0.6.5.200808 alpha (2020-08-08)
-
-### Added
-
-- 添加小心心支持
-- 添加活动抽奖
--
-
-### Changed
-
-- 去掉无效双端观看
-- 过期小心心赠送
-
-### Fixed
-
-- 修复小心心心跳错误
--
-
-## v0.6.0.200730 alpha (2020-07-30)
-
-### Added
-
-- 添加小心心支持
--
-
-### Changed
-
-- 更新天选过滤关键词
--
-
-### Fixed
-
-- 修复节奏风暴逻辑错误
--
-
-## v0.5.0.200625 alpha (2020-06-25)
-
-### Added
-
-- 添加破产机(赔到破产)
-- 添加推送KEY到期通知
-- 添加天选自动取关(测试)
-- 添加收益统计
-- 添加关注投币模式
--
-
-### Changed
-
-- 更新部分信息输出
-- 修改日志打印前缀
-- 更新活跃弹幕延迟
-- 重构部分统计代码
-- 更新视频投币逻辑
-- 更新实物抽奖API
-- 修改硬币兑换延迟
-- 重构部分登录逻辑
--
-
-### Fixed
-
-- 修复日志输出错误
-- 修复每日送礼异常
-- 修复赛事逻辑错误
-- 修复部分已知问题
--
-
-## v0.4.0.200505 alpha (2020-05-05)
-
-### Added
--
-
-### Changed
-
-- 关闭已结束的LPL赛事任务
--
-
-### Fixed
-
-- 修复日志回调空格解析异常
-- 修复节奏风暴回显过滤错误
-- 修复活动抽奖重复请求问题
-- 修复主站任务获取AID异常
--
-
-## v0.4.0.200428 alpha (2020-04-28)
-
-### Added
--
-
-### Changed
--
-
-### Fixed
-
-- 修复赠送礼物逻辑
--
-
-## v0.4.0.200426 alpha (2020-04-26)
-
-### Added
-
-- 赛事助手 (签到、分享)
--
-
-### Changed
--
-
-### Fixed
-
-- 修复休眠机制
--
-
-## v0.3.0.200425 alpha (2020-04-25)
-
-### Added
-
-- 添加调用函数
--
-
-### Changed
-
-- 取消一处请求头
--
-
-### Fixed
-
-- 修复休眠机制
--
-
-## v0.3.0.200424 alpha (2020-04-24)
-
-### Added
-
-- 抽出独立小黑屋
-- 提前引入BV2AV
--
-
-### Changed
-
-- 优化监控推送
-- 优化登录参数
-- 同步黑屋提醒
-
-### Fixed
-
-- 修复监控输出
--
-
-## v0.3.0.200421 alpha (2020-04-21)
-
-### Added
--
-
-### Changed
--
-
-### Fixed
-
-- 修复节奏风暴回显
-- 修复分区监控异常
-- 修复获取勋章异常
--
-
-## v0.3.0.200419 alpha (2020-04-19)
-
-### Added
--
-
-### Changed
-
-- 去除主监控
--
-
-### Fixed
-
-- 修复风暴回显过滤
-- 修复一处舰长处理
-- 修复中奖记录通知
-- 修复分区监控异常
-- 修复获取勋章异常
--
-
-## v0.3.0.200410 alpha (2020-04-10)
-
-### Added
--
-
-### Changed
--
-
-### Fixed
-
-- 修复备用监控无法获取
-- 修复获取分区ID异常
--
-
-## v0.3.0.200404 alpha (2020-04-04)
-
-### Added
-
-- 添加ISSUE模板
-- 添加处理监控端命令
--
-
-### Changed
--
-
-### Fixed
-
-- 修复主监控内存溢出
-- 修复活动礼物提醒
--
-
-## v0.3.0.200318 alpha (2020-03-18)
-
-### Added
-
-- 添加日常任务定时执行(AM10:00)
--
-
-### Changed
-
-- Sleep->Schedule
-- 优化异常通知
-- 优化异常重试数
-- 优化弹幕获取
-- 优化银瓜子宝箱
--
-
-### Fixed
-
-- 修复日志回调
-- 修复response为null
-- 修复PC端心跳
-- 修复部分已知
--
-
-## v0.3.0.200316 alpha (2020-03-16)
-
-### Added
-
-- 添加异常处理(通知)
--
-
-### Changed
--
-
-### Fixed
-
-- 修复活跃弹幕
-- 修复风纪测试
-- 修复部分已知
--
-
-## v0.3.0.200312 alpha (2020-03-12)
-
-### Added
-
-- 引入风纪 (不稳定测试)
-- 引入新库 (需要重新Composer)
--
-
-### Changed
-
-- 重构部分公用方法
-- 重构CURL请求
--
-
-### Fixed
-
-- 修复登录繁忙
-- 修复实物抽奖
-- 修复日志输出
-- 修复部分已知
--
-
-## v0.2.0.200226 alpha (2020-02-26)
-
-### Added
--
-
-### Changed
--
-
-### Fixed
-
-- 修复节奏风暴
-- 修复双端心跳
--
-
-## v0.2.0.200224 alpha (2020-02-24)
-
-### Added
-
-- 新增工具类
-- 引入新库(需要重新Composer)
--
-
-### Changed
-
-- 优化数据过滤条件
-- 更新PC端心跳API
-- 优化实物抽奖
-- 优化运行条件
--
-
-### Fixed
-
-- 修复内存异常
-- 修复重复投币
--
-
-## v0.2.0.200214 alpha (2020-02-14)
-
-### Added
--
-
-### Changed
-
-- 优化实物抽奖流程
-- 优化延迟礼物抽奖
-
-### Fixed
-
-- 修正部分函数名称
-- 尝试减少静态占用
--
-
-## v0.2.0.200208 alpha (2020-02-08)
-
-### Added
-
-- 哔哩哔哩漫画助手(可选)
--
-
-### Changed
-
-- 补充部分过滤关键字
--
-
-### Fixed
-
-- 修复一处内存异常
--
-
-## v0.1.0.200111 alpha (2020-01-11)
-
-### Added
-
-- 天选时刻奖品过滤
--
-
-### Changed
--
-
-### Fixed
--
-
-## v0.1.0.200128 alpha (2020-01-28)
-
-### Added
-
-- 增加推送消息过滤
--
-
-### Changed
--
-
-### Fixed
-
-- 修复抽奖推送错误
--
-
-## v0.1.0.200111 alpha (2020-01-11)
-
-### Added
-
-- 天选时刻奖品过滤
--
-
-### Changed
--
-
-### Fixed
--
-
-## v0.1.0.200103 alpha (2020-01-03)
-
-### Added
-
-- 增加并发请求
--
-
-### Changed
-
-- 更新抽奖逻辑
-- 更新部分API
-- 减少重复请求
--
-
-### Fixed
-
-- 修复部分回显错误
--
-
-## v0.1.0.200101 alpha (2020-01-01)
-
-### Added
-
-- 任务逻辑引入协程
-
-### Changed
--
-
-### Fixed
-
-- 修复礼物赠送异常
--
-
-## v0.1.0.191227 alpha (2019-12-27)
-
-### Added
-
-- 新增备用官方分区监控
-- 新增按勋章亲密度赠送
-- 新增天选时刻获奖推送
--
-
-### Changed
-
-- 优化部分架构
-- 优化独立监控
-- 优化监控逻辑
-- 优化礼物赠送
-- 优化通用抽奖
-- 优化推送逻辑
--
-
-### Fixed
-
-- 修复部分日志显示
-- 修复数据统计异常
-- 修复瓜子宝箱异常
-- 修复Server酱异常
-- 修复天选时刻异常
-- 修复抽奖数据异常
-- 修复部分已知BUG
-- 修复获奖推送通知
-- 修复休眠中心异常
-- 修复礼物赠送异常
--
-
-## v0.0.5.191223 alpha (2019-12-23)
-
-## v0.0.4.191030 alpha (2019-10-30)
-
-## v0.0.3.190730 alpha (2019-07-30)
-
-## v0.0.2.190723 alpha (2019-07-23)
-
-## v0.0.1.190713 alpha (2019-07-13)
diff --git a/DOC.md b/DOC.md
deleted file mode 100644
index d50f5ad..0000000
--- a/DOC.md
+++ /dev/null
@@ -1,338 +0,0 @@
-

-
-[comment]: <> (
)
-
-
-
-[](https://github.com/lkeme/ )
-
-
-
-
-
-
-
-## 环境依赖
-
-通常使用 `composer` 工具会自动检测以下依赖问题。
-
-|Requirement |
-|--------------------|
-|PHP >=8.0 |
-|php_curl |
-|php_sockets |
-|php_openssl |
-|php_json |
-|php_zlib |
-|php_mbstring |
-
-## Composer
-
-+ [Composer 安装与使用](https://www.runoob.com/w3cnote/composer-install-and-usage.html)
-
-+ [Composer 下载](https://getcomposer.org/download/)
-
-+ 当前项目 `composer.lock` 基于阿里云 Composer镜像生成
-
-+ 阿里云(全量镜像)
-
-```shell script
-# 使用帮助
-> https://developer.aliyun.com/composer
-# 使用命令
-> composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
-```
-
-+ 恢复默认镜像|Composer.phar加速下载
-
-```shell script
-> composer config -g --unset repos.packagist
-
-> https://mirrors.cloud.tencent.com/composer/composer.phar
-> https://mirrors.aliyun.com/composer/composer.phar
-```
-
-
-其余镜像 展开查看
-
-+ cnpkg(全量镜像)
-```shell script
-# 使用帮助
-> https://php.cnpkg.org/
-# 使用命令
-> composer config -g repos.packagist composer https://php.cnpkg.org
-```
-
-+ 腾讯云(全量镜像)
-
-```shell script
-# 使用帮助
-> https://mirrors.cloud.tencent.com/help/composer.html
-# 使用命令
-> composer config -g repos.packagist composer https://mirrors.cloud.tencent.com/composer/
-```
-
-+ PhpComposer(全量镜像)
-
-```shell script
-# 使用帮助
-> https://pkg.phpcomposer.com/
-# 使用命令
-> composer config -g repo.packagist composer https://packagist.phpcomposer.com
-```
-
-+ 华为云(全量镜像)
-
-```shell script
-# 使用帮助
-> https://mirrors.huaweicloud.com/repository/php/
-# 使用命令
-> composer config -g repos.packagist composer https://mirrors.huaweicloud.com/repository/php/
-```
-
-+ 交通大学(非全量镜像)
-
-```shell script
-# 使用帮助
-> https://packagist.mirrors.sjtug.sjtu.edu.cn/
-# 使用命令
-> composer config -g repos.packagist composer https://packagist.mirrors.sjtug.sjtu.edu.cn
-```
-
-
-
-
-## 使用指南
-
-1. 下载(克隆)项目代码,初始化项目
-
-```shell script
-$ git clone https://github.com/lkeme/BiliHelper-personal.git
-$ cd BiliHelper-personal/conf
-$ cp user.ini.example user.ini
-```
-
-2. 使用 [composer](https://getcomposer.org/download/) 工具进行安装
-
-```shell script
-$ composer install
-```
-
-[comment]: <> (composer dump-autoload (-o))
-
-[comment]: <> (composer dumpautoload (-o))
-
-3. 按照说明修改配置文件 `user.ini`
-
- ```shell script
- # 默认只需填写帐号密码,按需求开启其他功能即可
- ...
- ```
-
-4. 运行测试
-
-```shell script
-$ php index.php
-```
-
-> 以下是`多账户多开方案`,单个账户可以无视
-
-5. 复制一份example配置文件,修改账号密码即可
-
- ```shell script
- $ php index.php example.ini
- ```
-
-6. 自定义设备方案
-
- ```shell script
- $ cd conf
- $ cp bili.yaml user_bili.yaml
- $ cp device.yaml user_device.yaml
- ```
-
-7. 命令模式
-
-```shell script
-# 获取所有命令
-$ php index.php -?
-```
-
-8. 请保证配置文件存在,否则默认加载`user.ini`配置文件
-
-
-
-[comment]: <> (
)
-
-## Docker使用指南
-
-1. 安装好[Docker](https://yeasy.gitbooks.io/docker_practice/content/install/)
-2. 直接命令行拉取镜像后运行
-
-### 传入的参数方式有两种(二选一,如果同时传入则优先选择配置文件)
-
-- 通过环境变量进行传入
-
-```shell script
-$ docker run -itd --rm -e USER_NAME=你的B站登录账号 -e USER_PASSWORD=你的B站密码 lkeme/bilihelper-personal
-```
-
-- 通过配置文件进行传入(能保留登录状态,自定义配置)
-
-1. 下载[配置文件](https://raw.githubusercontent.com/lkeme/BiliHelper-personal/master/conf/user.ini.example)
-2. 修改
-3. 通过下面的命令进行挂载并运行
-
-```shell script
-$ docker run -itd --rm -v /path/to/your/confFileName.ini:/app/conf/user.ini lkeme/bilihelper-personal
-```
-
-- 使用github镜像加速
-
-```shell script
-$ -e MIRRORS=0 # 使用 github.com
-$ -e MIRRORS=1 # 使用 ghproxy.com
-$ -e MIRRORS=2 # 使用 fastgit.org
-$ -e MIRRORS=3 # 使用 hub.gitfast.tk
-$ -e MIRRORS=4 # 使用 hub.gitslow.tk
-$ -e MIRRORS=5 # 使用 hub.verge.tk
-$ -e MIRRORS=6 # 使用 gh.api.99988866.xyz
-$ -e MIRRORS=custom -e CUSTOM_CLONE_URL=https://github.com/lkeme/BiliHelper-personal.git # 使用 自定义克隆地址
-```
-
-- 相关参数
-
-```ps
- -it 前台运行
- -itd 后台运行
- -v 本地文件:容器内部文件 ==> 挂载本地文件到容器中。本地文件路径随便变,容器内部文件路径不能变。
-```
-
-- -v模式使用短信登录
-
-```
-配置文件里设置好,发送完短信
-docker attach 或者docker exec 再进去容器里输入
-```
-
-- 注意: Docker镜像已经包含了所有所需的运行环境,无需在本地环境弄composer。每次启动容器时,都会与项目进行同步以确保版本最新。
-
-## 升级指南
-
-> 注意新版本的配置文件是否变动,则需要重新覆盖配置文件,并重新填写设置
-
-1. 进入项目目录
-
-```
-$ cd BiliHelper-personal
-```
-
-2. 拉取最新代码
-
-```
-$ git pull
-```
-
-3. 更新依赖库
-
-```
-$ composer install
-```
-
-4. 如果使用 systemd 等,需要重启服务
-
-```
-$ systemctl restart bilibili
-```
-
-## 部署指南
-
-如果你将 BiliHelper-personal 部署到线上服务器时,则需要配置一个进程监控器来监测 `php index.php` 命令,在它意外退出时自动重启。
-
-通常可以使用以下的方式
-
-- systemd (推荐)
-- Supervisor
-- screen (自用)
-- nohup
-
-## systemd 脚本
-
-```
-# /usr/lib/systemd/system/bilibili.service
-
-[Unit]
-Description=BiliHelper Manager
-Documentation=https://github.com/lkeme/BiliHelper-personal
-After=network.target
-
-[Service]
-ExecStart=/usr/bin/php /path/to/your/BiliHelper-personal/index.php
-Restart=always
-
-[Install]
-WantedBy=multi-user.target
-```
-
-## Supervisor 配置
-
-```
-[program:bilibili]
-process_name=%(program_name)s
-command=php /path/to/your/BiliHelper-personal/index.php
-autostart=true
-autorestart=true
-redirect_stderr=true
-stdout_logfile=/tmp/bilibili.log
-```
-
-## 报错通知问题
-
-脚本出现 error 级别的报错,会调用通知地址进行提醒,这里推荐两个服务
-
-|服务|官网|
-|---|---|
-|Server酱|https://sc.ftqq.com/|
-|TelegramBot|https://core.telegram.org/bots/api|
-
-示范如下
-
-```
-# Server酱
-# 自行替换
-APP_CALLBACK="https://sc.ftqq.com/.send?text={message}"
-
-# TelegramBot
-# 自行替换
-APP_CALLBACK="https://api.telegram.org/bot/sendMessage?chat_id=&text={message}"
-```
-
-`{message}` 部分会自动替换成错误信息,接口采用 get 方式发送
-
-## 直播间 ID 问题
-
-文件 `user.ini` 里
-
-`ROOM_ID` 配置,填写此项可以清空临过期礼物给指定直播间。
-
-`ROOM_LIST` 配置,使用长位直播间,填写此项可以清空临礼物给指定有勋章的直播间。
-
-`FEED_FILL` 配置,搭配上一条使用,使用过期礼物或者倒序使用正常礼物。
-
-`SOCKET_ROOM_ID` 配置,监控使用,暂时没用到。
-
-通常可以在直播间页面的 url 获取到它
-
-```
-http://live.bilibili.com/9522051
-```
-
-长位直播间ID获取
-
-```
-https://api.live.bilibili.com/room/v1/Room/room_init?id=3
-```
-
-所有直播间号码小于 1000 的直播间为短号,部分4位直播间也为短号,
-
-该脚本在每次启动会自动修正部分功能,特殊标注的请留意。
diff --git a/NOTIFY.md b/NOTIFY.md
deleted file mode 100644
index c86cc33..0000000
--- a/NOTIFY.md
+++ /dev/null
@@ -1,132 +0,0 @@
-## 关于推送
-
-### 开关
-
-```ini
-[notify]
-enable = false
-filter_words =
-```
-
-### 推送单元
-
-> 以下数据为示例(e.g.),需要根据实际需求配置
-
-**钉钉**
-
-> 文档: https://developers.dingtalk.com/document/robots/custom-robot-access
-> 说明: 钉钉推送的密钥
-
-```ini
-; Dingtalk机器人|token|依赖USE_NOTIFY
-[notify.dingtalk]
-token = 566cc69da782ec****
-```
-
-**Telegram**
-
-> 文档: https://core.telegram.org/bots/api#sendmessage
-> 说明: 如果开启 TGbot API 反代,填写url,否则为空使用默认api。
-> 说明: TG 推送的Token, xxx/bot{这是token部分}/xxxx
-> 说明: TG 推送的用户/群组/频道 ID
-
-```ini
-; Tele机器人|url(可选)|token|chatid|依赖USE_NOTIFY
-[notify.telegram]
-url = https://*.*.workers.dev/bot
-bottoken = 1640****:AAGlV3****_FscZ-****
-chatid = 390****
-```
-
-**PUSH PLUS**
-
-> 文档: http://www.pushplus.plus/doc/
-> 说明: push plus++ 推送的 `token`
-
-```ini
-; Pushplus酱|token|依赖USE_NOTIFY
-[notify.pushplus]
-token = 566cc69da782ec****
-```
-
-**Sever酱(原版)**
-
-> 文档: https://sc.ftqq.com/
-> 说明: Server 酱老版本 key,SCU 开头的
-
-```ini
-; Sever酱(原版)|令牌Key|依赖USE_NOTIFY
-[notify.sc]
-sckey = SCU566cc69da782ec****
-```
-
-**Server酱(Turbo版)**
-
-> 文档: https://sct.ftqq.com/
-> 说明: Server 酱 Turbo 版本 key,SCT 开头的
-
-```ini
-; Server酱(Turbo版)|令牌Key|依赖USE_NOTIFY
-[notify.sct]
-sctkey = SCT566cc69da782ec****
-```
-
-**GoCqhttp**
-
-> 文档: https://docs.go-cqhttp.org/api/
-> 说明: 推送的完整api, 包含`/send_private_msg`、`/send_group_msg` 等等完整后缀
-> 说明: 推送的AccessToken
-> 说明: 目标QQ号或者QQ群号,根据API调整
-
-```ini
-; GoCqhttp|url|token|目标qq|依赖USE_NOTIFY
-[notify.gocqhttp]
-url = "http://127.0.0.1:5700/send_private_msg"
-token = 566cc69da782ec****
-target_qq = 10086
-```
-
-**Debug(个人用)**
-
-> 文档: https://localhost:8921/doc
-
-```ini
-; Debug|个人调试推送|url|token|
-[notify.debug]
-url = "https://localhost:8921/notify"
-token = 566cc69da782ec****
-```
-
-**企业微信群机器人**
-
-> 文档: https://open.work.weixin.qq.com/api/doc/90000/90136/91770 | https://weibanzhushou.com/blog/330
-> 说明: 推送的AccessToken
-
-```ini
-; 企业微信群机器人|token
-[notify.we_com]
-token = ec971f1d-****-4700-****-d9461e76****
-```
-
-**企业微信应用**
-
-> 文档: https://open.work.weixin.qq.com/wwopen/devtool/interface?doc_id=10167
-> 说明: 企业 id
-> 说明: 应用的凭证密钥
-> 说明: 企业应用的 id
-> 说明: 指定接收消息的成员,成员 ID 列表 默认为@all
-
-```ini
-; 企业微信应用消息|corp_id|corp_secret|agent_id|to_user
-[notify.we_com_app]
-corp_id = ****
-corp_secret = ****
-agent_id = ****
-to_user = UserId1|UserId2|UserId3
-```
-
-### 调试
-
-https://github.com/lkeme/BiliHelper-personal/blob/eb06f55fa0fa6cb07bbeffc7e85c6ac0bfaa67b3/data/latest_version.json#L8
-
-改成与线上不同的版本即可,检查新版本就会推送一次。
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index afdd59c..0000000
--- a/README.md
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-[comment]: <> (
)
-
-
-
-[](https://github.com/lkeme/ )
-
-
-
-
-
-[](https://hub.docker.com/r/lkeme/bilihelper-personal)
-
-
-
-
-
-
-
-
-
-
-
-
-## 📌公告通知
-
-代码开源,本地化99.9%,项目不收集或使用任何敏感信息,兴趣所致,一切只为学习。
-
-```notice
----- 免费的东西总是得不到人的珍惜。
----- 只有花大价钱去买到的东西,才会令人信任。
----- 本项目仅供学习交流使用,请勿用于非法用途!* 3
-```
-
-## 👤游客访问
-
-
-
-
-
-## 🎁打赏支持
-
-如果觉得本项目好用,对你有所帮助,欢迎打赏支持本项目哦。
-
-
-
-[comment]: <> ()
-
-## 🖨️相关文档
-
-有疑问一定要先看看文档或Issue里是否存在相同的问题,再考虑其他渠道咨询。
-
-[comment]: <> (:cherry_blossom: :gift: :gift_heart: :confetti_ball:)
-
-* [使用文档 / DOC.md](./DOC.md)
-* [推送文档 / DOC.md](./NOTIFY.md)
-* [更新日志 / CHANGELOG.md](./CHANGELOG.md)
-* [配置文档 / WIKI.md](https://github.com/lkeme/BiliHelper-personal/wiki/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%A6%E8%A7%A3)
-* [常见问题 / WIKI.md](https://github.com/lkeme/BiliHelper-personal/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
-
-## 💬交流反馈
-
-Group: [163955623](https://jq.qq.com/?_wv=1027&k=1QMFTUEO) | **请不要来问如何使用, 文档齐全, 仅用于BUG提交反馈**
-
-## 🧑🏭功能组件
-
-以下任务都是按设定周期自动执行,`true`为正常使用,`false`为暂停使用或抛弃。
-
-
-点击展开
-
-| plugin | status | version | cycle | description |
-|-----------------|--------|----------|-------|-------------------------|
-| CheckUpdate | true | 22.01.18 | 待整理 | 程序检查更新 |
-| Login | true | 22.01.18 | 待整理 | 账号登录、刷新、维持 |
-| Schedule | true | 22.01.18 | 待整理 | 控制插件运行周期 |
-| MainSite | true | 22.01.18 | 待整理 | 投币、观看、分享视频 (速升6级不是梦) |
-| DailyBag | true | 22.01.18 | 待整理 | 双端领取日常/周常礼包 |
-| ManGa | true | 22.01.18 | 待整理 | 漫画签到、分享 |
-| ActivityLottery | true | 22.01.18 | 待整理 | 主站活动九宫格抽奖 |
-| Competition | true | 22.01.18 | 待整理 | 游戏赛事竞猜 |
-| DoubleHeart | true | 22.01.18 | 待整理 | 双端心跳 (姥爷直播经验) |
-| DailyTask | true | 22.01.18 | 待整理 | 直播每日任务(签到、观看) |
-| Barrage | true | 22.01.18 | 待整理 | 保持活跃弹幕 |
-| Silver2Coin | true | 22.01.18 | 待整理 | 银瓜子兑换硬币 |
-| Judge | true | 22.01.18 | 待整理 | 風機委員投票 |
-| GiftSend | true | 22.01.18 | 待整理 | 礼物赠送、维持每日勋章亲密度 |
-| GroupSignIn | true | 22.01.18 | 待整理 | 友爱社签到 |
-| GiftHeart | true | 22.01.18 | 待整理 | 日常心跳每日礼包礼物 |
-| SmallHeart | true | 22.01.18 | 待整理 | 直播挂机,每日24个小心心 |
-| MaterialObject | true | 22.01.18 | 待整理 | 直播金色宝箱实物抽奖 |
-| AloneTcpClient | true | 22.01.18 | 待整理 | 作者的独立直播监控(可支持本项目哦) |
-| ZoneTcpClient | true | 22.01.18 | 待整理 | 官方的分区直播监控 |
-| StormRaffle | true | 22.01.18 | 待整理 | 直播节奏风暴抽奖、亿元 |
-| GiftRaffle | true | 22.01.18 | 待整理 | 直播礼物抽奖 |
-| PkRaffle | true | 22.01.18 | 待整理 | 直播大乱斗抽奖 |
-| GuardRaffle | true | 22.01.18 | 待整理 | 直播大航海抽奖 |
-| AnchorRaffle | true | 22.01.18 | 待整理 | 直播天选时刻抽奖 |
-| GiftRaffle | true | 22.01.18 | 待整理 | 直播礼物抽奖 |
-| AwardRecord | true | 22.01.18 | 待整理 | 最新的中奖纪录通知 |
-| Forward | true | 22.01.18 | 待整理 | 主站动态抽奖转发 |
-| CapsuleLottery | true | 22.01.18 | 待整理 | 直播扭蛋活动抽奖 |
-| PolishTheMedal | true | 22.01.18 | 待整理 | 每日自动点亮灰色勋章 |
-| CapsuleLottery | true | 22.01.18 | 待整理 | 直播扭蛋活动抽奖 |
-| VipPrivilege | true | 22.01.18 | 待整理 | 每月领取年度大会员特权(B币券、会员购优惠券) |
-| BpConsumption | true | 22.01.18 | 待整理 | 每月消费使用年度大会员特权的B币券 |
-| Statistics | true | 22.01.18 | 待整理 | 全局抽奖结果统计 |
-| LiveReservation | true | 22.01.18 | 待整理 | 直播预约有奖 |
-| Silver | false | 21.03.27 | 待整理 | 直播银瓜子自动开启宝箱 |
-
-
-
-
-## 🖥️星图
-
-[](https://starchart.cc/lkeme/BiliHelper-personal)
-[](https://starchart.cc/lkeme/BiliHelper)
-
-## 运行效果
-
-效果图不代表当前版本,请以当前最新版本运行结果为准。
-
-
-
-[comment]: <> ()
-
-## 项目相关
-
-* [BilibiliHelper](https://github.com/metowolf/BilibiliHelper)
-* [BiliHelper](https://github.com/lkeme/BiliHelper)
-* [Github](https://github.com/)
-
-## 致谢
-
-感谢 `JetBrains` 提供优秀的IDE。
-
-
-
-
-
-## License 许可证
-
-BiliHelper is under the MIT license.
-
-本项目基于 MIT 协议发布,并增加了 SATA 协议。
-
-当你使用了使用 SATA 的开源软件或文档的时候,在遵守基础许可证的前提下,你必须马不停蹄地给你所使用的开源项目 “点赞” ,比如在 GitHub 上
-star,然后你必须感谢这个帮助了你的开源项目的作者,作者信息可以在许可证头部的版权声明部分找到。
-
-本项目的所有代码文件、配置项,除另有说明外,均基于上述介绍的协议发布,具体请看分支下的 LICENSE。
-
-此处的文字仅用于说明,条款以 LICENSE 文件中的内容为准。
diff --git a/index.php b/app.php
similarity index 70%
rename from index.php
rename to app.php
index bd53619..d4c5fc4 100644
--- a/index.php
+++ b/app.php
@@ -1,31 +1,24 @@
-load($argv)
- ->inspect()
- ->start();
\ No newline at end of file
+
+$app = Bhp\Bootstrap\Bootstrap::getInstance(__DIR__,$argv);
+$app->run();
+
diff --git a/composer.json b/composer.json
index cfeae6e..b1d4753 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,46 @@
{
- "name": "lkeme/bilihelper",
+ "name": "lkeme/bilihelper-personal",
"description": "B 站自动领瓜子、直播助手、直播挂机脚本、主站助手 - PHP 版(Personal)",
"type": "project",
+ "version": "1.0.0-dev",
+ "minimum-stability": "stable",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Lkeme",
+ "email": "Useri@live.cn",
+ "homepage": "https://mudew.com",
+ "role": "Developer"
+ }
+ ],
+ "keywords": [],
+ "homepage": "https://github.com/lkeme/BiliHelper-personal",
+ "readme": "https://github.com/lkeme/BiliHelper-personal/blob/master/README.md",
+ "support": {
+ "issues": "https://github.com/lkeme/BiliHelper-personal/issues?state=open",
+ "wiki": "https://github.com/lkeme/BiliHelper-personal/wiki",
+ "source": "https://github.com/lkeme/BiliHelper-personal"
+ },
+ "repositories": [
+ {
+ "description": "阿里云(全量)/1Min",
+ "type": "composer",
+ "url": "https://mirrors.aliyun.com/composer/",
+ "canonical": false
+ },
+ {
+ "description": "华为云(全量)",
+ "type": "composer",
+ "url": "https://mirrors.huaweicloud.com/repository/php/",
+ "canonical": false
+ },
+ {
+ "description": "PhpComposer(全量)",
+ "type": "composer",
+ "url": "https://packagist.phpcomposer.com",
+ "canonical": false
+ }
+ ],
"require": {
"php": ">=8.0",
"ext-curl": "*",
@@ -10,41 +49,26 @@
"ext-json": "*",
"ext-zlib": "*",
"ext-mbstring": "*",
- "monolog/monolog": "^1.26.0",
- "bramus/monolog-colored-line-formatter": "^2.0.3",
- "clue/socket-raw": "^1.5.0",
- "amphp/amp": "^2.5.2",
- "guzzlehttp/guzzle": "^7.4.0",
- "overtrue/pinyin": "~4.0",
- "adhocore/cli": "^0.9.0",
- "lkeme/inifile": "^3.4",
- "symfony/yaml": "^6.0",
- "sven/file-config": "^3.1",
- "fire015/flintstone": "^2.3",
- "consolidation/config": "^2.0",
- "lkeme/data": "dev-master",
- "phppkg/config": "^1.1"
+ "monolog/monolog": "2.x-dev",
+ "bramus/monolog-colored-line-formatter": "dev-master",
+ "symfony/yaml": "6.1.x-dev",
+ "toolkit/stdlib": "*",
+ "adhocore/cli": "dev-main",
+ "lkeme/data": "4.x-dev",
+ "grasmash/expander": "dev-main",
+ "amphp/amp": "^2.6.2",
+ "fire015/flintstone": "dev-master",
+ "overtrue/pinyin": "dev-master",
+ "guzzlehttp/guzzle": "^7.4",
+ "toolkit/pflag": "^2.0",
+ "symfony/console": "^6.1"
},
- "license": "MIT",
- "authors": [
- {
- "name": "Lkeme",
- "email": "Useri@live.cn",
- "homepage": "https://mudew.com"
- }
- ],
"autoload": {
"psr-4": {
- "BiliHelper\\Core\\": "src/core",
- "BiliHelper\\Plugin\\": "src/plugin",
- "BiliHelper\\Plugins\\": "src/plugins",
- "BiliHelper\\Util\\": "src/util",
- "BiliHelper\\Tool\\": "src/tool",
- "BiliHelper\\Script\\": "src/script",
- "BiliHelper\\Exceptions\\": "src/exception"
+ "Bhp\\": "src"
},
"files": [
- "src/core/Helpers.php"
+ "src/Helpers.php"
]
}
}
diff --git a/conf/bili.yaml b/conf/bili.yaml
deleted file mode 100644
index 2e8ce39..0000000
--- a/conf/bili.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-bili_version: 0.0.1
-
-# Android
-bili_a:
- package: "tv.danmaku.bili"
- version: "6.60.0"
- build: "6600300"
- channel: "bili"
- device: "phone"
- mobi_app: "android"
- platform: "android"
- s_locale: "zh-Hans_CN"
- c_locale: "zh-Hans_CH"
- app_key: "MWQ4YjZlN2Q0NTIzMzQzNg=="
- secret_key: "NTYwYzUyY2NkMjg4ZmVkMDQ1ODU5ZWQxOGJmZmQ5NzM"
- app_key_n: "NzgzYmJiNzI2NDQ1MWQ4Mg=="
- secret_key_n: "MjY1MzU4M2M4ODczZGVhMjY4YWI5Mzg2OTE4YjFkNjU="
-
-# IOS
-bili_i:
- app_key: "MjdlYjUzZmM5MDU4ZjhjMw=="
- secret_key: "YzJlZDUzYTc0ZWVlZmUzY2Y5OWZiZDAxZDhjOWMzNzU="
-
-# Tv
-bili_t:
- app_key: "NDQwOWUyY2U4ZmZkMTJiOA=="
- secret_key: "NTliNDNlMDRhZDY5NjVmMzQzMTkwNjJiNDc4ZjgzZGQ="
\ No newline at end of file
diff --git a/conf/device.yaml b/conf/device.yaml
deleted file mode 100644
index ce2fc22..0000000
--- a/conf/device.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-device_version: 0.0.1
-
-device:
- os: "android"
- os_ver: "7.1.2"
- model: "MuMu"
- network: "2" # 1 流量 2 WIFI
-
- other_headers:
- ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
- pc_headers:
- ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53"
- app_headers:
- ua: "Mozilla/5.0 BiliDroid/${bili_a.version} (bbcallen@gmail.com) os/${device.os} model/${device.model} mobi_app/${bili_a.mobi_app} build/${bili_a.build} channel/${bili_a.channel} innerVer/${bili_a.build} osVer/${device.os_ver} network/${device.network}"
diff --git a/conf/user.ini.example b/conf/user.ini.example
deleted file mode 100644
index 1e351ea..0000000
--- a/conf/user.ini.example
+++ /dev/null
@@ -1,313 +0,0 @@
-version = 0.0.1
-
-#######################
-# 账户设置 #
-#######################
-
-[login.account]
-; 帐号|密码
-username = ""
-password = ""
-
-; 登录模式|[1.账密模式 2.短信验证码模式 3.行为验证码模式(暂未开放)]
-[login.mode]
-mode = 1
-
-; 国家代码|mode=2触发|例:大陆86 澳大利亚61
-[login.country]
-country_code = 86
-
-; 校验|mode=2触发|校验国内手机号|国外手机号关闭校验
-[login.check]
-phone = true
-
-; UID|CSRF|令牌|(当前区域自动生成)
-[login.auth]
-uid =
-csrf =
-cookie =
-access_token =
-refresh_token =
-
-#######################
-# 功能设置 #
-#######################
-
-; 主站每日任务(每日登录、观看、投币、分享)
-[main_site]
-enable = true
-; 每日观看
-watch = true
-; 每日分享
-share = true
-; 每日视频投币|random(随机热门)/fixed(关注列表)|投币稿件数(每日任务最大5)
-add_coin = true
-add_coin_mode = random
-add_coin_num = 5
-
-; 日常/周常礼物
-[daily_bag]
-enable = true
-
-; 双端心跳
-[double_heart]
-enable = true
-
-; 友爱社签到
-[love_club]
-enable = true
-
-; 领取心跳礼物
-[gift_heart]
-enable = true
-
-; 直播每日任务|每日签到|每日登录
-[daily_task]
-enable = true
-
-; 直播箱子实物抽奖
-[live_box]
-enable = true
-
-; 直播预约有奖|UP_UID|逗号分隔
-[live_reservation]
-enable = true
-vmids = 9617619
-
-; 每日24个小心心|依赖加密心跳服务器
-[small_heart]
-enable = true
-
-; 直播扭蛋活动抽奖|依赖加密心跳服务器
-[live_capsule]
-enable = true
-
-; 点亮灰色勋章|100亲密度|每日或者只点亮灰色
-[polish_the_medal]
-enable = true
-everyday = false
-
-; 主站九宫格抽奖活动助手
-[main_activity]
-enable = true
-
-; 银瓜子兑换硬币
-[silver2coin]
-enable = true
-
-; 直播大乱斗
-[live_pk]
-enable = false
-
-; 直播舰长总督
-[live_guard]
-enable = false
-
-; 直播其他礼物
-[live_gift]
-enable = false
-
-; 直播节奏风暴|丢弃率(0-100)|尝试次数(范围值)
-[live_storm]
-enable = false
-drop_rate = 0
-attempt = "5,10"
-
-; 直播天选时刻|抽取类型(0: 无限制; 1: 关注主播; 2: 粉丝勋章; 3大航海; 4用户等级;5主站等级)|自动取关(测试功能)依赖于award_record|过滤关键词|逗号分隔
-[live_anchor]
-enable = false
-limit_type = "0,1"
-auto_unfollow = true
-filter_words =
-
-; 获取最新的中奖纪录
-[award_record]
-enable = true
-
-; 活跃弹幕|弹幕房间(为空则随机)|弹幕内容(为空则随机)
-[barrage]
-enable = true
-room_id = 9522051
-content =
-
-; 漫画助手
-[manga]
-enable = false
-
-; 風機委員投票|需要实名认证|慎用
-[judgement]
-enable = false
-auto_apply = false
-
-; 大会员权益|年度大会员专享
-[vip_privilege]
-enable = false
-
-; B币券消费|年度大会员专享|每月默认充值数量5B币|消费方式优先级从上到下
-[bp_consumption]
-enable = false
-# 消费B币充电|充电的目标UID|可充值自己的UID
-bp2charge = false
-bp2charge_uid = 6580464
-# 消费B币充值金瓜子|5000金瓜子
-bp2gold = false
-
-; 自动转发抽奖动态|自动取关未中奖动态|强制清除抽奖组关注|过滤低于多少粉丝|自定义回复|过滤词|逗号分割
-[dynamic]
-enable = false
-clear_dynamic = false
-clear_group_follow = false
-min_fans_num = 15000
-auto_reply_text =
-filter_words =
-
-; 游戏赛事竞猜预测|破产机|每日竞猜次数|每次竞猜硬币(1-10)|下注(1.压大,2.压小,3.随机)
-[match_forecast]
-enable = false
-max_num = 20
-max_coin = 10
-bet = 1
-
-; 休眠时间|时间区间(0-23)|逗号分隔
-[sleep]
-enable = true
-section = "2,3,4,5,6"
-
-#######################
-# 通知设置 #
-#######################
-
-; 消息推送|消息推送过滤词|逗号分割|优先级从上到下
-[notify]
-enable = false
-filter_words =
-
-; Dingtalk机器人|token|依赖USE_NOTIFY
-[notify.dingtalk]
-token =
-
-; Tele机器人|url(可选)|token|chatid|依赖USE_NOTIFY
-[notify.telegram]
-url =
-bottoken =
-chatid =
-
-; Pushplus酱|token|依赖USE_NOTIFY
-[notify.pushplus]
-token =
-
-; Sever酱(原版)|令牌Key|依赖USE_NOTIFY
-[notify.sc]
-sckey =
-
-; Server酱(Turbo版)|令牌Key|依赖USE_NOTIFY
-[notify.sct]
-sctkey =
-
-; GoCqhttp|url|token|目标qq|依赖USE_NOTIFY
-[notify.gocqhttp]
-url = ""
-token =
-target_qq =
-
-; 飞书机器人|依赖USE_NOTIFY
-[notify.feishu]
-token =
-
-; Debug|个人调试推送|url|token
-[notify.debug]
-url = ""
-token =
-
-; 企业微信群机器人|token
-[notify.we_com]
-token =
-
-; 企业微信应用消息|corp_id|corp_secret|agent_id|to_user
-[notify.we_com_app]
-corp_id =
-corp_secret =
-agent_id =
-to_user =
-
-#######################
-# 基础设置 #
-#######################
-
-; 独立推送服务(主)|主备冲突
-[alone_monitor]
-enable = false
-server_addr = "tcp://livecmt-1.mudew.com:10088"
-server_key = ""
-
-; 分区推送服务(备)|主备冲突
-[zone_monitor]
-enable = true
-server_addr = "tcp://broadcastlv.chat.bilibili.com:2243/sub"
-
-; 加密心跳服务器(开源)|如失效自行搭建
-[heartbeat_enc]
-server = "http://heartbeat-1.mudew.com:3000/enc"
-
-#######################
-# 房间设置 #
-#######################
-
-; 直播间ID,全局房间,用于礼物赠送、心跳等等.
-[global_room]
-room_id = 9522051
-
-; 弹幕监控房间(为空则随机)
-[socket]
-room_id = 9522051
-
-; 勋章亲密度,测试功能,有短位使用短位反之长位,左侧优先,仅支持辣条和亿元
-; 当列表中所有ID都已达到每日上限,剩余礼物赠送ROOM_ID指定的直播间
-; 是否填满(按时间投喂正常礼物,否则过期礼物)|直播间ID列表|逗号分隔|
-[intimacy]
-feed_fill = false
-room_list = 9522051
-
-#######################
-# 网络设置 #
-#######################
-
-; 验证SSL证书|请求时验证SSL证书行为
-[network.ssl]
-verify_ssl = true
-
-; 是否使用代理|(http\https)
-[network.proxy]
-enable = false
-proxy = "http://127.0.0.1:8888"
-
-#######################
-# 程序设置 #
-#######################
-
-; 调试模式|采集完整日志
-[debug]
-enable = false
-
-; 记录日志|日志回调地址
-[log]
-enable = false
-callback = "http://www.example.com/api.send?text={account}[{level}]: {message}"
-# 错误回调级别
-#
-# DEBUG 100
-# INFO 200
-# NOTICE 250
-# WARNING 300
-# ERROR 400
-#
-callback_level = 400
-
-; app设置
-[print]
-; 用户名,可自定义
-uname =
-; 账号别名,如果为空则默认使用登录账号作为标示
-user_identity =
-; 多账号区别输出
-multiple = false
diff --git a/data/activity_infos.json b/data/activity_infos.json
deleted file mode 100644
index 688bf24..0000000
--- a/data/activity_infos.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "remarks": "有需要可以自己添加,或者提pr",
- "data": [
- {
- "_url": "注释: 活动地址",
- "url": "https://www.bilibili.com/blackboard/activity-S9PC.html",
- "_title": "注释: 活动标题",
- "title": "vivo S9系列照亮我的美",
- "_description": "注释: 活动描述",
- "description": "vivo S9系列照亮我的美",
- "_sid": "注释: 活动SID",
- "sid": "f3ae9322-817d-11eb-8597-246e966235d8",
- "_login": "注释: 登录+1次机会",
- "login": "true",
- "_follow": "注释: 关注+1次机会",
- "follow": "false",
- "_share": "注释: 分享+1次机会",
- "share": "true",
- "_draw_times": "抽奖次数",
- "draw_times": 2,
- "_expire_at": "注释: 到期时间",
- "expire_at": "2021-03-30 23:59:59"
- },
- {
- "url": "https://www.bilibili.com/blackboard/activity-yellowVSgreen7th.html",
- "title": "哔哩哔哩,黄绿合战7th",
- "description": "神仙打架、大型选择困难症的噩梦!鬼畜区的巅峰对决,每日两两battle,为你带来最棒的视听盛宴!",
- "sid": "13096c11-e89a-11eb-8597-246e966235d8",
- "login": "true",
- "follow": "false",
- "share": "true",
- "draw_times": 1,
- "expire_at": "2021-08-12 11:59:59"
- }
- ]
-}
\ No newline at end of file
diff --git a/data/capsule_infos.json b/data/capsule_infos.json
deleted file mode 100644
index 53133b3..0000000
--- a/data/capsule_infos.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "pool": "https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/get_pool_detail?pool_id=131&_=1616829671726",
- "v3": "https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/get_capsule_info_v3?id=79&from=web&_=1608110093802",
- "remarks": "有需要可以自己添加,或者提pr",
- "data": [
- {
- "_url": "注释: 活动地址",
- "url": "https://www.bilibili.com/blackboard/activity-aEe0qbEc8N.html",
- "_title": "注释: 活动标题",
- "title": "原神2.2应援激励计划",
- "_description": "注释: 活动描述",
- "description": "每日在原神分区任意直播间观看,20分钟1张、1小时2张、2小时3张,每日最高6张。",
- "_room_id": "注释: 活动直播间,如果为0,则取分区随机一个",
- "room_id": 0,
- "_area_id": "注释: 子分区id",
- "area_id": 321,
- "_parent_area_id": "注释: 主分区id",
- "parent_area_id": 3,
- "_coin_id": "注释: 抽奖id",
- "coin_id": 284,
- "_pool_id": "注释: 抽奖id",
- "pool_id": 290,
- "_watch_time": "注释: 观看时长 单位秒",
- "watch_time": 7320,
- "_draw_times": "注释: 抽奖次数",
- "draw_times": 6,
- "_expire_at": "注释: 活动有效时间",
- "expire_at": "2021-11-23 23:59:59"
- },
- {
- "url": "https://live.bilibili.com/6",
- "title": "S11,LOL,英雄联盟,全球总决赛",
- "description": "每日在LOL直播间观看,1分钟1张,3分钟2张,5分钟3张",
- "room_id": 0,
- "area_id": 86,
- "parent_area_id": 2,
- "coin_id": 76,
- "watch_time": 600,
- "draw_times": 0,
- "expire_at": "2021-11-06 23:59:59"
- }
- ]
-}
\ No newline at end of file
diff --git a/data/latest_version.json b/data/latest_version.json
deleted file mode 100644
index fc60b4a..0000000
--- a/data/latest_version.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "code": 0,
- "project": "BiliHelper-personal",
- "branch": "master",
- "source": "https://github.com/lkeme/BiliHelper-personal",
- "raw_url": "https://cdn.jsdelivr.net/gh/lkeme/BiliHelper-personal@master/data/latest_version.json",
- "purge_url": "https://purge.jsdelivr.net/gh/lkeme/BiliHelper-personal@master/data/latest_version.json",
- "version": "1.1.0.220218",
- "des": "程序有更新,请及时线上查看更新哦~",
- "time": "2022年2月18日10:40:35",
- "ini_version": "0.0.1",
- "ini_des": "配置有更新,请及时线上查看更新哦~",
- "ini_time": "2022年2月18日10:40:35"
-}
\ No newline at end of file
diff --git a/data/reply_words.json b/data/reply_words.json
deleted file mode 100644
index 1380ede..0000000
--- a/data/reply_words.json
+++ /dev/null
@@ -1,175 +0,0 @@
-{
- "DynamicForward": {
- "default": [
- "从未中奖,从未放弃[doge]",
- "来当分母= =",
- "让我中一次吧QAQ",
- "继续分母",
- "转发动态",
- "单纯想中次奖",
- "我我我",
- "不错",
- "来了来了",
- "爱了",
- "(;¬_¬)",
- "(~_~;)",
- "= =!",
- "╮(╯▽╰)╭",
- "(シ_ _)シ",
- ">_<",
- "(๑• _ •๑)",
- "(:3_ヽ)_",
- "(⌒▽⌒)",
- "(`・ω・´)",
- "(◦˙▽˙◦)",
- "(=・ω・=)",
- "_Σ:з」∠)シ",
- "o(∩_∩)o",
- "(〜 ̄▽ ̄)〜",
- "(๑• ▽ •๑)",
- "哔哩哔哩干杯~",
- "Bilibili 干杯~",
- "bilibili 干杯~",
- "[doge][doge][doge]",
- "冲冲冲[打call][打call]",
- "[doge]",
- "万一可能呢",
- "我的",
- "[打call]",
- "就看欧不欧啦",
- "来了",
- "中",
- "好诶",
- "好耶",
- "拉低中奖率",
- "重在拉低中奖率[doge]",
- "分母",
- "评论",
- "成为一个分母,希望成为分子",
- "[藏狐]我来",
- "重在参与[OK]",
- "就是我[给心心][打call]",
- "抽起来!!!",
- "我也要中奖",
- "冲",
- "[拥抱]",
- "万一呢?",
- "我来了",
- "欧欧欧",
- "冲冲冲",
- "谢谢给我一个中奖的机会",
- "奖励不重要,重要的是心[doge]",
- "许愿",
- "分母报道",
- "大吉大利",
- "欧气满满",
- "不想再当分母",
- "吸欧气",
- "中!!!",
- "",
- "好运来",
- "啊~",
- "哈哈哈",
- "抽奖奖(⌒▽⌒)",
- "中奖绝缘体",
- "绝缘体",
- "求脱非入欧",
- "好运",
- "中奖绝缘体表示想中!",
- "呜呜呜非洲人来了",
- "选我吧",
- "一定会中",
- "好运bufff",
- "滴滴滴",
- "哇哇哇哇",
- "万一呢",
- "非酋日常",
- "加油",
- "抽中吧",
- "我要",
- "想欧一次!",
- "拉低中奖率233",
- "想要...",
- "路过拉低中奖率",
- "希望有个好运气",
- "中奖",
- "什么时候才会抽到我呢?",
- "试试水,看看能不能中",
- "过来水一手",
- "这辈子都不可能中奖的",
- "先拉低中奖率23333",
- "先抽奖,抽不到再说",
- "嘤嘤嘤",
- "捞一把",
- "我就想中一次",
- "拉低拉低",
- "试一试",
- "搞一搞",
- "中奖什么的不可能的( ̄▽ ̄)",
- "听说我中奖了?",
- "脱非转欧",
- "emm",
- "无聊.。。。。",
- "[星星眼]",
- "[妙啊]",
- "[辣眼睛]",
- "[吃瓜][吃瓜]",
- "[滑稽]",
- "[呲牙]",
- "[打call][打call]",
- "[哈欠][哈欠]",
- "[吃瓜]不嫌事大",
- "[口罩]",
- "[思考]",
- "[冷][冷][冷]",
- "次次参加",
- "从来没中过",
- "让我中吧",
- "来中个吧",
- "送妹子多好",
- "再送个小姐姐咋样",
- "忍不住抽",
- " (* ̄︶ ̄)",
- "如影随形",
- "参加够多就能中奖",
- "参加够多就能当分子",
- "[喜极而泣]",
- "我是天选之子",
- "中一次吧!",
- "坚持不懈,迎难而上,开拓创新!",
- "[OK][OK]",
- "抽个奖和寂寞",
- "中中",
- "坚持不懈,迎难而上!",
- "呵呵",
- "我一般不抽奖,除非忍不住。",
- "[OK]",
- "[喜欢]",
- "[偷笑]",
- "[笑]",
- "[吃瓜]",
- "[奋斗]",
- "在",
- "冲吖~",
- "[保佑][保佑]",
- "从未中,从未停",
- "[抠鼻][抠鼻]",
- "来力",
- "秋梨膏",
- "从不缺席",
- "分子",
- "1",
- "好",
- "rush",
- "来来来",
- "ok",
- "凑热闹",
- "我要我要[打call]",
- "我还能中!让我中!!!",
- "大家都散了吧,已经抽完了,是我的",
- "给我中一次吧!",
- "我来抽个奖",
- "[doge][doge][doge]"
- ]
- }
-}
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index 2317f65..0000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,46 +0,0 @@
-FROM php:alpine
-
-#MAINTAINER zsnmwy
-LABEL AUTHOR = "Lkeme "
-
-ENV USER_NAME='' \
- USER_PASSWORD='' \
- REPO_URL='https://github.com/' \
- CUSTOM_CLONE_URL='https://speed.example.com/example/example.git' \
- MIRRORS="0" \
- CONIFG_PATH='/app/conf/user.ini' \
- Green="\\033[32m" \
- Red="\\033[31m" \
- GreenBG="\\033[42;37m" \
- RedBG="\\033[41;37m" \
- Font="\\033[0m" \
- Green_font_prefix="\\033[32m" \
- Green_background_prefix="\\033[42;37m" \
- Font_color_suffix="\\033[0m" \
- Info="${Green}[信息]${Font}" \
- OK="${Green}[OK]${Font}" \
- Error="${Red}[错误]${Font}"
-
-WORKDIR /app
-
-RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
-#RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
-RUN docker-php-ext-install sockets
-
-#RUN if [ "${CN}" = true ]; then export REPO_URL="https://github.com.cnpmjs.org"; fi
-
-#RUN set -ex \
-# && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
-# && echo "Asia/Shanghai" > /etc/timezone \
-
-RUN apk add --no-cache git && \
- git clone ${REPO_URL}/lkeme/BiliHelper-personal.git --depth=1 /app && \
- cp -f /app/docker/entrypoint.sh /usr/local/bin/entrypoint.sh && \
- chmod 777 /usr/local/bin/entrypoint.sh && \
- php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" && \
- php composer-setup.php && \
- php composer.phar install && \
- rm -r /var/cache/apk && \
- rm -r /usr/share/man
-
-ENTRYPOINT ["entrypoint.sh"]
\ No newline at end of file
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
deleted file mode 100644
index 5291fb4..0000000
--- a/docker/entrypoint.sh
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/bin/sh
-set -e
-
-# 源切换
-case ${MIRRORS} in
-"custom")
- # custom
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-自定义克隆链接 ${Font} \n ======== \n"
- git remote set-url origin ${CUSTOM_CLONE_URL}
- ;;
-"0")
- # https://github.com/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-github.com ${Font} \n ======== \n"
- git remote set-url origin https://github.com/lkeme/BiliHelper-personal.git
- ;;
-"1")
- # https://ghproxy.com/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-ghproxy.com ${Font} \n ======== \n"
- git remote set-url origin https://ghproxy.com/https://github.com/lkeme/BiliHelper-personal.git
- ;;
-"2")
- # http://fastgit.org/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-hub.fastgit.xyz ${Font} \n ======== \n"
- git remote set-url origin https://hub.fastgit.xyz/lkeme/BiliHelper-personal.git
- ;;
-"3")
- # https://hub.gitfast.tk/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-hub.gitfast.tk ${Font} \n ======== \n"
- git remote set-url origin https://hub.gitfast.tk/lkeme/BiliHelper-personal.git
- ;;
-"4")
- # https://hub.gitslow.tk/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-hub.gitslow.tk ${Font} \n ======== \n"
- git remote set-url origin https://hub.gitslow.tk/lkeme/BiliHelper-personal.git
- ;;
-"5")
- # https://hub.verge.tk/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-hub.verge.tk ${Font} \n ======== \n"
- git remote set-url origin https://hub.verge.tk/lkeme/BiliHelper-personal.git
- ;;
-"6")
- # https://gh.api.99988866.xyz/
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-gh.api.99988866.xyz ${Font} \n ======== \n"
- git remote set-url origin https://gh.api.99988866.xyz/https://github.com/lkeme/BiliHelper-personal.git
- ;;
-*)
- echo -e "\n ======== \n ${Info} ${GreenBG} 切换源-github.com ${Font} \n ======== \n"
- git remote set-url origin https://github.com/lkeme/BiliHelper-personal.git
- ;;
-esac
-
-# 拉取更新
-echo -e "\n ======== \n ${Info} ${GreenBG} 正使用 git pull 同步项目 ${Font} \n ======== \n"
-git pull
-
-# 安装依赖
-echo -e "\n ======== \n ${Info} ${GreenBG} 安装/更新 项目运行依赖 ${Font} \n ======== \n"
-php composer.phar install
-echo -e "\n \n \n \n"
-
-# 判断类型
-if [[ -f ${CONIFG_PATH} ]]; then
- echo -e "\n ======== \n ${GreenBG} 正在使用外部配置文件 ${Font} \n ======== \n"
-else
- echo -e "${OK} ${GreenBG} 正在使用传入的环境变量进行用户配置。\n 如果需要配置更多选择项,请通过挂载配置文件来传入。具体参考项目中的README。\n https://github.com/lkeme/BiliHelper-personal.git ${Font} \n ======== \n "
- cp /app/conf/user.ini.example /app/conf/user.ini
- sed -i ''"$(cat /app/conf/user.ini -n | grep "username = \"\"" | awk '{print $1}')"'c '"$(echo "username = \"${USER_NAME}\"")"'' ${CONIFG_PATH}
- sed -i ''"$(cat /app/conf/user.ini -n | grep "password = \"\"" | awk '{print $1}')"'c '"$(echo "password = \"${USER_PASSWORD}\"")"'' ${CONIFG_PATH}
-fi
-
-php index.php
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..e3d27b0
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ src
+
+
+
+
+ test/
+
+
+
\ No newline at end of file
diff --git a/plugin/CheckUpdate/CheckUpdate.php b/plugin/CheckUpdate/CheckUpdate.php
new file mode 100644
index 0000000..96e5774
--- /dev/null
+++ b/plugin/CheckUpdate/CheckUpdate.php
@@ -0,0 +1,140 @@
+ __CLASS__, // hook
+ 'name' => 'CheckUpdate', // 插件名称
+ 'version' => '0.0.1', // 插件版本
+ 'desc' => '检查版本更新', // 插件描述
+ 'author' => 'Lkeme',// 作者
+ 'priority' => 1000, // 插件优先级
+ 'cycle' => '24(小时)', // 运行周期
+ ];
+
+ /**
+ * @param Plugin $plugin
+ */
+ public function __construct(Plugin &$plugin)
+ {
+ //
+ TimeLock::initTimeLock();
+ //
+ Cache::initCache();
+ //
+ Log::info('加载CheckUpdate插件');
+ // $this::class
+ $plugin->register($this, 'execute');
+ }
+
+ /**
+ * @use 执行
+ * @return void
+ */
+ public function execute(): void
+ {
+ if (TimeLock::getTimes() > time()) return;
+ //
+ $this->_checkUpdate();
+ //
+ TimeLock::setTimes(24 * 60 * 60);
+ }
+
+ /**
+ * @return void
+ */
+ protected function _checkUpdate(): void
+ {
+ //
+ Log::info('开始检查项目更新');
+ // resource object
+ $offline = $this->fetchOfflineVersion();
+ //
+ Log::info('拉取线上最新配置');
+ // object
+ $online = $this->fetchOnlineVersion();
+ // 比较版本
+ if ($this->compareVersion($offline->get('version'), $online->version)) {
+ // TODO 完善消息 支持markdown
+ $time = $online->time;
+ $version = $online->version;
+ $des = $online->des;
+ $info = "请注意版本变动更新哦~\n\n版本号: $version\n\n更新日志: $des\n\n更新时间: $time\n\n";
+ Log::notice($info);
+ Notice::push('update', $info);
+ } else {
+ Log::info('程序已是最新版本');
+ }
+ }
+
+ /**
+ * @use 拉取本地版本
+ * @return void
+ */
+ protected function fetchOfflineVersion(): Resource
+ {
+ $this->loadResource('version.json', 'json');
+ return $this->resource;
+ }
+
+ /**
+ * @use 拉取线上版本
+ * @return object
+ */
+ protected function fetchOnlineVersion(): object
+ {
+ $url = $this->resource->get('raw_url');
+ $payload = [];
+ return Request::getJson(false, 'other', $url, $payload);
+ }
+
+ /**
+ * @use 比较版本号
+ * @param string $off
+ * @param string $on
+ * @return bool
+ */
+ protected static function compareVersion(string $off, string $on): bool
+ {
+ // true 有更新 false 无更新
+ return !($off == $on);
+ }
+
+ /**
+ * @use 重写系统路径
+ * @param string $filename
+ * @return string
+ */
+ protected function getFilePath(string $filename): string
+ {
+ return str_replace("\\", "/", APP_RESOURCES_PATH . $filename);
+ }
+}
\ No newline at end of file
diff --git a/plugin/Login/Login.php b/plugin/Login/Login.php
new file mode 100644
index 0000000..f11ff6d
--- /dev/null
+++ b/plugin/Login/Login.php
@@ -0,0 +1,604 @@
+ __CLASS__, // hook
+ 'name' => 'Login', // 插件名称
+ 'version' => '0.0.1', // 插件版本
+ 'desc' => '登录模块', // 插件描述
+ 'author' => 'Lkeme',// 作者
+ 'priority' => 1001, // 插件优先级
+ 'cycle' => '24(小时)', // 运行周期
+ ];
+
+ /**
+ * @var string|null
+ */
+ protected ?string $username = '';
+ /**
+ * @var string|null
+ */
+ protected ?string $password = '';
+
+ /**
+ * @param Plugin $plugin
+ */
+ public function __construct(Plugin &$plugin)
+ {
+ //
+ TimeLock::initTimeLock();
+ //
+ Cache::initCache();
+ //
+ Log::info('加载Login插件');
+ // $this::class
+ $plugin->register($this, 'execute');
+ }
+
+ /**
+ * @return void
+ */
+ public function execute(): void
+ {
+ //
+ if (TimeLock::getTimes() && TimeLock::getTimes() < time()) {
+ TimeLock::setTimes(7200);
+ $this->keepLogin();
+ }
+ //
+ if (!TimeLock::getTimes()) {
+ $this->initLogin();
+ TimeLock::setTimes(3600);
+ }
+ }
+
+ /**
+ * @use 初始化登录
+ * @return void
+ */
+ protected function initLogin(): void
+ {
+ //
+ $token = getU('access_token');
+ $r_token = getU('refresh_token');
+ // Token不存在的情况\直接调用登录
+ Log::info('启动登录程序');
+ if (!$token || !$r_token) {
+ Log::info('准备载入登录令牌');
+ $this->login();
+ }
+ // Token存在\校验有效性\否则调用登录
+ Log::info('检查登录令牌有效性');
+ if (!$this->validateToken($token)) {
+ Log::warning('登录令牌失效或即将过期');
+ Log::info('申请更换登录令牌中');
+ if (!$this->refreshToken($token, $r_token)) {
+ Log::warning('无效的登录令牌,尝试重新申请');
+ $this->login();
+ }
+ }
+ }
+
+ /**
+ * @use 登录控制中心
+ * @return void
+ */
+ protected function login(): void
+ {
+ $this->checkLogin();
+ //
+ switch (getConf('login_mode.mode')) {
+ case 1:
+ // 账密模式
+ $this->accountLogin();
+ break;
+ case 2:
+ // 短信验证码模式
+ $this->smsLogin();
+ break;
+ case 3:
+ // 二维码模式
+ $this->qrcodeLogin();
+ break;
+ case 4:
+ // 行为验证码模式(暂未开放)
+ // self::captchaLogin();
+ failExit('此登录模式暂未开放');
+ default:
+ failExit('登录模式配置错误');
+ }
+ }
+
+ /**
+ * @use 保持认证
+ * @return bool
+ */
+ protected function keepLogin(): bool
+ {
+ //
+ $token = getU('access_token');
+ $r_token = getU('refresh_token');
+ //
+ if ($this->validateToken($token)) {
+ return true;
+ }
+ Log::warning('令牌即将过期');
+ Log::info('申请更换令牌中...');
+ if (!$this->refreshToken($token, $r_token)) {
+ Log::warning('无效令牌,正在重新申请...');
+ self::accountLogin();
+ }
+ return false;
+ }
+
+ /**
+ * @use 校验令牌信息
+ * @param string $token
+ * @return bool
+ */
+ protected function validateToken(string $token): bool
+ {
+ // {"ts":1234,"code":0,"data":{"mid":1234,"access_token":"1234","expires_in":7759292}}
+ $response = ApiOauth2::tokenInfo($token);
+ //
+ if (isset($response['code']) && $response['code']) {
+ Log::error('检查令牌失败', ['msg' => $response['message']]);
+ return false;
+ }
+ Log::notice('令牌有效期: ' . date('Y-m-d H:i:s', $response['ts'] + $response['data']['expires_in']));
+ return $response['data']['expires_in'] > 14400;
+ }
+
+ /**
+ * @use 刷新token
+ * @param string $token
+ * @param string $r_token
+ * @return bool
+ */
+ protected function refreshToken(string $token, string $r_token): bool
+ {
+ $response = ApiOauth2::tokenRefresh($token, $r_token);
+ // {"message":"user not login","ts":1593111694,"code":-101}
+ if (isset($response['code']) && $response['code']) {
+ Log::error('重新生成令牌失败', ['msg' => $response['message']]);
+ return false;
+ }
+ Log::info('重新令牌生成完毕');
+ $this->updateLoginInfo($response);
+ Log::info('重置信息配置完毕');
+ return true;
+ }
+
+ /**
+ * @use 更新登录信息
+ * @param array $data
+ */
+ protected function updateLoginInfo(array $data): void
+ {
+ //
+ $access_token = $data['data']['token_info']['access_token'];
+ $this->updateInfo('access_token', $access_token);
+ //
+ $refresh_token = $data['data']['token_info']['refresh_token'];
+ $this->updateInfo('refresh_token', $refresh_token);
+ //
+ $cookie = $this->formatCookie($data['data']['cookie_info']['cookies']);
+ $this->updateInfo('cookie', $cookie);
+ //
+ $user = User::parseCookie();
+ $this->updateInfo('uid', $user['uid'], false);
+ $this->updateInfo('csrf', $user['csrf'], false);
+ $this->updateInfo('sid', $user['sid'], false);
+ //
+ // $this->updateInfo('username',$this->username);
+ // $this->updateInfo('password',$this->password);
+ }
+
+ /**
+ * @use 更新Tv登录信息
+ * @param array $data
+ */
+ protected function updateTvLoginInfo(array $data): void
+ {
+ //
+ $access_token = $data['data']['access_token'];
+ $this->updateInfo('access_token', $access_token);
+ //
+ $refresh_token = $data['data']['refresh_token'];
+ $this->updateInfo('refresh_token', $refresh_token);
+ //
+ //
+ $cookie = $this->token2Cookie($access_token);
+ $this->updateInfo('cookie', $cookie);
+ //
+ $user = User::parseCookie();
+ $this->updateInfo('uid', $user['uid'], false);
+ $this->updateInfo('csrf', $user['csrf'], false);
+ $this->updateInfo('sid', $user['sid'], false);
+ //
+ // $this->updateInfo('username',$this->username);
+ // $this->updateInfo('password',$this->password);
+ }
+
+ /**
+ * @use 更新信息
+ * @param string $key
+ * @param mixed $value
+ * @param bool $print
+ * @param bool $hide
+ * @return void
+ */
+ protected function updateInfo(string $key, mixed $value, bool $print = true, bool $hide = true): void
+ {
+ setU($key, $value);
+ if ($print) {
+ Log::info(" > $key: " . ($hide ? Common::replaceStar($value, 6, 6) : $value));
+ }
+ }
+
+ /**
+ * @use 格式化Cookie
+ * @param array $cookies
+ * @return string
+ */
+ protected function formatCookie(array $cookies): string
+ {
+ $c = '';
+ foreach ($cookies as $cookie) {
+ $c .= $cookie['name'] . '=' . $cookie['value'] . ';';
+ }
+ return $c;
+ }
+
+ /**
+ * @use 账密登录
+ * @param string $validate
+ * @param string $challenge
+ * @param string $mode
+ * @return void
+ */
+ protected function accountLogin(string $validate = '', string $challenge = '', string $mode = '账密模式'): void
+ {
+ Log::info("尝试 $mode 登录");
+ // {"ts":1593079322,"code":-629,"message":"账号或者密码错误"}
+ // {"ts":1593082268,"code":-105,"data":{"url":"https://passport.bilibili.com/register/verification.html?success=1>=b6e5b7fad7ecd37f465838689732e788&challenge=7efb4020b22c0a9ac124aea624e11ad7&ct=1&hash=7fa8282ad93047a4d6fe6111c93b308a"},"message":"验证码错误"}
+ // {"ts":1593082432,"code":0,"data":{"status":0,"token_info":{"mid":123456,"access_token":"123123","refresh_token":"123123","expires_in":2592000},"cookie_info":{"cookies":[{"name":"bili_jct","value":"123123","http_only":0,"expires":1595674432},{"name":"DedeUserID","value":"123456","http_only":0,"expires":1595674432},{"name":"DedeUserID__ckMd5","value":"123123","http_only":0,"expires":1595674432},{"name":"sid","value":"bd6aagp7","http_only":0,"expires":1595674432},{"name":"SESSDATA","value":"6d74d850%123%2Cf0e36b61","http_only":1,"expires":1595674432}],"domains":[".bilibili.com",".biligame.com",".bigfunapp.cn"]},"sso":["https://passport.bilibili.com/api/v2/sso","https://passport.biligame.com/api/v2/sso","https://passport.bigfunapp.cn/api/v2/sso"]}}
+ // {"ts":1610254019,"code":0,"data":{"status":2,"url":"https://passport.bilibili.com/account/mobile/security/managephone/phone/verify?tmp_token=2bc5dd260df7158xx860565fxx0d5311&requestId=dffcfxx052fe11xxa9c8e2667739c15c&source=risk","message":"您的账号存在高危异常行为,为了您的账号安全,请验证手机号后登录帐号"}}
+ // https://passport.bilibili.com/mobile/verifytel_h5.html
+ $response = ApiQrcode::passwordLogin($this->username, $this->password, $validate, $challenge);
+ //
+ $this->loginAfter($mode, $response['code'], $response);
+ }
+
+ /**
+ * @use 短信登录
+ * @param string $mode
+ * @return void
+ */
+ protected function smsLogin(string $mode = '短信模式'): void
+ {
+ Log::info("尝试 $mode 登录");
+ //
+ if (getConf('login_check.phone')) {
+ if (!Common::checkPhone($this->username)) {
+ failExit('当前用户名不是有效手机号格式');
+ }
+ }
+ //
+ $captcha = $this->sendSms($this->username, getConf('login_country.code'));
+ $code = $this->cliInput('请输入收到的短信验证码: ');
+ $response = ApiQrcode::smsLogin($captcha, $code);
+ //
+ $this->loginAfter($mode, $response['code'], $response);
+ }
+
+ /**
+ * @use 扫码登录
+ * @param string $mode
+ * @return void
+ */
+ protected function qrcodeLogin(string $mode = '扫码模式'): void
+ {
+ Log::info("尝试 $mode 登录");
+ //
+ $this->cliInput("请尝试放大窗口,以确保二维码完整显示,回车继续");
+ //
+ $response = $this->fetchQrAuthCode();
+ $auth_code = $response['auth_code'];
+ //
+ Qrcode::show($response['url']);
+ // max 180 step 3
+ foreach (range(0, 180, 3) as $_) {
+ sleep(3);
+ if ($this->validateQrAuthCode($auth_code)) {
+ return;
+ }
+ }
+ failExit("扫码失败 二维码已失效");
+ }
+
+ /**
+ * @use 获取AuthCode
+ * @return array
+ */
+ protected function fetchQrAuthCode(): array
+ {
+ // {"code":0,"message":"0","ttl":1,"data":{"url":"https://passport.bilibili.com/x/passport-tv-login/h5/qrcode/auth?auth_code=xxxx","auth_code":"xxxx"}}
+ $response = ApiQrcode::authCode();
+ //
+ if ($response['code']) {
+ failExit('获取AuthCode错误', ['msg' => $response['message']]);
+ }
+ Log::info("获取到AuthCode: {$response['data']['auth_code']}");
+ return $response['data'];
+ }
+
+ /**
+ * @use 验证AuthCode
+ * @param string $auth_code
+ * @return bool
+ */
+ protected function validateQrAuthCode(string $auth_code): bool
+ {
+ // {"code":0,"message":"0","ttl":1,"data":{"mid":123,"access_token":"xxx","refresh_token":"xxx","expires_in":2592000}}
+ $response = ApiQrcode::poll($auth_code);
+ echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
+ switch ($response['code']) {
+ case 0:
+ // 登录成功
+ Log::notice("扫码成功 {$response['message']}");
+ //
+ // $this->updateTvLoginInfo($response);
+ $this->updateLoginInfo($response);
+ return true;
+ case -3:
+ // API校验密匙错误
+ failExit("扫码失败 {$response['message']}");
+ case -400:
+ // 请求错误
+ failExit("扫码失败 {$response['message']}");
+ case 86038:
+ // 二维码已失效
+ failExit("扫码失败 {$response['message']}");
+ case 86039:
+ // 二维码尚未确认
+ Log::info("等待扫码 {$response['message']}");
+ return false;
+ default:
+ failExit("扫码失败 {$response['message']}");
+ }
+
+ }
+
+ /**
+ * @use 登录后处理
+ * @param string $mode
+ * @param int $code
+ * @param array $data
+ * @return void
+ */
+ protected function loginAfter(string $mode, int $code, array $data): void
+ {
+ switch ($code) {
+ case 0:
+ // data->data->status number
+ if (array_key_exists('status', $data['data'])) {
+ // 二次判断
+ switch ($data['data']['status']) {
+ case 0:
+ // 正常登录
+ $this->loginSuccess($mode, $data);
+ break;
+ case 2:
+ // 异常高危
+ $this->loginFail($mode, $data['data']['message']);
+ case 3:
+ // 需要验证手机号
+ $this->loginFail($mode, "需要验证手机号: {$data['data']['url']}");
+ default:
+ // 未知错误
+ $this->loginFail($mode, '未知错误: ' . json_encode($data));
+ }
+ } else {
+ // 正常登录
+ $this->loginSuccess($mode, $data);
+ }
+ break;
+ case -105:
+ // 需要验证码
+ $this->loginFail($mode, '此次登录需要验证码或' . $data['message']);
+ case -629:
+ // 密码错误
+ $this->loginFail($mode, $data['message']);
+ case -2100:
+ // 验证手机号
+ $this->loginFail($mode, '账号启用了设备锁或异地登录需验证手机号');
+ default:
+ // 未知错误
+ $this->loginFail($mode, '未知错误: ' . $data['message']);
+ }
+ }
+
+ /**
+ * @use 登录成功处理
+ * @param string $mode
+ * @param array $data
+ * @return void
+ */
+ protected function loginSuccess(string $mode, array $data): void
+ {
+ Log::info("$mode 登录成功");
+ $this->updateLoginInfo($data);
+ Log::info('生成信息配置完毕');
+ }
+
+ /**
+ * @use 登录失败处理
+ * @param string $mode
+ * @param string $data
+ * @return void
+ */
+ #[NoReturn]
+ protected function loginFail(string $mode, string $data): void
+ {
+ failExit("$mode 登录失败", ['msg' => $data]);
+ }
+
+ /**
+ * @use 检查登录
+ */
+ protected function checkLogin(): void
+ {
+ $username = getConf('login_account.username');
+ $password = getConf('login_account.password');
+ if (empty($username) || empty($password)) {
+ failExit('空白的帐号和口令');
+ }
+ $this->username = $username;
+ $this->password = $this->publicKeyEnc($password);
+ }
+
+ /**
+ * @use 公钥加密
+ * @param string $plaintext
+ * @return string
+ */
+ protected function publicKeyEnc(string $plaintext): string
+ {
+ Log::info('正在载入公钥');
+ //
+ $response = ApiOauth2::getKey();
+ //
+ if (isset($response['code']) && $response['code']) {
+ failExit('公钥载入失败', ['msg' => $response['message']]);
+ } else {
+ Log::info('公钥载入完毕');
+ }
+ // print_r($data);
+ $public_key = $response['data']['key'];
+ $hash = $response['data']['hash'];
+ openssl_public_encrypt($hash . $plaintext, $crypt, $public_key);
+ return base64_encode($crypt);
+ }
+
+ /**
+ * @use 发送短信验证码
+ * @param string $phone
+ * @param string $cid
+ * @return array
+ */
+ protected function sendSms(string $phone, string $cid): array
+ {
+ // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"4e292933816755442c1568e2043b8e41","recaptcha_url":""}}
+ // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"","recaptcha_url":"https://www.bilibili.com/h5/project-msg-auth/verify?ct=geetest\u0026recaptcha_token=ad520c3a4a3c46e29b1974d85efd2c4b\u0026gee_gt=1c0ea7c7d47d8126dda19ee3431a5f38\u0026gee_challenge=c772673050dce482b9f63ff45b681ceb\u0026hash=ea2850a43cc6b4f1f7b925d601098e5e"}}
+ $raw = ApiQrcode::sendSms($phone, $cid);
+ $response = json_decode($raw, true);
+ //
+ if ($response['code'] == 0 && isset($response['data']['captcha_key']) && $response['data']['recaptcha_url'] == '') {
+ Log::info("短信验证码发送成功 {$response['data']['captcha_key']}");
+ $payload['captcha_key'] = $response['data']['captcha_key'];
+ return $payload;
+ }
+ failExit("短信验证码发送失败 $raw");
+ }
+
+ /**
+ * @use 输入短信验证码
+ * @param string $msg
+ * @param int $max_char
+ * @return string
+ */
+ protected function cliInput(string $msg, int $max_char = 100): string
+ {
+ $stdin = fopen('php://stdin', 'r');
+ echo '# ' . $msg;
+ $input = fread($stdin, $max_char);
+ fclose($stdin);
+ return str_replace(PHP_EOL, '', $input);
+ }
+
+ /**
+ * @use 获取验证码
+ * @return array
+ */
+ protected function getCaptcha(): array
+ {
+ $response = ApiCaptcha::combine();
+ Log::info('正在获取验证码 ' . $response['code']);
+ if ($response['code'] == 0 && isset($response['data']['result'])) {
+ return [
+ 'gt' => $response['data']['result']['gt'],
+ 'challenge' => $response['data']['result']['challenge'],
+ 'key' => $response['data']['result']['key'],
+ ];
+ }
+ return [
+ 'gt' => '',
+ 'challenge' => '',
+ 'key' => ''
+ ];
+ }
+
+ /**
+ * @use 验证码模式
+ * @param string $mode
+ * @return void
+ */
+ protected function captchaLogin(string $mode = '验证码模式'): void
+ {
+// $captcha_ori = $this->getCaptcha();
+// $captcha = $this->ocrCaptcha($captcha_ori);
+// $this->accountLogin($captcha['validate'], $captcha['challenge'], $mode);
+ }
+
+ /**
+ * @use 转换Cookie
+ * @param string $token
+ * @return string
+ */
+ protected function token2Cookie(string $token): string
+ {
+ $response = ApiOauth2::token2Cookie($token);
+ $headers = $response['Set-Cookie'];
+ $cookies = [];
+ foreach ($headers as $header) {
+ preg_match_all('/^(.*);/iU', $header, $cookie);
+ $cookies[] = $cookie[0][0];
+ }
+ return implode("", array_reverse($cookies));
+ }
+
+}
\ No newline at end of file
diff --git a/profile/example/config/user.ini b/profile/example/config/user.ini
new file mode 100644
index 0000000..bd3b480
--- /dev/null
+++ b/profile/example/config/user.ini
@@ -0,0 +1,123 @@
+version = 0.0.1
+
+#######################
+# 账户设置 #
+#######################
+
+; 帐号/密码
+[login_account]
+username =
+password =
+
+; 登录模式/[1.账密模式 2.短信验证码模式 3.扫码模式 4.行为验证码模式(暂未开放)]
+[login_mode]
+mode = 1
+
+; 国家代码/mode=2触发/例:大陆86 澳大利亚61
+[login_country]
+code = 86
+
+; 校验/mode=2触发/校验国内手机号/国外手机号关闭校验
+[login_check]
+phone = true
+
+#######################
+# 通知设置 #
+#######################
+
+; 消息推送/消息推送过滤词/逗号分割/优先级从上到下
+[notify]
+enable = false
+filter_words = test,测试
+
+; Dingtalk机器人/token/依赖USE_NOTIFY
+[notify_dingtalk]
+token =
+
+; Tele机器人/url(可选)/token/chatid/依赖USE_NOTIFY
+[notify_telegram]
+url =
+bottoken =
+chatid =
+
+; Pushplus酱/token/依赖USE_NOTIFY
+[notify_pushplus]
+token =
+
+; Sever酱(原版)/令牌Key/依赖USE_NOTIFY
+[notify_sc]
+sckey =
+
+; Server酱(Turbo版)/令牌Key/依赖USE_NOTIFY
+[notify_sct]
+sctkey =
+
+; GoCqhttp/url/token/目标qq/依赖USE_NOTIFY
+[notify_gocqhttp]
+url = ""
+token =
+target_qq =
+
+; 飞书机器人/依赖USE_NOTIFY
+[notify_feishu]
+token =
+
+; Debug/个人调试推送/url/token
+[notify_debug]
+url = ""
+token =
+
+; 企业微信群机器人/token
+[notify_we_com]
+token =
+
+; 企业微信应用消息/corp_id/corp_secret/agent_id/to_user
+[notify_we_com_app]
+corp_id =
+corp_secret =
+agent_id =
+to_user =
+
+#######################
+# 网络设置 #
+#######################
+
+; 验证SSL证书/请求时验证SSL证书行为
+[network_ssl]
+verify = true
+
+; 是否使用代理/(http\https)
+[network_proxy]
+enable = false
+proxy = "http://127.0.0.1:8888"
+
+#######################
+# 程序设置 #
+#######################
+
+; 调试模式/采集完整日志
+[debug]
+enable = false
+
+; 记录日志/日志回调地址
+[log]
+enable = false
+callback = "http://www.example.com/api.send?text={account}[{level}]: {message}"
+# 错误回调级别
+#
+# DEBUG 100
+# INFO 200
+# NOTICE 250
+# WARNING 300
+# ERROR 400
+#
+callback_level = 400
+
+; app设置
+[print]
+; 用户名,可自定义
+uname =
+; 账号别名,如果为空则默认使用登录账号作为标示
+user_identity =
+; 多账号区别输出
+multiple = false
diff --git a/profile/example/device/device.yaml b/profile/example/device/device.yaml
new file mode 100644
index 0000000..dc51bd6
--- /dev/null
+++ b/profile/example/device/device.yaml
@@ -0,0 +1,34 @@
+device_version: 0.0.1
+
+app:
+ bili_a: # Android
+ package: "tv.danmaku.bili"
+ version: "6.72.0"
+ build: "6720300"
+ channel: "bili"
+ device: "phone"
+ mobi_app: "android"
+ platform: "android"
+ s_locale: "zh-Hans_CN"
+ c_locale: "zh-Hans_CH"
+ app_key: "MWQ4YjZlN2Q0NTIzMzQzNg=="
+ secret_key: "NTYwYzUyY2NkMjg4ZmVkMDQ1ODU5ZWQxOGJmZmQ5NzM"
+ app_key_n: "NzgzYmJiNzI2NDQ1MWQ4Mg=="
+ secret_key_n: "MjY1MzU4M2M4ODczZGVhMjY4YWI5Mzg2OTE4YjFkNjU="
+ bili_i: # IOS
+ app_key: "MjdlYjUzZmM5MDU4ZjhjMw=="
+ secret_key: "YzJlZDUzYTc0ZWVlZmUzY2Y5OWZiZDAxZDhjOWMzNzU="
+ bili_t: # Tv
+ app_key: "NDQwOWUyY2U4ZmZkMTJiOA=="
+ secret_key: "NTliNDNlMDRhZDY5NjVmMzQzMTkwNjJiNDc4ZjgzZGQ="
+
+platform:
+ system:
+ os: "android"
+ os_ver: "7.1.2"
+ model: "MuMu"
+ network: "2" # 1 流量 2 WIFI
+ headers:
+ other_ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
+ pc_ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53"
+ app_ua: "Mozilla/5.0 BiliDroid/${app.bili_a.version} (bbcallen@gmail.com) os/${platform.system.os} model/${device.model} mobi_app/${app.bili_a.mobi_app}build/${app.bili_a.build} channel/${app.bili_a.channel} innerVer/${app.bili_a.build}osVer/${platform.system.os_ver} network/${platform.system.network}"
diff --git a/data/filter_library.json b/resources/filter_library.json
similarity index 99%
rename from data/filter_library.json
rename to resources/filter_library.json
index 2306bcd..24d1ea6 100644
--- a/data/filter_library.json
+++ b/resources/filter_library.json
@@ -1,4 +1,5 @@
{
+ "code": 200,
"MaterialObject": {
"sensitive": [
"测试",
diff --git a/resources/version.json b/resources/version.json
new file mode 100644
index 0000000..1a4362e
--- /dev/null
+++ b/resources/version.json
@@ -0,0 +1,14 @@
+{
+ "code": 200,
+ "project": "BiliHelper-personal",
+ "branch": "master",
+ "source": "https://github.com/lkeme/BiliHelper-personal",
+ "raw_url": "https://gh.notifyplus.cf/https://raw.githubusercontent.com/lkeme/BiliHelper-personal/master/data/latest_version.json",
+ "purge_url": "https://cdn.staticaly.com/gh/lkeme/BiliHelper-personal/master/data/latest_version.json",
+ "version": "1.2.0.220517-dev",
+ "des": "程序有更新,请及时线上查看更新哦~",
+ "time": "2022年5月17日",
+ "ini_version": "0.0.1",
+ "ini_des": "配置有更新,请及时线上查看更新哦~",
+ "ini_time": "2022年5月17日"
+}
\ No newline at end of file
diff --git a/src/Api/Passport/ApiCaptcha.php b/src/Api/Passport/ApiCaptcha.php
new file mode 100644
index 0000000..7ede5fb
--- /dev/null
+++ b/src/Api/Passport/ApiCaptcha.php
@@ -0,0 +1,60 @@
+ $plat
+ ];
+ // {"code":0,"data":{"result":{"success":1,"gt":"b6e5b7fad7ecd37f465838689732e788","challenge":"88148a764f94e5923564b356a69277fc","key":"230509df5ce048ca9bf29e1ee323af30"},"type":1}}
+
+ return Request::getJson(true, 'other', $url, $payload);
+ }
+
+ /**
+ * @use 识别验证码
+ * @param array $captcha
+ * @return array
+ */
+ public static function ocr(array $captcha):array{
+ $url = 'https://captcha-v1.mudew.com:19951/';
+ $payload = [
+ 'type' => 'gt3',
+ 'gt' => $captcha['gt'],
+ "challenge" => $captcha['challenge'],
+ "referer" => "https://passport.bilibili.com/"
+ ];
+ $headers = [
+ 'Content-Type' => 'application/json',
+ ];
+ return Request::postJson(true,'other', $url, $payload, $headers);
+ }
+}
+
\ No newline at end of file
diff --git a/src/Api/Passport/ApiLogin.php b/src/Api/Passport/ApiLogin.php
new file mode 100644
index 0000000..50f0c12
--- /dev/null
+++ b/src/Api/Passport/ApiLogin.php
@@ -0,0 +1,91 @@
+ $validate ? "$validate|jordan" : '',
+ 'validate' => $validate,
+ 'challenge' => $challenge,
+ 'permission' => 'ALL',
+ 'username' => $username,
+ 'password' => $password,
+ 'captcha' => '',
+ 'subid' => 1,
+ 'cookies' => ''
+ ];
+ return Request::postJson(true, 'app', $url, Sign::login($payload));
+ }
+
+ /**
+ * @use 发送短信验证码
+ * @param string $phone
+ * @param string $cid
+ * @return string
+ */
+ public static function sendSms(string $phone, string $cid): string
+ {
+ $url = 'https://passport.bilibili.com//x/passport-login/sms/send';
+ // TODO 动态版本参数
+ $payload = [
+ 'cid' => $cid,
+ 'tel' => $phone,
+ 'statistics' => '{"appId":1,"platform":3,"version":"6.72.0","abtest":""}',
+ ];
+ // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"4e292933816755442c1568e2043b8e41","recaptcha_url":""}}
+ // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"","recaptcha_url":"https://www.bilibili.com/h5/project-msg-auth/verify?ct=geetest\u0026recaptcha_token=ad520c3a4a3c46e29b1974d85efd2c4b\u0026gee_gt=1c0ea7c7d47d8126dda19ee3431a5f38\u0026gee_challenge=c772673050dce482b9f63ff45b681ceb\u0026hash=ea2850a43cc6b4f1f7b925d601098e5e"}}
+ return Request::post('app', $url, Sign::login($payload));
+ }
+
+ /**
+ * @use 短信验证码登录
+ * @param array $captcha
+ * @param string $code
+ * @return array
+ */
+ public static function smsLogin(array $captcha, string $code): array
+ {
+ $url = 'https://passport.bilibili.com/x/passport-login/login/sms';
+ $payload = [
+ 'captcha_key' => $captcha['captcha_key'],
+ 'cid' => $captcha['cid'],
+ 'tel' => $captcha['tel'],
+ 'statistics' => $captcha['statistics'],
+ 'code' => $code,
+ ];
+ return Request::postJson(true, 'app', $url, Sign::login($payload));
+ }
+
+}
+
\ No newline at end of file
diff --git a/src/Api/Passport/ApiOauth2.php b/src/Api/Passport/ApiOauth2.php
new file mode 100644
index 0000000..72fa230
--- /dev/null
+++ b/src/Api/Passport/ApiOauth2.php
@@ -0,0 +1,118 @@
+ $token,
+ ];
+ // {"ts":1234,"code":0,"data":{"mid":1234,"access_token":"1234","expires_in":7759292}}
+ return Request::getJson(true, 'app', $url, Sign::common($payload));
+ }
+
+ /**
+ * @use 新令牌信息
+ * @param string $token
+ * @return array
+ */
+ public static function tokenInfoNew(string $token): array
+ {
+ $url = 'https://passport.bilibili.com/x/passport-login/oauth2/info';
+ $payload = [
+ 'access_key' => $token,
+ ];
+ return Request::postJson(true, 'app', $url, Sign::common($payload));
+
+ }
+
+ /**
+ * @use 刷新令牌信息
+ * @param string $token
+ * @param string $r_token
+ * @return array
+ */
+ public static function tokenRefreshNew(string $token, string $r_token): array
+ {
+ $url = 'https://passport.bilibili.com/x/passport-login/oauth2/refresh_token';
+ $payload = [
+ 'access_key' => $token,
+ 'access_token' => $token,
+ 'refresh_token' => $r_token,
+ ];
+ // {"message":"user not login","ts":1593111694,"code":-101}
+ return Request::postJson(true, 'app', $url, Sign::common($payload));
+ }
+
+ /**
+ * @use 刷新令牌信息
+ * @param string $token
+ * @param string $r_token
+ * @return array
+ */
+ public static function tokenRefresh(string $token, string $r_token): array
+ {
+ $url = 'https://passport.bilibili.com/api/v2/oauth2/refresh_token';
+ $payload = [
+ 'access_token' => $token,
+ 'refresh_token' => $r_token,
+ ];
+ // {"message":"user not login","ts":1593111694,"code":-101}
+ return Request::postJson(true, 'app', $url, Sign::common($payload));
+ }
+
+ /**
+ * @use 获取公钥
+ * @return array
+ */
+ public static function getKey(): array
+ {
+ // $url = 'https://passport.bilibili.com/api/oauth2/getKey';
+ $url = 'https://passport.bilibili.com/x/passport-login/web/key';
+ $payload = [];
+ return Request::getJson(true, 'app', $url, Sign::login($payload));
+ }
+
+ /**
+ * @use 刷新COOKIE
+ * @param string $token
+ * @return array
+ */
+ public static function token2Cookie(string $token): array
+ {
+ $url = 'https://passport.bilibili.com/api/login/sso';
+ $payload = [
+ 'access_key' => $token,
+ 'gourl' => 'https%3A%2F%2Faccount.bilibili.com%2Faccount%2Fhome'
+ ];
+ return Request::headers('app', $url, Sign::tv($payload));
+ }
+
+}
+
\ No newline at end of file
diff --git a/src/Api/PassportTv/ApiQrcode.php b/src/Api/PassportTv/ApiQrcode.php
new file mode 100644
index 0000000..99ed674
--- /dev/null
+++ b/src/Api/PassportTv/ApiQrcode.php
@@ -0,0 +1,61 @@
+ 'application/x-www-form-urlencoded',
+ ];
+ // {"code":0,"message":"0","ttl":1,"data":{"url":"https://passport.bilibili.com/x/passport-tv-login/h5/qrcode/auth?auth_code=xxxx","auth_code":"xxxx"}}
+ return Request::postJson(true, 'app', $url, Sign::tv($payload), $headers);
+ }
+
+ /**
+ * @use 验证登录
+ * @param string $auth_code
+ * @return mixed
+ */
+ public static function poll(string $auth_code): array
+ {
+ $url = 'https://passport.bilibili.com/x/passport-tv-login/qrcode/poll';
+ $payload = [
+ 'auth_code' => $auth_code,
+ ];
+ $headers = [
+ 'content-type' => 'application/x-www-form-urlencoded',
+ ];
+ // {"code":0,"message":"0","ttl":1,"data":{"mid":123,"access_token":"xxx","refresh_token":"xxx","expires_in":2592000}}
+ return Request::postJson(true, 'app', $url, Sign::tv($payload), $headers);
+
+ }
+
+}
+
\ No newline at end of file
diff --git a/src/Bootstrap/Bootstrap.php b/src/Bootstrap/Bootstrap.php
new file mode 100644
index 0000000..aa7ca2e
--- /dev/null
+++ b/src/Bootstrap/Bootstrap.php
@@ -0,0 +1,103 @@
+global_path = $global_path;
+ $this->profile_name = Console::parse($argv);
+ $this->superRegister();
+ }
+
+ public function superRegister(): void
+ {
+ // 核心
+ Core::getInstance($this->global_path, $this->profile_name);
+ // 配置
+ Config::getInstance();
+ // 缓存中心
+ Cache::getInstance();
+ // 日志
+ Log::getInstance();
+ // 环境
+ Env::getInstance();
+ // 设备/取前缀
+ Device::getInstance();
+ // 请求中心
+ Request::getInstance();
+ // 时间锁
+ TimeLock::getInstance();
+ // 过滤词
+ FilterWords::getInstance();
+ // 签名
+ Sign::getInstance();
+ // 用户
+ User::getInstance();
+ // 通知中心
+ Notice::getInstance();
+ // 任务中心
+ Task::getInstance();
+ // 插件中心
+ Plugin::getInstance();
+ // 控制台
+ Console::getInstance();
+ }
+
+ /**
+ * @return void
+ */
+ public function run(): void
+ {
+ Console::getInstance()->register();
+ }
+}
\ No newline at end of file
diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php
new file mode 100644
index 0000000..6c7b062
--- /dev/null
+++ b/src/Cache/Cache.php
@@ -0,0 +1,160 @@
+getCallClassName();
+ //
+ if (!array_key_exists($class_name, self::getInstance()->caches)) {
+ // 判断字符串中是否有中文
+ if (preg_match("/[\x7f-\xff]/", $class_name)) {
+ $pinyin = new Pinyin(); // 默认
+ $class_name = $pinyin->permalink($class_name); // yong-hu-ming
+ }
+ // 如果不存在缓存 初始化
+ $database = self::getInstance()->removeSpecStr('cache_' . $class_name);
+ $options = [
+ 'dir' => PROFILE_CACHE_PATH,
+ 'gzip' => true,
+ 'formatter' => new JsonFormatter()
+ ];
+ self::getInstance()->caches[$class_name] = new Flintstone($database, $options);
+ // ->set('bob', ['email' => 'bob@site.com', 'password' => '123456']);
+ }
+ print_r(array_keys(self::getInstance()->caches));
+
+ }
+
+ /**
+ * @use 写入值
+ * @param string $key
+ * @param mixed $value
+ * @param string|null $classname
+ * @return void
+ */
+ public static function set(string $key, mixed $value, ?string $classname = null): void
+ {
+ // Set a key
+ // $users->set('bob', ['email' => 'bob@site.com', 'password' => '123456']);
+ self::getInstance()->getCache($classname)->set($key, $value);
+ }
+
+ /**
+ * @use 获取值
+ * @param string $key
+ * @param string|null $classname
+ * @return false|mixed
+ */
+ public static function get(string $key, ?string $classname = null): mixed
+ {
+ // Get a key
+ // $user = $users->get('bob');
+ // echo 'Bob, your email is ' . $user['email'];
+ return self::getInstance()->getCache($classname)->get($key);
+ }
+
+ /**
+ * @use 强转一下类型
+ * @param string|null $classname
+ * @return Flintstone
+ */
+ protected function getCache(?string $classname = null): Flintstone
+ {
+ $class_name = $classname ?? $this->getCallClassName();
+ if (!array_key_exists($class_name, $this->caches)) {
+ failExit("当前类 $class_name 并未初始化缓存");
+ }
+ return $this->caches[$class_name];
+ }
+
+ /**
+ * @use 获取调用者类名
+ * @return string
+ */
+ protected function getCallClassName(): string
+ {
+ // basename(str_replace('\\', '/', __CLASS__));
+ $backtraces = debug_backtrace();
+ $temp = pathinfo(basename($backtraces[1]['file']))['filename'];
+ //
+ if ($temp == basename(str_replace('\\', '/', __CLASS__))) {
+ return pathinfo(basename($backtraces[2]['file']))['filename'];
+ } else {
+ return $temp;
+ }
+ }
+
+ /**
+ * @use 去除特殊符号
+ * @param string $str
+ * @return string
+ */
+ protected function removeSpecStr(string $str): string
+ {
+ $specs = str_split("-.,:;'*?~`!@#$%^&+=)(<>{}]|\/、");
+ foreach ($specs as $spec) {
+ $str = str_replace($spec, '', $str);
+ }
+ return $str;
+ }
+
+ /**
+ * @use 获取调用链类
+ * @return mixed
+ */
+ protected function backtraceClass(): mixed
+ {
+ // TODO 耦合度过高 需要解耦
+ $backtraces = debug_backtrace();
+ array_shift($backtraces);
+ return pathinfo(basename($backtraces[2]['file']))['filename'];
+ }
+}
\ No newline at end of file
diff --git a/src/Config/Config.php b/src/Config/Config.php
new file mode 100644
index 0000000..3184b5e
--- /dev/null
+++ b/src/Config/Config.php
@@ -0,0 +1,42 @@
+loadResource($filename, 'ini');
+ }
+
+ /**
+ * @use 重写获取路径
+ * @param string $filename
+ * @return string
+ */
+ protected function getFilePath(string $filename): string
+ {
+ return str_replace("\\", "/", PROFILE_CONFIG_PATH . $filename);
+ }
+}
diff --git a/src/Console/Command/AppCommand.php b/src/Console/Command/AppCommand.php
new file mode 100644
index 0000000..e45265e
--- /dev/null
+++ b/src/Console/Command/AppCommand.php
@@ -0,0 +1,64 @@
+desc);
+ }
+
+ /**
+ * @param Interactor $io
+ * @return void
+ */
+ public function interact(Interactor $io): void
+ {
+ }
+
+ /**
+ * @return void
+ */
+ public function execute(): void
+ {
+ Log::info("执行 $this->desc");
+ //
+ $plugins = Plugin::getPlugins();
+ print_r($plugins);
+ foreach ($plugins as $plugin) {
+ Task::addTask($plugin['hook'], null);
+ }
+ //
+ Task::execTasks();
+ }
+}
\ No newline at end of file
diff --git a/src/Console/Command/RestoreCommand.php b/src/Console/Command/RestoreCommand.php
new file mode 100644
index 0000000..5c6614d
--- /dev/null
+++ b/src/Console/Command/RestoreCommand.php
@@ -0,0 +1,57 @@
+desc);
+ }
+
+ /**
+ * @param Interactor $io
+ * @return void
+ */
+ public function interact(Interactor $io): void
+ {
+ }
+
+ /**
+ * @return void
+ */
+ public function execute(): void
+ {
+ Log::info("执行 $this->desc");
+
+ // 清理排程文件
+ // 清理缓存文件
+ }
+}
\ No newline at end of file
diff --git a/src/Console/Command/ScriptCommand.php b/src/Console/Command/ScriptCommand.php
new file mode 100644
index 0000000..df3be39
--- /dev/null
+++ b/src/Console/Command/ScriptCommand.php
@@ -0,0 +1,54 @@
+desc);
+ }
+
+ /**
+ * @param Interactor $io
+ * @return void
+ */
+ public function interact(Interactor $io): void
+ {
+ }
+
+ /**
+ * @return void
+ */
+ public function execute(): void
+ {
+ Log::info("执行 $this->desc");
+ }
+}
\ No newline at end of file
diff --git a/src/Console/Console.php b/src/Console/Console.php
new file mode 100644
index 0000000..4f01c06
--- /dev/null
+++ b/src/Console/Console.php
@@ -0,0 +1,102 @@
+argv = $argv;
+ $app = new Application('parse');
+ $args = $app->parse($argv);
+ // 省略参数
+ $filename = str_contains($args->args()[0] ?? '', ':') ? $default : $args->args()[0] ?? $default;
+ unset($app);
+ } catch (Exception $e) {
+ failExit('解析命令行参数错误', ['msg' => $e->getMessage()]);
+ }
+ return $filename;
+ }
+
+ /**
+ * @return void
+ */
+ public function register(): void
+ {
+ $this->app = new Application(getAppName(), getAppVersion());
+ $this->app
+ ->add(new AppCommand(), 'm:a', true) // 模式1
+ ->add(new ScriptCommand(), 'm:s') // 模式2
+ ->add(new RestoreCommand(), 'm:r') // 模式3
+ ->logo($this->logo)
+ ->handle($this->argv);
+ }
+
+}
\ No newline at end of file
diff --git a/src/Device/Device.php b/src/Device/Device.php
new file mode 100644
index 0000000..1d58c90
--- /dev/null
+++ b/src/Device/Device.php
@@ -0,0 +1,64 @@
+loadResource($filename, 'yaml');
+ }
+
+ /**
+ * @use 重写真实路径获取
+ * @param string $filename
+ * @return string
+ */
+ protected function getFilePath(string $filename): string
+ {
+ return str_replace("\\", "/", PROFILE_DEVICE_PATH . $filename);
+ }
+
+ /**
+ * @use 重写真实路径
+ * @param string $filename
+ * @param string $default_filename
+ * @return string
+ */
+// protected function getRealFileName(string $filename, string $default_filename): string
+// {
+// $prefix = str_replace(strrchr($filename, "."), "", $filename) . '_';
+// $new_filename = $prefix . $default_filename;
+// // 自定义设备
+// if (is_file($this->getFilePath($new_filename))) {
+// Log::info('使用自定义设备参数' . $new_filename);
+// return $new_filename;
+// } else {
+// Log::info('使用默认设备参数' . $default_filename);
+// return $default_filename;
+// }
+// }
+}
diff --git a/src/Env/Env.php b/src/Env/Env.php
new file mode 100644
index 0000000..d5fd170
--- /dev/null
+++ b/src/Env/Env.php
@@ -0,0 +1,136 @@
+loadResource($filename, 'json');
+ //
+ $this->app_name = $this->resource->get('project', 'BiliHelper-personal');
+ $this->app_version = $this->resource->get('version', '0.0.0.000000');
+ $this->app_branch = $this->resource->get('branch', 'master');
+ $this->app_source = $this->resource->get('source', 'https://github.com/lkeme/BiliHelper-personal');
+ //
+ $this->inspectConfigure()->inspectExtension();
+ }
+
+ /**
+ * @use 检查是否开启
+ * @return $this
+ */
+ protected function inspectExtension(): Env
+ {
+ $default_extensions = ['curl', 'openssl', 'sockets', 'json', 'zlib', 'mbstring'];
+ foreach ($default_extensions as $extension) {
+ if (!extension_loaded($extension)) {
+ Log::error("检查到项目依赖 $extension 扩展未加载。");
+ Log::error("请在 php.ini中启用 $extension 扩展后重试。");
+ Log::error("程序常见问题请移步 $this->app_source 文档部分查看。");
+ failExit("");
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @use 检查php环境
+ * @return $this
+ */
+ protected function inspectConfigure(): Env
+ {
+ Log::info("欢迎使用 项目: $this->app_name@$this->app_branch 版本: $this->app_version");
+ Log::info("使用说明请移步 $this->app_source 查看");
+
+ if (PHP_SAPI != 'cli') {
+ failExit('Please run this script from command line .');
+ }
+ if (version_compare(PHP_VERSION, '8.0.0', '<')) {
+ failExit('Please upgrade PHP version < 8.0.0 .');
+ }
+ return $this;
+ }
+
+ /**
+ * @use 重写获取路径
+ * @param string $filename
+ * @return string
+ */
+ protected function getFilePath(string $filename): string
+ {
+ return str_replace("\\", "/", APP_RESOURCES_PATH . $filename);
+ }
+
+ /**
+ * @use Check: running in docker?
+ * @return bool
+ */
+ public static function isDocker(): bool
+ {
+ // Win直接跳出
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ return false;
+ }
+ if (!file_exists('/.dockerenv')) {
+ return false;
+ }
+ // 检查/proc/1/cgroup内是否包含"docker"等字符串;
+ if (!is_file('/proc/self/cgroup') || !preg_match('%^\d+:\w+:/(docker|actions_job)/' . preg_quote(gethostname(), '%') . '\w+%sm', file_get_contents('/proc/self/cgroup'))) {
+ return false;
+ }
+// $processStack = explode(PHP_EOL, shell_exec('cat /proc/self/cgroup | grep docker'));
+// $processStack = array_filter($processStack);
+// return count($processStack) > 0;
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/FilterWords/FilterWords.php b/src/FilterWords/FilterWords.php
new file mode 100644
index 0000000..34c2cae
--- /dev/null
+++ b/src/FilterWords/FilterWords.php
@@ -0,0 +1,43 @@
+loadResource($filename, 'json');
+ }
+
+ /**
+ * @use 重写真实路径
+ * @param string $filename
+ * @return string
+ */
+ protected function getFilePath(string $filename): string
+ {
+ return str_replace("\\", "/", APP_RESOURCES_PATH . $filename);
+ }
+}
+
\ No newline at end of file
diff --git a/src/Helpers.php b/src/Helpers.php
new file mode 100644
index 0000000..db5a2ec
--- /dev/null
+++ b/src/Helpers.php
@@ -0,0 +1,181 @@
+get($key, $default, $type);
+}
+
+/**用户
+ * @use 配置写入
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+function setConf(string $key, mixed $value): void
+{
+ Config::getInstance()->set($key, $value);
+}
+
+/**
+ * @use 配置开关获取(大量调用独立抽取)
+ * @param string $key
+ * @param bool $default
+ * @return bool
+ */
+function getEnable(string $key, bool $default = false): bool
+{
+ return getConf($key . '.enable', $default, 'bool');
+}
+
+/**
+ * @use 获取用户相关信息(Login)
+ * @param string $key
+ * @return mixed
+ */
+function getU(string $key): mixed
+{
+ $value = '';
+ $fillable = ['username', 'password', 'uid', 'csrf', 'cookie', 'access_token', 'refresh_token', 'sid'];
+ if (in_array($key, $fillable)) {
+ $value = Cache::get('auth_' . $key, 'Login');
+ }
+ return $value ?: '';
+}
+
+/**
+ * @use 设置用户相关信息(Login)
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+function setU(string $key, mixed $value): void
+{
+ $fillable = ['username', 'password', 'uid', 'csrf', 'cookie', 'access_token', 'refresh_token', 'sid'];
+ if (in_array($key, $fillable)) {
+ Cache::set('auth_' . $key, $value, 'Login');
+ }
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+
+/**
+ * @use 获取APP名称
+ * @return string
+ */
+function getAppName(): string
+{
+ return Env::getInstance()->app_name;
+}
+
+/**
+ * @use 获取APP版本
+ * @return string
+ */
+function getAppVersion(): string
+{
+ return Env::getInstance()->app_version;
+}
+
+/**
+ * @use 获取APP主页
+ * @return string
+ */
+function getAppHomePage(): string
+{
+ return Env::getInstance()->app_source;
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+
+/**
+ * @use 错误退出
+ * @param $message
+ * @param array $context
+ * @param int $delay
+ * @return void
+ */
+#[NoReturn] function failExit($message, array $context = [], int $delay = 60 * 2): void
+{
+ Log::error($message, $context);
+ // 如果在docker环境中,延迟退出,方便查看错误
+ if (Env::isDocker()) {
+ // 暂停两分钟后自动退出
+ sleep($delay);
+ }
+ die();
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+
+/**
+ * @use 获取设备信息
+ * @param string $key
+ * @param mixed|null $default
+ * @param string $type
+ * @return mixed
+ */
+function getDevice(string $key, mixed $default = null, string $type = 'default'): mixed
+{
+ return Device::getInstance()->get($key, $default, $type);
+}
+
+///**
+// * @use 缓存读取
+// * @param string $key
+// * @param string $extra_name
+// * @return mixed
+// */
+//function getCache(string $key, string $extra_name = ''): mixed
+//{
+// return Cache::getInstance()->_get($key, $extra_name);
+//}
+//
+///**
+// * @use 缓存写入
+// * @param string $key
+// * @param $data
+// * @param string $extra_name
+// */
+//function setCache(string $key, $data, string $extra_name = ''): void
+//{
+// Cache::getInstance()->_set($key, $data, $extra_name);
+//}
diff --git a/src/Log/Log.php b/src/Log/Log.php
new file mode 100644
index 0000000..781b931
--- /dev/null
+++ b/src/Log/Log.php
@@ -0,0 +1,222 @@
+_logger instanceof Logger) {
+ $logger = new Logger('BHP');
+ // 日志等级 DEBUG ?? INFO
+ $handler = new StreamHandler('php://stdout', getEnable('debug') ? Logger::DEBUG : Logger::INFO);
+ $handler->setFormatter(new ColoredLineFormatter(null, null, 'Y-m-d H:i:s'));
+ $logger->pushHandler($handler);
+ $this->_logger = $logger;
+ }
+ return $this->_logger;
+ }
+
+ /**
+ * @use 通用
+ * @param string $level
+ * @param string $msg
+ * @param array $context
+ * @return void
+ */
+ protected function log(string $level, string $msg, array $context = []): void
+ {
+ // 拼装信息内容
+ $message = $this->prefix() . $this->backtrace() . $msg;
+ // 写入文件
+ $this->writeLog($level, $message);
+
+ // DEBUG数据单独处理/不需要回调
+ if ($level == 'DEBUG') {
+ $this->getInstance()->getLogger()->debug($msg, $context);
+ return;
+ }
+ // 匹配等级
+ switch ($level) {
+ case 'INFO':
+ $level_id = Logger::INFO;
+ $this->getInstance()->getLogger()->info($message, $context);
+ break;
+ case 'NOTICE':
+ $level_id = Logger::NOTICE;
+ $this->getInstance()->getLogger()->notice($message, $context);
+ break;
+ case 'WARNING':
+ $level_id = Logger::WARNING;
+ $this->getInstance()->getLogger()->warning($message, $context);
+ break;
+ case 'ERROR':
+ $level_id = Logger::ERROR;
+ $this->getInstance()->getLogger()->error($message, $context);
+ break;
+ default:
+ $level_id = Logger::CRITICAL;
+ $this->getInstance()->getLogger()->critical($message, $context);
+ break;
+ }
+ // 回调
+ $this->callback($level_id, $level, $message);
+ }
+
+ /**
+ * @use 错误
+ * @param mixed $message
+ * @param array $context
+ * @return void
+ */
+ public static function error(mixed $message, array $context = []): void
+ {
+ self::getInstance()->log('ERROR', $message, $context);
+ }
+
+ /**
+ * @use 警告
+ * @param mixed $message
+ * @param array $context
+ * @return void
+ */
+ public static function warning(mixed $message, array $context = []): void
+ {
+ self::getInstance()->log('WARNING', $message, $context);
+ }
+
+ /**
+ * @use 提醒
+ * @param mixed $message
+ * @param array $context
+ * @return void
+ */
+ public static function notice(mixed $message, array $context = []): void
+ {
+ self::getInstance()->log('NOTICE', $message, $context);
+ }
+
+ /**
+ * @use 信息
+ * @param mixed $message
+ * @param array $context
+ * @return void
+ */
+ public static function info(mixed $message, array $context = []): void
+ {
+ self::getInstance()->log('INFO', $message, $context);
+ }
+
+ /**
+ * @use 调试
+ * @param mixed $message
+ * @param array $context
+ * @return void
+ */
+ public static function debug(mixed $message, array $context = []): void
+ {
+ self::getInstance()->log('DEBUG', $message, $context);
+ }
+
+ /**
+ * @use 堆栈
+ * @return string
+ */
+ protected function backtrace(): string
+ {
+ $backtraces = debug_backtrace();
+ return "(" . pathinfo(basename($backtraces[2]['file']))['filename'] . ") => ";
+ }
+
+ /**
+ * @use 前缀
+ * @return string
+ */
+ protected function prefix(): string
+ {
+ if (getConf('print.multiple')) {
+ // return '[' . (getConf('print.user_identity') ?? getConf('login_account.username')) . ']';
+ return sprintf("[%s]", getConf('print.user_identity') ?? getConf('login_account.username'));
+ }
+ return '';
+ }
+
+ /**
+ * @use 写日志
+ * @param string $type
+ * @param string $message
+ */
+ protected function writeLog(string $type, string $message): void
+ {
+ if (getEnable('log')) {
+ if ($type == 'DEBUG' && !getEnable('debug')) {
+ return;
+ }
+ $filename = PROFILE_LOG_PATH . getConf('login_account.username') . ".log";
+ $date = date('[Y-m-d H:i:s] ');
+ $data = $date . ' Log.' . $type . ' ' . $message . PHP_EOL;
+ file_put_contents($filename, $data, FILE_APPEND);
+ }
+ }
+
+ /**
+ * @use 回调
+ * @param int $levelId
+ * @param string $level
+ * @param mixed $message
+ * @return void
+ */
+ protected function callback(int $levelId, string $level, mixed $message): void
+ {
+ $callback_level = getConf('log.callback_level') ?? Logger::ERROR;
+ if ($levelId >= $callback_level) {
+ $url = str_replace('{account}', $this->prefix(), getConf('log.callback'));
+ $url = str_replace('{level}', $level, $url);
+ $url = str_replace('{message}', urlencode($message), $url);
+ //
+ Request::single('get', str_replace(' ', '%20', $url));
+ }
+ }
+
+}
+
+
+
\ No newline at end of file
diff --git a/src/plugin/Notice.php b/src/Notice/Notice.php
similarity index 68%
rename from src/plugin/Notice.php
rename to src/Notice/Notice.php
index 67824d4..9dafdd0 100644
--- a/src/plugin/Notice.php
+++ b/src/Notice/Notice.php
@@ -1,75 +1,117 @@
- 'application/json'
];
+
+ /**
+ * @return void
+ */
+ public function init(): void
+ {
+ }
+
+
/**
* @use 推送消息
* @param string $type
- * @param string $result
+ * @param string $msg
+ * @return void
*/
- public static function push(string $type, string $result = '')
+ public static function push(string $type, string $msg = ''): void
{
- if (!getEnable('notify')) {
- return;
+ // 开关
+ if (!getEnable('notify')) return;
+ // 过滤词
+ if (self::getInstance()->filterMsgWords($msg)) return;
+ //
+ self::getInstance()->sendInfo($type, $msg);
+ }
+
+
+ /**
+ * @use 发送消息
+ * @param string $type
+ * @param string $msg
+ * @return void
+ */
+ protected function sendInfo(string $type, string $msg): void
+ {
+ $info = $this->fillContent($type, $msg);
+ //
+ if (getConf('notify_sct.sctkey')) {
+ $this->sctSend($info);
}
- if (self::filterResultWords($result)) {
- return;
+ if (getConf('notify_sc.sckey')) {
+ $this->scSend($info);
+ }
+ if (getConf('notify_telegram.bottoken') && getConf('notify_telegram.chatid')) {
+ $this->teleSend($info);
+ }
+ if (getConf('notify_dingtalk.token')) {
+ $this->dingTalkSend($info);
+ }
+ if (getConf('notify_pushplus.token')) {
+ $this->pushPlusSend($info);
+ }
+ if (getConf('notify_gocqhttp.target_qq') && getConf('notify_gocqhttp.token') && getConf('notify_gocqhttp.url')) {
+ $this->goCqhttp($info);
+ }
+ if (getConf('notify_debug.token') && getConf('notify_debug.url')) {
+ $this->debug($info);
+ }
+ if (getConf('notify_we_com.token')) {
+ $this->weCom($info);
+ }
+ if (getConf('notify_we_com_app.corp_id') && getConf('notify_we_com_app.corp_secret') && getConf('notify_we_com_app.agent_id')) {
+ $this->weComApp($info);
+ }
+ if (getConf('notify_feishu.token')) {
+ $this->feiShuSend($info);
}
- $uname = getConf('uname', 'print') ?? getConf('username', 'login.account');
- self::sendInfoHandle($type, $uname, $result);
}
/**
* @use 过滤信息
- * @param string $result
+ * @param string $msg
* @return bool
*/
- private static function filterResultWords(string $result): bool
+ protected function filterMsgWords(string $msg): bool
{
- self::loadJsonData();
- $default_words = self::$store->get("Notice.default");
- $custom_words = explode(',', getConf('filter_words', 'notify'));
+ $default_words = FilterWords::getInstance()->get("Notice.default");
+ $custom_words = explode(',', getConf('notify.filter_words'));
$total_words = array_merge($default_words, $custom_words);
foreach ($total_words as $word) {
if (empty($word)) continue;
- if (str_contains($result, $word)) {
+ if (str_contains($msg, $word)) {
return true;
}
}
@@ -77,51 +119,52 @@ class Notice
}
/**
- * @use 处理信息
+ * @use 填充消息
* @param string $type
- * @param string $uname
- * @param string $result
- * @return bool
+ * @param string $msg
+ * @return array
*/
- private static function sendInfoHandle(string $type, string $uname, string $result): bool
+ protected function fillContent(string $type, string $msg): array
{
$now_time = date('Y-m-d H:i:s');
+ $uname = getConf('print.uname') ?? getConf('login_account.username');
+
$info = match ($type) {
'update' => [
'title' => '版本更新通知',
- 'content' => "[$now_time] 用户: $uname 程序更新通知: $result"
+ 'content' => "[$now_time] 用户: $uname 程序更新通知: $msg"
],
'anchor' => [
'title' => '天选时刻获奖记录',
- 'content' => "[$now_time] 用户: $uname 在天选时刻中获得: $result"
+ 'content' => "[$now_time] 用户: $uname 在天选时刻中获得: $msg"
],
'raffle' => [
'title' => '实物奖励获奖纪录',
- 'content' => "[$now_time] 用户: $uname 在实物奖励中获得: $result"
+ 'content' => "[$now_time] 用户: $uname 在实物奖励中获得: $msg"
],
'gift' => [
'title' => '活动礼物获奖纪录',
- 'content' => "[$now_time] 用户: $uname 在活动礼物中获得: $result"
+ 'content' => "[$now_time] 用户: $uname 在活动礼物中获得: $msg"
],
'storm' => [
'title' => '节奏风暴获奖纪录',
- 'content' => "[$now_time] 用户: $uname 在节奏风暴中获得: $result"
+ 'content' => "[$now_time] 用户: $uname 在节奏风暴中获得: $msg"
],
'cookieRefresh' => [
'title' => 'Cookie刷新',
- 'content' => "[$now_time] 用户: $uname 刷新Cookie: $result"
+ 'content' => "[$now_time] 用户: $uname 刷新Cookie: $msg"
],
'todaySign' => [
'title' => '每日签到',
- 'content' => "[$now_time] 用户: $uname 签到: $result"
+ 'content' => "[$now_time] 用户: $uname 签到: $msg"
],
'banned' => [
'title' => '任务小黑屋',
- 'content' => "[$now_time] 用户: $uname 小黑屋: $result"
+ 'content' => "[$now_time] 用户: $uname 小黑屋: $msg"
],
'error' => [
'title' => '程序运行错误',
- 'content' => "[$now_time] 用户: $uname 错误详情: $result"
+ 'content' => "[$now_time] 用户: $uname 错误详情: $msg"
],
'key_expired' => [
'title' => '监控KEY异常',
@@ -129,69 +172,29 @@ class Notice
],
'capsule_lottery' => [
'title' => '直播扭蛋抽奖活动',
- 'content' => "[$now_time] 用户: $uname 详情: $result"
+ 'content' => "[$now_time] 用户: $uname 详情: $msg"
],
'activity_lottery' => [
'title' => '主站九宫格抽奖活动',
- 'content' => "[$now_time] 用户: $uname 详情: $result"
+ 'content' => "[$now_time] 用户: $uname 详情: $msg"
],
'jury_leave_office' => [
'title' => '已卸任風機委員',
- 'content' => "[$now_time] 用户: $uname 详情: $result ,请及时关注風機委員连任状态哦~"
+ 'content' => "[$now_time] 用户: $uname 详情: $msg ,请及时关注風機委員连任状态哦~"
],
'jury_auto_apply' => [
'title' => '嘗試連任風機委員',
- 'content' => "[$now_time] 用户: $uname 详情: $result ,请及时关注風機委員连任状态哦~"
+ 'content' => "[$now_time] 用户: $uname 详情: $msg ,请及时关注風機委員连任状态哦~"
],
default => [
'title' => '推送消息异常记录',
- 'content' => "[$now_time] 用户: $uname 推送消息key错误: $type->$result"
+ 'content' => "[$now_time] 用户: $uname 推送消息key错误: $type->$msg"
],
};
-
// 添加前缀
$info['title'] = "【BHP】" . $info['title'];
-
- self::sendLog($info);
- return true;
- }
-
- /**
- * @use 推送消息
- * @param array $info
- */
- private static function sendLog(array $info)
- {
- if (getConf('sctkey', 'notify.sct')) {
- self::sctSend($info);
- }
- if (getConf('sckey', 'notify.sc')) {
- self::scSend($info);
- }
- if (getConf('bottoken', 'notify.telegram') && getConf('chatid', 'notify.telegram')) {
- self::teleSend($info);
- }
- if (getConf('token', 'notify.dingtalk')) {
- self::dingTalkSend($info);
- }
- if (getConf('token', 'notify.pushplus')) {
- self::pushPlusSend($info);
- }
- if (getConf('target_qq', 'notify.gocqhttp') && getConf('token', 'notify.gocqhttp') && getConf('url', 'notify.gocqhttp')) {
- self::goCqhttp($info);
- }
- if (getConf('token', 'notify.debug') && getConf('url', 'notify.debug')) {
- self::debug($info);
- }
- if (getConf('token', 'notify.we_com')) {
- self::weCom($info);
- }
- if (getConf('corp_id', 'notify.we_com_app') && getConf('corp_secret', 'notify.we_com_app') && getConf('agent_id', 'notify.we_com_app')) {
- self::weComApp($info);
- }
- if (getConf('token', 'notify.feishu')) {
- self::feiShuSend($info);
- }
+ //
+ return $info;
}
/**
@@ -199,10 +202,10 @@ class Notice
* @doc https://developers.dingtalk.com/document/robots/custom-robot-access
* @param array $info
*/
- private static function dingTalkSend(array $info)
+ protected function dingTalkSend(array $info): void
{
Log::info('使用DingTalk机器人推送消息');
- $url = 'https://oapi.dingtalk.com/robot/send?access_token=' . getConf('token', 'notify.dingtalk');
+ $url = 'https://oapi.dingtalk.com/robot/send?access_token=' . getConf('notify_dingtalk.token');
$payload = [
'msgtype' => 'markdown',
'markdown' => [
@@ -213,7 +216,7 @@ class Notice
$headers = [
'Content-Type' => 'application/json;charset=utf-8'
];
- $raw = Curl::put('other', $url, $payload, $headers);
+ $raw = Request::put('other', $url, $payload, $headers);
$de_raw = json_decode($raw, true);
if ($de_raw['errcode'] == 0) {
Log::notice("推送消息成功: {$de_raw['errmsg']}");
@@ -227,17 +230,17 @@ class Notice
* @doc https://core.telegram.org/bots/api#sendmessage
* @param array $info
*/
- private static function teleSend(array $info)
+ protected function teleSend(array $info): void
{
Log::info('使用Tele机器人推送消息');
- $base_url = getConf('url', 'notify.telegram') ?: 'https://api.telegram.org/bot';
- $url = $base_url . getConf('bottoken', 'notify.telegram') . '/sendMessage';
+ $base_url = getConf('notify_telegram.url') ?: 'https://api.telegram.org/bot';
+ $url = $base_url . getConf('notify_telegram.bottoken') . '/sendMessage';
$payload = [
- 'chat_id' => getConf('chatid', 'notify.telegram'),
+ 'chat_id' => getConf('notify_telegram.chatid'),
'text' => $info['content']
];
// {"ok":true,"result":{"message_id":7,"from":{"id":,"is_bot":true,"first_name":"","username":""},"chat":{"id":,"first_name":"","username":"","type":"private"},"date":,"text":""}}
- $raw = Curl::post('other', $url, $payload);
+ $raw = Request::post('other', $url, $payload);
$de_raw = json_decode($raw, true);
if ($de_raw['ok'] && array_key_exists('message_id', $de_raw['result'])) {
Log::notice("推送消息成功: MSG_ID->{$de_raw['result']['message_id']}");
@@ -251,15 +254,15 @@ class Notice
* @use https://sc.ftqq.com/
* @param array $info
*/
- private static function scSend(array $info)
+ protected function scSend(array $info): void
{
Log::info('使用ServerChan推送消息');
- $url = 'https://sc.ftqq.com/' . getConf('sckey', 'notify.sc') . '.send';
+ $url = 'https://sc.ftqq.com/' . getConf('notify_sc.sckey') . '.send';
$payload = [
'text' => $info['title'],
'desp' => $info['content'],
];
- $raw = Curl::post('other', $url, $payload);
+ $raw = Request::post('other', $url, $payload);
$de_raw = json_decode($raw, true);
if ($de_raw['errno'] == 0) {
@@ -274,15 +277,15 @@ class Notice
* @doc https://sct.ftqq.com/
* @param array $info
*/
- private static function sctSend(array $info)
+ protected function sctSend(array $info): void
{
Log::info('使用ServerChan(Turbo)推送消息');
- $url = 'https://sctapi.ftqq.com/' . getConf('sctkey', 'notify.sct') . '.send';
+ $url = 'https://sctapi.ftqq.com/' . getConf('notify_sct.sctkey') . '.send';
$payload = [
'text' => $info['title'],
'desp' => $info['content'],
];
- $raw = Curl::post('other', $url, $payload);
+ $raw = Request::post('other', $url, $payload);
$de_raw = json_decode($raw, true);
// {'message': '[AUTH]用户不存在或者权限不足', 'code': 40001, 'info': '用户不存在或者权限不足', 'args': [None]}
// {'code': 0, 'message': '', 'data': {'pushid': 'xxxx', 'readkey': 'xxxxx', 'error': 'SUCCESS', 'errno': 0}}
@@ -298,16 +301,16 @@ class Notice
* @doc http://www.pushplus.plus/doc/
* @param array $info
*/
- private static function pushPlusSend(array $info)
+ protected function pushPlusSend(array $info): void
{
Log::info('使用PushPlus酱推送消息');
$url = 'https://www.pushplus.plus/send';
$payload = [
- 'token' => getConf('token', 'notify.pushplus'),
+ 'token' => getConf('notify_pushplus.token'),
'title' => $info['title'],
'content' => $info['content']
];
- $raw = Curl::put('other', $url, $payload, self::$json_headers);
+ $raw = Request::put('other', $url, $payload, $this->headers);
// {"code":200,"msg":"请求成功","data":"发送消息成功"}
$de_raw = json_decode($raw, true);
if ($de_raw['code'] == 200) {
@@ -322,16 +325,16 @@ class Notice
* @doc https://docs.go-cqhttp.org/api/
* @param array $info
*/
- private static function goCqhttp(array $info)
+ protected function goCqhttp(array $info): void
{
Log::info('使用GoCqhttp推送消息');
- $url = getConf('url', 'notify.gocqhttp');
+ $url = getConf('notify_gocqhttp.url');
$payload = [
- 'access_token' => getConf('token', 'notify.gocqhttp'),
- 'user_id' => getConf('target_qq', 'notify.gocqhttp'),
+ 'access_token' => getConf('notify_gocqhttp.token'),
+ 'user_id' => getConf('notify_gocqhttp.target_qq'),
'message' => $info['content']
];
- $raw = Curl::get('other', $url, $payload);
+ $raw = Request::get('other', $url, $payload);
// {"data":{"message_id":123456},"retcode":0,"status":"ok"}
$de_raw = json_decode($raw, true);
if ($de_raw['retcode'] == 0) {
@@ -346,20 +349,20 @@ class Notice
* @doc https://localhost:8921/doc
* @param array $info
*/
- private static function debug(array $info)
+ protected function debug(array $info): void
{
Log::info('使用Debug推送消息');
- $url = getConf('url', 'notify.debug');
+ $url = getConf('notify_debug.url');
$payload = [
- 'receiver' => getConf('token', 'notify.debug'),
+ 'receiver' => getConf('notify_debug.token'),
'title' => $info['title'],
'body' => $info['content'],
'url' => '',
];
- $raw = Curl::post('other', $url, $payload);
+ $raw = Request::post('other', $url, $payload);
$de_raw = json_decode($raw, true);
// {"success": true, "msg": null, "data": {"errcode": 0, "errmsg": "ok", "msgid": 1231, "token": "456"}}
- if ($de_raw['success'] == true) {
+ if ($de_raw['success']) {
Log::notice("推送消息成功: {$de_raw['data']['msgid']}");
} else {
Log::warning("推送消息失败: $raw");
@@ -371,10 +374,10 @@ class Notice
* @doc https://open.work.weixin.qq.com/api/doc/90000/90136/91770
* @param array $info
*/
- private static function weCom(array $info)
+ protected function weCom(array $info): void
{
Log::info('使用weCom推送消息');
- $url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=' . getConf('token', 'notify.we_com');
+ $url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=' . getConf('notify_we_com.token');
$payload = [
'msgtype' => 'markdown',
@@ -382,7 +385,7 @@ class Notice
'content' => "{$info['title']} \n\n{$info['content']}"
]
];
- $raw = Curl::put('other', $url, $payload, self::$json_headers);
+ $raw = Request::put('other', $url, $payload, $this->headers);
// {"errcode":0,"errmsg":"ok","created_at":"1380000000"}
$de_raw = json_decode($raw, true);
if ($de_raw['errcode'] == 0) {
@@ -397,13 +400,13 @@ class Notice
* @doc https://open.work.weixin.qq.com/wwopen/devtool/interface?doc_id=10167
* @param array $info
*/
- private static function weComApp(array $info)
+ protected function weComApp(array $info): void
{
Log::info('使用weComApp推送消息');
- $corp_id = getConf('corp_id', 'notify.we_com_app');
- $corp_secret = getConf('corp_secret', 'notify.we_com_app');
- $agent_id = getConf('agent_id', 'notify.we_com_app');
- $to_user = getConf('to_user', 'notify.we_com_app') ?? '@all';
+ $corp_id = getConf('notify_we_com_app.corp_id');
+ $corp_secret = getConf('notify_we_com_app.corp_secret' );
+ $agent_id = getConf('notify_we_com_app.agent_id');
+ $to_user = getConf('notify_we_com_app.to_user') ?? '@all';
// 获取token
$url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken';
@@ -411,7 +414,7 @@ class Notice
'corpid' => $corp_id,
'corpsecret' => $corp_secret,
];
- $raw = Curl::get('other', $url, $payload);
+ $raw = Request::get('other', $url, $payload);
// {"errcode":0,"errmsg":"ok","created_at":"1380000000"}
$de_raw = json_decode($raw, true);
if ($de_raw['errcode'] == 0) {
@@ -430,7 +433,7 @@ class Notice
'content' => "{$info['title']} \n\n{$info['content']}"
]
];
- $raw = Curl::put('other', $url, $payload, self::$json_headers);
+ $raw = Request::put('other', $url, $payload, $this->headers);
// {"errcode":0,"errmsg":"ok","created_at":"1380000000"}
$de_raw = json_decode($raw, true);
if ($de_raw['errcode'] == 0) {
@@ -446,10 +449,10 @@ class Notice
* @doc https://developers.dingtalk.com/document/robots/custom-robot-access
* @param array $info
*/
- private static function feiShuSend(array $info)
+ protected function feiShuSend(array $info): void
{
Log::info('使用飞书webhook机器人推送消息');
- $url = 'https://open.feishu.cn/open-apis/bot/v2/hook/' . getConf('token', 'notify.feishu');
+ $url = 'https://open.feishu.cn/open-apis/bot/v2/hook/' . getConf('notify_feishu.token');
$payload = [
'msg_type' => 'text',
'content' => [
@@ -459,7 +462,7 @@ class Notice
$headers = [
'Content-Type' => 'application/json;charset=utf-8'
];
- $raw = Curl::put('other', $url, $payload, $headers);
+ $raw = Request::put('other', $url, $payload, $headers);
$de_raw = json_decode($raw, true);
if ($de_raw['StatusCode'] == 0) {
Log::notice("推送消息成功: {$de_raw['StatusCode']}");
@@ -468,4 +471,7 @@ class Notice
}
}
-}
\ No newline at end of file
+
+}
+
+
\ No newline at end of file
diff --git a/src/exception/TaskException.php b/src/Plugin/BasePlugin.php
similarity index 67%
rename from src/exception/TaskException.php
rename to src/Plugin/BasePlugin.php
index 180cf3d..1e48891 100644
--- a/src/exception/TaskException.php
+++ b/src/Plugin/BasePlugin.php
@@ -1,33 +1,25 @@
- __CLASS__, // Hook名称
+ 'name' => 'name', // 插件名称
+ 'version' => 'version', // 插件版本
+ 'desc' => 'desc', // 插件描述
+ 'author' => 'Lkeme',// 作者
+ 'priority' => 0, // 插件优先级
+ 'cycle' => 'cycle', // 运行周期
+ ];
+
+ /**
+ * @var array|null
+ */
+ protected ?array $info;
+
+ /**
+ * @use 设置Hook
+ * @param string $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setHook(string $value, string $key = 'hook'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 设置名称
+ * @param string $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setName(string $value, string $key = 'name'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 设置版本
+ * @param string $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setVersion(string $value, string $key = 'version'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 设置描述
+ * @param string $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setDesc(string $value, string $key = 'desc'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 设置优先级
+ * @param int $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setPriority(int $value, string $key = 'priority'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 设置运行周期
+ * @param string $value
+ * @param string $key
+ * @return $this
+ */
+ protected function setCycle(string $value, string $key = 'name'): static
+ {
+ $this->info[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @use 返回插件信息
+ * @return array|string[]
+ */
+ public function getPluginInfo(): array
+ {
+ return $this->info;
+ }
+
+ /**
+ * @param $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $name;
+ }
+
+ /**
+ * @param $name
+ * @param $value
+ * @return string
+ */
+ public function __set($name, $value)
+ {
+ return $name . $value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/exception/SingletonException.php b/src/Plugin/BasePluginRW.php
similarity index 65%
rename from src/exception/SingletonException.php
rename to src/Plugin/BasePluginRW.php
index c50ae28..48524e0 100644
--- a/src/exception/SingletonException.php
+++ b/src/Plugin/BasePluginRW.php
@@ -1,33 +1,27 @@
-detector();
+ }
+
+ /**
+ * @return array
+ */
+ public static function getPlugins(): array
+ {
+ return self::getInstance()->_plugins;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getPluginsPriority(): array
+ {
+ return self::getInstance()->_priority;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getPluginsStaff(): array
+ {
+ return self::getInstance()->_staff;
+ }
+
+ /**
+ * @use 这个是全局使用的触发钩子动作方法
+ * @param string $hook
+ * @param string $data
+ * @return string
+ */
+ public function trigger(string $hook, mixed...$params): string
+ {
+ // 首先需要判断一下$hook 存不存在
+ if (isset($this->_staff[$hook]) && is_array($this->_staff[$hook]) && count($this->_staff[$hook]) > 0) {
+ $plugin_func_result = '';
+ // 如果存在定义 $plugin_func_result
+ foreach ($this->_staff[$hook] as $staff) {
+ // 如果只是记录 请不要返回
+ $plugin_func_result = '';
+ $class = &$staff[0]; // 引用过来的类
+ $method = $staff[1]; // 类下面的方法
+ if (method_exists($class, $method)) {
+ $func_result = $class->$method(...$params);
+ if (is_numeric($func_result)) {
+ // 这里判断返回值是不是字符串,如果不是将不进行返回到页面上
+ $plugin_func_result .= $func_result;
+ }
+ }
+ }
+ }
+ return $plugin_func_result ?? '';
+ }
+
+ /**
+ * @use 这里是在插件中使用的方法 用来注册插件
+ * @param object $class_obj
+ * @param string $method
+ * @return void
+ */
+ public function register(object &$class_obj, string $method): void
+ {
+ $hook = get_class($class_obj);
+ // 获取类名和方法名链接起来做下标
+ $func_class = $hook . '->' . $method;
+ // 将类和方法放入监听数组中 以$func_class做下标
+ $this->_staff[$hook][$func_class] = array(&$class_obj, $method);
+ // 每个插件必须实现的 getPluginInfo 获取插件信息
+ $this->addPluginInfo($hook, $class_obj->getPluginInfo());
+ }
+
+ /**
+ * @param string $hook
+ * @param array $info
+ * @return void
+ */
+ protected function addPluginInfo(string $hook, array $info): void
+ {
+ $this->validatePlugins($hook, $info);
+ //
+ $this->_plugins[$hook] = $info;
+ $this->_priority[] = $info['priority'];
+ }
+
+ /**
+ * @param string $hook
+ * @param array $info
+ * @return void
+ */
+ protected function validatePlugins(string $hook, array $info): void
+ {
+ // 插件信息缺失
+ $fillable = ['hook', 'name', 'version', 'desc', 'priority', 'cycle'];
+ foreach ($fillable as $val) {
+ if (!array_key_exists($val, $info)) {
+ failExit("加载 $hook 插件错误,插件信息缺失,请检查修正.");
+ }
+ }
+ // 插件名冲突
+ if (array_key_exists($hook, $this->_plugins)) {
+ failExit("加载 $hook 插件错误,插件名冲突,请检查修正.");
+ }
+ // 插件优先级冲突
+ if (in_array($info['priority'], $this->_priority)) {
+ failExit("加载 $hook 插件错误,插件优先级冲突,请检查修正.");
+ }
+ // 插件优先级定义
+ if ($info['priority'] < 1000) {
+ failExit("加载 $hook 插件错误,插件优先级定义错误,请检查修正.");
+ }
+ }
+
+ /**
+ * @use 初始化插件(all)
+ * @return void
+ */
+ protected function detector(): void
+ {
+ // 主要功能为将插件需要执行功能放入 $_staff
+ $plugins = $this->getActivePlugins();
+ foreach ($plugins as $plugin) {
+ // 这里将所有插件践行初始化
+ // 路径请自己注意
+ if (@file_exists($plugin['path'])) {
+ include_once($plugin['path']);
+ // 此时设定 文件夹名称 文件名称 类名 是统一的 如果想设定不统一请自己在get_active_plugins()内进行实现
+ $class = $plugin['name'];
+ if (class_exists($class)) {
+ // 初始化所有插件类
+ new $class($this);
+ }
+ }
+ }
+ $this->sortPlugins();
+ }
+
+ /**
+ * @use 获取插件信息(all)
+ * @use 假定了插件在根目录的/plugin
+ * @use 假定插件的入口和插件文件夹的名字是一样的
+ * @use 注意:这个执行文件我放在了根目录 以下路径请根据实际情况获取
+ * @return array
+ */
+ protected function getActivePlugins(): array
+ {
+ $plugins = [];
+ $plugin_dir_name_arr = scandir(APP_PLUGIN_PATH);
+ //
+ foreach ($plugin_dir_name_arr as $_ => $v) {
+ if ($v == "." || $v == "..") {
+ continue;
+ }
+ // /plugin/Test/Test.php
+ if (is_dir(APP_PLUGIN_PATH . $v)) {
+ $path = APP_PLUGIN_PATH . $v . DIRECTORY_SEPARATOR . $v . '.php';
+ $plugins[] = ['name' => $v, 'path' => $path];
+ }
+ }
+ //
+ return $plugins;
+ }
+
+ /**
+ * @use 插件排序
+ * @param string $column_key
+ * @param int $sort_order
+ * @return void
+ */
+ protected function sortPlugins(string $column_key = 'priority', int $sort_order = SORT_ASC): void
+ {
+ $arr = array_column($this->_plugins, $column_key);
+ array_multisort($arr, $sort_order, $this->_plugins);
+ }
+
+}
\ No newline at end of file
diff --git a/src/Request/MultiRequest.php b/src/Request/MultiRequest.php
new file mode 100644
index 0000000..376e58b
--- /dev/null
+++ b/src/Request/MultiRequest.php
@@ -0,0 +1,164 @@
+client = $client;
+ }
+
+ /**
+ * @param Client $client
+ * @return MultiRequest
+ */
+ public static function newMultiRequest(Client $client): MultiRequest
+ {
+ return new self($client);
+ }
+
+ /**
+ * @param array $headers
+ * @return $this
+ */
+ public function withHeaders(array $headers): static
+ {
+ $this->headers = array_merge($this->headers, $headers);
+ return $this;
+ }
+
+ /**
+ * @param $options
+ * @return $this
+ */
+ public function withOptions($options): static
+ {
+ $this->options = $options;
+ return $this;
+ }
+
+ /**
+ * @param $concurrency
+ * @return $this
+ */
+ public function concurrency($concurrency): static
+ {
+ $this->concurrency = $concurrency;
+ return $this;
+ }
+
+ /**
+ * @param Closure $success
+ * @return $this
+ */
+ public function success(Closure $success): static
+ {
+ $this->successCallback = $success;
+ return $this;
+ }
+
+ /**
+ * @param Closure $error
+ * @return $this
+ */
+ public function error(Closure $error): static
+ {
+ $this->errorCallback = $error;
+ return $this;
+ }
+
+ /**
+ * @param array $urls
+ * @return $this
+ */
+ public function urls(array $urls): static
+ {
+ $this->urls = $urls;
+ return $this;
+ }
+
+ /**
+ * @return void
+ */
+ public function get(): void
+ {
+ $this->method = 'GET';
+ $this->send();
+ }
+
+ /**
+ * @return void
+ */
+ public function post(): void
+ {
+ $this->method = 'POST';
+ $this->send();
+ }
+
+ /**
+ * @return void
+ */
+ protected function send(): void
+ {
+ $client = $this->client;
+
+ $requests = function ($urls) use ($client) {
+ foreach ($urls as $url) {
+ if (is_string($url)) {
+ yield new PRequest($this->method, $url, $this->headers);
+ } else {
+ yield $url;
+ }
+ }
+ };
+
+ $pool = new Pool($client, $requests($this->urls), [
+ 'concurrency' => $this->concurrency,
+ 'fulfilled' => $this->successCallback,
+ 'rejected' => $this->errorCallback,
+ 'options' => $this->options
+ ]);
+
+ $promise = $pool->promise();
+ $promise->wait();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/Request/Request.php b/src/Request/Request.php
new file mode 100644
index 0000000..2c944ef
--- /dev/null
+++ b/src/Request/Request.php
@@ -0,0 +1,565 @@
+retry_times = range($min_rt, $max_rt);
+ $this->timeout = $timeout;
+ }
+
+ /**
+ * 初始化客户端
+ * @param string $request_id
+ * @param array $config
+ * @return Request
+ */
+ protected function withClient(string $request_id, array $config = []): static
+ {
+ if (is_null($this->client)) {
+ $this->client = new Client($config);
+ }
+ // clone 对象深拷贝
+ $this->setRequest($request_id, 'client', clone $this->client);
+ return $this;
+ }
+
+ /**
+ * @use 请求地址
+ * @param string $request_id
+ * @param string $url
+ * @return $this
+ */
+ protected function withUrl(string $request_id, string $url): static
+ {
+ $this->setRequest($request_id, 'url', $url);
+ return $this;
+ }
+
+ /**
+ * @use 请求方式
+ * @param string $request_id
+ * @param string $method
+ * @return $this
+ */
+ protected function withMethod(string $request_id, string $method): static
+ {
+ $this->setRequest($request_id, 'method', $method);
+ return $this;
+ }
+
+ /**
+ * @use 请求头
+ * @param string $request_id
+ * @param string $os
+ * @param array $headers
+ * @return $this
+ */
+ protected function withHeaders(string $request_id, string $os = 'app', array $headers = []): static
+ {
+ // 从缓存中加载 如果存在就赋值 否则默认值
+ if (is_null($this->buvid)) {
+ $this->buvid = ($tmp = Cache::get('buvid')) ? $tmp : Fake::buvid();
+ Cache::set('buvid', $this->buvid);
+ }
+ //
+ $app_headers = [
+ 'env' => 'prod',
+ 'APP-KEY' => 'android',
+ 'Buvid' => $this->buvid,
+ 'Accept' => '*/*',
+ 'Accept-Encoding' => 'gzip',
+ 'Accept-Language' => 'zh-cn',
+ 'Connection' => 'keep-alive',
+ 'User-Agent' => getDevice('platform.headers.app_ua'),
+ // 'Content-Type' => 'application/x-www-form-urlencoded',
+ // 'User-Agent' => 'Mozilla/5.0 BiliDroid/5.51.1 (bbcallen@gmail.com)',
+ // 'Referer' => 'https://live.bilibili.com/',
+ ];
+ //
+ $pc_headers = [
+ 'Accept' => "application/json, text/plain, */*",
+ 'Accept-Encoding' => 'gzip, deflate',
+ 'Accept-Language' => "zh-CN,zh;q=0.9",
+ 'User-Agent' => getDevice('platform.headers.pc_ua'),
+ // 'Content-Type' => 'application/x-www-form-urlencoded',
+ // 'Referer' => 'https://live.bilibili.com/',
+ ];
+ //
+ $other_headers = [
+ 'User-Agent' => getDevice('platform.headers.other_ua'),
+ ];
+ $default_headers = ${$os . "_headers"} ?? $other_headers;
+ if (in_array($os, ['app', 'pc']) && getU('cookie') != "") {
+ $default_headers['Cookie'] = getU('cookie');
+ }
+ //
+ $this->setRequest($request_id, 'headers', array_merge($default_headers, $headers));
+ return $this;
+ }
+
+ /**
+ * @use 客户端配置
+ * @param string $request_id
+ * @param array $add_options
+ * @param array $headers
+ * @param float $timeout
+ * @return Request
+ */
+ protected function withOptions(string $request_id, array $add_options, array $headers = [], float $timeout = 30.0): static
+ {
+ $default_options = [
+ // 'connect_timeout' => 10,
+ // 'debug' => false,
+ 'headers' => $headers,
+ 'timeout' => $timeout,
+ 'http_errors' => false,
+ 'verify' => getConf('network_ssl.verify', false, 'bool'),
+ ];
+ if (getEnable('network_proxy')) {
+ $default_options['proxy'] = getConf('network_proxy.proxy');
+ }
+ //
+ $this->setRequest($request_id, 'options', array_merge($default_options, $add_options));
+ return $this;
+
+ }
+
+ /**
+ * @use 处理请求
+ * @param string $request_id
+ * @return ResponseInterface
+ */
+ protected function handle(string $request_id): ResponseInterface
+ {
+ $client = (object)$this->getRequest($request_id, 'client');
+ $url = (string)$this->getRequest($request_id, 'url');
+ $method = (string)$this->getRequest($request_id, 'method');
+ $options = (array)$this->getRequest($request_id, 'options');
+ //
+ foreach ($this->retry_times as $retry) {
+ try {
+ // $response = $this->client->get($url, $options);
+ // $response = $this->client->$method($url, $options);
+ // 稍有性能损耗,不过影响不大
+ $response = call_user_func_array([$client, $method], [$url, $options]);
+ if (is_null($response) or empty($response)) throw new ResponseEmptyException("Value IsEmpty");
+ return $response;
+ } catch (RequestException $e) {
+ // var_dump($e->getRequest());
+ if ($e->hasResponse()) var_dump($e->getResponse());
+ } catch (ResponseEmptyException|Exception $e) {
+ // $e->getHandlerContext()
+ // var_dump($e);
+ }
+ Log::warning("Target -> URL: $url METHOD: $method");
+ Log::warning("CURl -> RETRY: $retry ERROR: {$e->getMessage()} ERRNO: {$e->getCode()} STATUS: Waiting for recovery!");
+ sleep(15);
+ }
+ failExit('网络异常,超出最大尝试次数,退出程序~');
+ }
+
+ /**
+ * @use Method GetResponse
+ * @param string $os
+ * @param string $url
+ * @param array $params
+ * @param array $headers
+ * @param float $timeout
+ * @return ResponseInterface
+ */
+ protected static function _getResponse(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0): ResponseInterface
+ {
+ //
+ $rid = self::getInstance()->startRequest();
+ //
+ Log::debug("[GET#$rid] $url ", $params);
+ //
+ $payload['query'] = count($params) ? $params : [];
+ //
+ $response = self::getInstance()
+ ->withClient($rid)
+ ->withUrl($rid, $url)
+ ->withMethod($rid, 'get')
+ ->withHeaders($rid, $os, $headers)
+ ->withOptions($rid, $payload, $headers, $timeout)
+ ->handle($rid);
+ //
+ self::getInstance()->stopRequest($rid);
+ //
+ Log::debug("[GET#$rid] " . $response->getBody());
+ //
+ return $response;
+ }
+
+ /**
+ * @use Method Get
+ * @param mixed ...$params
+ * @return string
+ */
+ protected static function _get(mixed ...$params): string
+ {
+ $response = self::getResponse(...$params);
+ return (string)$response->getBody();
+ }
+
+ /**
+ * @use Method GetJson
+ * @param bool|null $associative
+ * @param mixed ...$params
+ * @return mixed
+ */
+ protected static function _getJson(?bool $associative = null, mixed...$params): mixed
+ {
+ $response = self::get(...$params);
+ // JSON_UNESCAPED_UNICODE 当该参数为 TRUE 时,将返回 array 而非 object 。
+ return json_decode($response, $associative);
+ }
+
+ /**
+ * @use Method PostResponse
+ * @param $os
+ * @param $url
+ * @param array $params
+ * @param array $headers
+ * @param float $timeout
+ * @return ResponseInterface
+ */
+ protected static function _postResponse($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): ResponseInterface
+ {
+ //
+ $rid = self::getInstance()->startRequest();
+ //
+ Log::debug("[POST#$rid] $url ", $params);
+ //
+ $payload['form_params'] = count($params) ? $params : [];
+ //
+ $response = self::getInstance()
+ ->withClient($rid)
+ ->withUrl($rid, $url)
+ ->withMethod($rid, 'post')
+ ->withHeaders($rid, $os, $headers)
+ ->withOptions($rid, $payload, $headers, $timeout)
+ ->handle($rid);
+ //
+ self::getInstance()->stopRequest($rid);
+ //
+ Log::debug("[POST#$rid] " . $response->getBody());
+ //
+ return $response;
+ }
+
+ /**
+ * @use Method Post
+ * @param mixed ...$params
+ * @return string
+ */
+ protected static function _post(mixed ...$params): string
+ {
+ $response = self::postResponse(...$params);
+ return (string)$response->getBody();
+ }
+
+ /**
+ * @use Method PostJson
+ * @param bool|null $associative
+ * @param mixed ...$params
+ * @return mixed
+ */
+ protected static function _postJson(?bool $associative = null, mixed...$params): mixed
+ {
+ $response = self::post(...$params);
+ // JSON_UNESCAPED_UNICODE
+ return json_decode($response, $associative);
+ }
+
+ /**
+ * @use Method PutResponse
+ * @param $os
+ * @param $url
+ * @param array $params
+ * @param array $headers
+ * @param float $timeout
+ * @return ResponseInterface
+ */
+ protected static function _putResponse($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): ResponseInterface
+ {
+ //
+ $rid = self::getInstance()->startRequest();
+ //
+ Log::debug("[PUT#$rid] $url ", $params);
+ //
+ $payload['json'] = count($params) ? $params : [];
+ //
+ $response = self::getInstance()
+ ->withClient($rid)
+ ->withUrl($rid, $url)
+ ->withMethod($rid, 'post')
+ ->withHeaders($rid, $os, $headers)
+ ->withOptions($rid, $payload, $headers, $timeout)
+ ->handle($rid);
+ //
+ self::getInstance()->stopRequest($rid);
+ //
+ Log::debug("[PUT#$rid] " . $response->getBody());
+ //
+ return $response;
+ }
+
+ /**
+ * @use Method Put
+ * @param mixed ...$params
+ * @return string
+ */
+ protected static function _put(mixed ...$params): string
+ {
+ $response = self::putResponse(...$params);
+ return (string)$response->getBody();
+ }
+
+ /**
+ * @use Method PutJson
+ * @param bool|null $associative
+ * @param mixed ...$params
+ * @return mixed
+ */
+ protected static function __putJson(?bool $associative = null, mixed...$params): mixed
+ {
+ $response = self::put(...$params);
+ // JSON_UNESCAPED_UNICODE
+ return json_decode($response, $associative);
+ }
+
+
+ /**
+ * @use 下载文件
+ * @param $os
+ * @param $url
+ * @param string $filepath
+ * @param array $params
+ * @param array $headers
+ * @param float $timeout
+ * @return mixed
+ */
+ public static function download($os, $url, string $filepath, array $params = [], array $headers = [], float $timeout = 600.0): string
+ {
+ $params = array_merge($params, [
+ 'sink' => $filepath,
+ ]);
+ return self::get($os, $url, $params, $headers, $timeout);
+ }
+
+ /**
+ * @use 单次请求
+ * @param string $method
+ * @param string $url
+ * @param array $payload
+ * @param array $headers
+ * @param int $timeout
+ * @return bool|string|null
+ */
+ public static function single(string $method, string $url, array $payload = [], array $headers = [], int $timeout = 10): bool|string|null
+ {
+ Log::debug("[SINGLE] $url ", $payload);
+ //
+ $options = array(
+ 'http' => array(
+ 'method' => strtoupper($method),
+ 'header' => ArrayR::toStr($headers),
+ 'content' => http_build_query($payload),
+ 'timeout' => $timeout,
+ ),
+ );
+ $result = $url ? @file_get_contents($url, false, stream_context_create($options)) : null;
+ //
+ Log::debug("[SINGLE] $result");
+ return $result ?: null;
+ }
+
+ // postAsync getAsync
+ protected static function multiple(string $os, array $tasks = [], float $timeout = 30.0)
+ {
+
+
+ }
+
+
+ public function standardizeParam($param): array
+ {
+ return array_map(function ($item) {
+ if (is_array($item)) {
+ return $this->standardizeParam($item);
+ } else {
+ return (string)$item;
+ }
+ }, $param);
+ }
+
+
+ /**
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ * @throws MethodNotFoundException
+ */
+ public static function __callStatic(string $name, array $arguments): mixed
+ {
+ $protectedName = '_' . $name;
+ if (method_exists(self::class, $protectedName)) {
+ return self::$protectedName(...$arguments);
+ }
+ throw new MethodNotFoundException('Call undefined method ' . self::class . ':' . $name . '()');
+ }
+
+ /**
+ * @use 开始请求
+ * @return string
+ */
+ protected function startRequest(): string
+ {
+ $request_id = Fake::hash();
+ $this->caches[$request_id] = [];
+ return $request_id;
+ }
+
+ /**
+ * @param string $request_id
+ * @param string $key
+ * @param mixed $values
+ * @return void
+ */
+ protected function setRequest(string $request_id, string $key, mixed $values): void
+ {
+ $this->caches[$request_id][$key] = $values;
+ }
+
+ /**
+ * @param string $request_id
+ * @param string $key
+ * @return mixed
+ */
+ protected function getRequest(string $request_id, string $key): mixed
+ {
+ return $this->caches[$request_id][$key];
+ }
+
+ /**
+ * @use 结束请求
+ * @param string $request_id
+ * @return void
+ */
+ protected function stopRequest(string $request_id): void
+ {
+ unset($this->caches[$request_id]);
+ }
+
+
+ /**
+ * @use GET请求
+ * @param $os
+ * @param $url
+ * @param array $params
+ * @param array $headers
+ * @param float $timeout
+ * @return mixed
+ */
+ public static function headers($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): array
+ {
+ //
+ $rid = self::getInstance()->startRequest();
+ //
+ Log::debug("[HEADERS#$rid] $url ", $params);
+ //
+ $payload['query'] = count($params) ? $params : [];
+ $payload['allow_redirects'] = false;
+ //
+ $response = self::getInstance()
+ ->withClient($rid)
+ ->withUrl($rid, $url)
+ ->withMethod($rid, 'get')
+ ->withHeaders($rid, $os, $headers)
+ ->withOptions($rid, $payload, $headers, $timeout)
+ ->handle($rid);
+ //
+ self::getInstance()->stopRequest($rid);
+ //
+ Log::debug("[HEADERS#$rid] " . $response->getBody());
+ //
+ return $response->getHeaders();
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/Sign/Sign.php b/src/Sign/Sign.php
new file mode 100644
index 0000000..e5318f6
--- /dev/null
+++ b/src/Sign/Sign.php
@@ -0,0 +1,125 @@
+ getU('access_token'),
+ 'actionKey' => 'appkey',
+ 'appkey' => $app_key,
+ 'build' => getDevice('app.bili_a.build'),
+ 'channel' => getDevice('app.bili_a.channel'),
+ 'device' => getDevice('app.bili_a.device'),
+ 'mobi_app' => getDevice('app.bili_a.mobi_app'),
+ 'platform' => getDevice('app.bili_a.platform'),
+ 'ts' => time(),
+ ];
+ //
+ $payload = array_merge($payload, $default);
+ return self::getInstance()->encryption($payload, $app_secret);
+
+ }
+
+
+ /**
+ * @use 登录签名
+ * @param array $payload
+ * @return array
+ */
+ public static function tv(array $payload): array
+ {
+ # Tv
+ $app_key = base64_decode(getDevice('app.bili_t.app_key'));
+ $app_secret = base64_decode(getDevice('app.bili_t.secret_key'));
+ //
+ $default = [
+ 'appkey' => $app_key,
+ 'local_id' => 0,
+ 'ts' => time(),
+ ];
+ //
+ $payload = array_merge($payload, $default);
+ return self::getInstance()->encryption($payload, $app_secret);
+
+ }
+
+ /**
+ * @use 通用签名
+ * @param array $payload
+ * @return array
+ */
+ public static function common(array $payload): array
+ {
+ # Android 旧
+ $app_key = base64_decode(getDevice('app.bili_a.app_key'));
+ $app_secret = base64_decode(getDevice('app.bili_a.secret_key'));
+
+ $default = [
+ 'access_key' => getU('access_token'),
+ 'actionKey' => 'appkey',
+ 'appkey' => $app_key,
+ 'build' => getDevice('app.bili_a.build'),
+ 'device' => getDevice('app.bili_a.device'),
+ 'mobi_app' => getDevice('app.bili_a.mobi_app'),
+ 'platform' => getDevice('app.bili_a.platform'),
+ 'ts' => time(),
+ ];
+ $payload = array_merge($payload, $default);
+ return self::getInstance()->encryption($payload, $app_secret);
+ }
+
+ /**
+ * @use 加密
+ * @param array $payload
+ * @param string $app_secret
+ * @return array
+ */
+ protected function encryption(array $payload, string $app_secret): array
+ {
+ if (isset($payload['sign'])) {
+ unset($payload['sign']);
+ }
+ ksort($payload);
+ $data = http_build_query($payload);
+ $payload['sign'] = md5($data . $app_secret);
+ return $payload;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Task/Task.php b/src/Task/Task.php
new file mode 100644
index 0000000..f5b7031
--- /dev/null
+++ b/src/Task/Task.php
@@ -0,0 +1,69 @@
+trigger($hook, ...$data);
+ } catch (Throwable $e) {
+ // TODO 多次错误删除tasks_***.json文件
+ $error_msg = "MSG: {$e->getMessage()} CODE: {$e->getCode()} FILE: {$e->getFile()} LINE: {$e->getLine()}";
+ Log::error($error_msg);
+ // Notice::push('error', $error_msg);
+ }
+ yield TimeLock::Delayed();
+ }
+ });
+ }
+
+ /**
+ * @return void
+ */
+ public static function execTasks(): void
+ {
+ Loop::run();
+ }
+}
+
+
\ No newline at end of file
diff --git a/src/TimeLock/TimeLock.php b/src/TimeLock/TimeLock.php
new file mode 100644
index 0000000..8e3decd
--- /dev/null
+++ b/src/TimeLock/TimeLock.php
@@ -0,0 +1,195 @@
+getCallClassName();
+ if (!array_key_exists($class_name, self::getInstance()->locks)) {
+ self::getInstance()->locks[$class_name] = ['times' => $times, 'pause' => $status];
+ }
+ print_r(self::getInstance()->locks);
+ }
+
+ /**
+ * @use 设置暂停状态
+ * @param bool $status
+ * @return void
+ */
+ public static function setPause(bool $status = false): void
+ {
+ $class_name = self::getInstance()->getCallClassName();
+ self::getInstance()->locks[$class_name]['pause'] = $status;
+ }
+
+ /**
+ * @use 获取暂停状态
+ * @return bool
+ */
+ public static function getPause(): bool
+ {
+ return self::getInstance()->getLock()['pause'];
+ }
+
+ /**
+ * @use 设置计时器
+ * @param int $times
+ * @return void
+ */
+ public static function setTimes(int $times): void
+ {
+ $class_name = self::getInstance()->getCallClassName();
+ self::getInstance()->locks[$class_name]['times'] = time() + $times;
+ }
+
+ /**
+ * @use 获取计时器
+ * @return bool
+ */
+ public static function getTimes(): int
+ {
+ return self::getInstance()->getLock()['times'];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getLock(): array
+ {
+ $class_name = $this->getCallClassName();
+ if (!array_key_exists($class_name, $this->locks)) {
+ failExit("当前类 $class_name 并未初始化时间锁");
+ }
+ return $this->locks[$class_name];
+ }
+
+ /**
+ * @use used in Amp loop Delayed
+ * @param int $times
+ * @return Delayed
+ */
+ public static function Delayed(int $times=1000): Delayed
+ {
+ return new Delayed($times);
+ }
+
+ /**
+ * @use 定时
+ * @param int $hour 时
+ * @param int $minute 分
+ * @param int $seconds 秒
+ * @param bool $random 随机一个小时内
+ * @return int
+ */
+ public static function timing(int $hour, int $minute = 0, int $seconds = 0, bool $random = false): int
+ {
+ $time = strtotime('today') + ($hour * 60 * 60) + ($minute * 60) + ($seconds);
+ if ($time > time()) {
+ $timing = strtotime('today') + ($hour * 60 * 60) + ($minute * 60) + ($seconds) - time();
+ } else {
+ $timing = strtotime('tomorrow') + ($hour * 60 * 60) + ($minute * 60) + ($seconds) - time();
+ }
+ return $random ? $timing + mt_rand(1, 60) * 60 : $timing;
+ }
+
+ /**
+ * @use 判断是否在时间内
+ * @param string $first_time
+ * @param string $second_time
+ * @return bool
+ */
+ public static function inTime(string $first_time, string $second_time): bool
+ {
+ #判断当前时间是否在时间段内,如果是,则执行
+ $Day = date('Y-m-d ', time());
+ $timeBegin = strtotime($Day . $first_time);
+ $timeEnd = strtotime($Day . $second_time);
+ $curr_time = time();
+ if ($curr_time >= $timeBegin && $curr_time <= $timeEnd) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @use 获取调用者类名
+ * @param int $index
+ * @return string
+ */
+ protected function getCallClassName(): string
+ {
+ // basename(str_replace('\\', '/', __CLASS__));
+ $backtraces = debug_backtrace();
+ $temp = pathinfo(basename($backtraces[1]['file']))['filename'];
+ //
+ if ($temp == basename(str_replace('\\', '/', __CLASS__))){
+ return pathinfo(basename($backtraces[2]['file']))['filename'];
+ }else{
+ return $temp;
+ }
+ }
+
+ /**
+ * @use 获取基础CLASS NAME
+ * @return string
+ */
+ protected function getBaseClass(): string
+ {
+ return basename(str_replace('\\', '/', __CLASS__));
+ }
+
+// /**
+// * @use 暂停
+// */
+// public static function pauseLock(): void
+// {
+// // 备份几种获取方式 get_called_class()
+// // basename(str_replace('\\', '/', $class));
+// // substr(strrchr($class, "\\"), 1);
+// // substr($class, strrpos($class, '\\') + 1);
+// // array_pop(explode('\\', $class));
+//// Schedule::triggerRefused((new ReflectionClass(static::class))->getShortName());
+// }
+
+
+}
+
\ No newline at end of file
diff --git a/src/User/User.php b/src/User/User.php
new file mode 100644
index 0000000..d8f5c71
--- /dev/null
+++ b/src/User/User.php
@@ -0,0 +1,52 @@
+ "mixed|string", 'uid' => "mixed|string", 'sid' => "mixed|string"])]
+ public static function parseCookie(): array
+ {
+ $cookies = getU('cookie');
+ preg_match('/bili_jct=(.{32})/', $cookies, $token);
+ preg_match('/DedeUserID=(\d+)/', $cookies, $uid);
+ preg_match('/DedeUserID__ckMd5=(.{16})/', $cookies, $sid);
+ return [
+ 'csrf' => $token[1] ?? '',
+ 'uid' => $uid[1] ?? '',
+ 'sid' => $sid[1] ?? '',
+ ];
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/Util/ArrayR/ArrayR.php b/src/Util/ArrayR/ArrayR.php
new file mode 100644
index 0000000..72cfef9
--- /dev/null
+++ b/src/Util/ArrayR/ArrayR.php
@@ -0,0 +1,42 @@
+ $value) {
+ $tmp .= "$key$sep1$value$sep2";
+ }
+ return $tmp;
+
+ }
+
+
+}
+
+
\ No newline at end of file
diff --git a/src/tool/Common.php b/src/Util/Common/Common.php
similarity index 81%
rename from src/tool/Common.php
rename to src/Util/Common/Common.php
index 50375dc..7a96c12 100644
--- a/src/tool/Common.php
+++ b/src/Util/Common/Common.php
@@ -1,28 +1,21 @@
-init(...$params);
+ }
+ }
+ return self::$_instances[$className];
+ }
+}
\ No newline at end of file
diff --git a/src/script/DelDynamic.php b/src/Util/Exceptions/GetConfigException.php
similarity index 53%
rename from src/script/DelDynamic.php
rename to src/Util/Exceptions/GetConfigException.php
index 34a9264..15a2bc1 100644
--- a/src/script/DelDynamic.php
+++ b/src/Util/Exceptions/GetConfigException.php
@@ -1,40 +1,34 @@
-register('Example', $this, 'example');
+ parent::__construct($message, $code, $previous);
}
-
-
- public function example($example): string
- {
- return "插件" . $example;
- }
-
}
\ No newline at end of file
diff --git a/src/tool/Generator.php b/src/Util/Fake/Fake.php
similarity index 68%
rename from src/tool/Generator.php
rename to src/Util/Fake/Fake.php
index cc1af9c..4c57f38 100644
--- a/src/tool/Generator.php
+++ b/src/Util/Fake/Fake.php
@@ -1,4 +1,4 @@
-width = $width;
+ $this->frame = $frame;
+ $this->x = $width - 1;
+ $this->y = $width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ }
+
+ /**
+ * @param array $at
+ * @param int $val
+ * @return void
+ */
+ public function setFrameAt(array $at, int $val): void
+ {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ /**
+ * @param array $at
+ * @return int
+ */
+ public function getFrameAt(array $at): int
+ {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ /**
+ * @return array|null
+ */
+ public function next(): ?array
+ {
+ do {
+ if ($this->bit == -1) {
+ $this->bit = 0;
+ return array('x' => $this->x, 'y' => $this->y);
+ }
+
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+
+ if ($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+
+ if ($this->dir < 0) {
+ if ($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if ($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if ($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if ($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if ($x < 0 || $y < 0) return null;
+
+ $this->x = $x;
+ $this->y = $y;
+
+ } while (ord($this->frame[$y][$x]) & 0x80);
+
+ return array('x' => $x, 'y' => $y);
+ }
+
+}
diff --git a/src/Util/Qrcode/Lib/QrBitStream.php b/src/Util/Qrcode/Lib/QrBitStream.php
new file mode 100644
index 0000000..3334d9d
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrBitStream.php
@@ -0,0 +1,200 @@
+data);
+ }
+
+ /**
+ * @param int $setLength
+ * @return int
+ */
+ public function allocate(int $setLength): int
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ /**
+ * @param int $bits
+ * @param int $num
+ * @return QrBitStream
+ */
+ public static function newFromNum(int $bits, int $num): QrBitStream
+ {
+ $bstream = new QrBitStream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for ($i = 0; $i < $bits; $i++) {
+ if ($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ /**
+ * @param int $size
+ * @param array $data
+ * @return QrBitStream
+ */
+ public static function newFromBytes(int $size, array $data): QrBitStream
+ {
+ $bstream = new QrBitStream();
+ $bstream->allocate($size * 8);
+ $p = 0;
+
+ for ($i = 0; $i < $size; $i++) {
+ $mask = 0x80;
+ for ($j = 0; $j < 8; $j++) {
+ if ($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ /**
+ * @param QrBitStream $arg
+ * @return int
+ */
+ public function append(QrBitStream $arg): int
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if ($arg->size() == 0) {
+ return 0;
+ }
+
+ if ($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ /**
+ * @param int $bits
+ * @param int $num
+ * @return int
+ */
+ public function appendNum(int $bits, int $num): int
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QrBitStream::newFromNum($bits, $num);
+
+ if (is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ /**
+ * @param int $size
+ * @param array $data
+ * @return int
+ */
+ public function appendBytes(int $size, array $data): int
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QrBitStream::newFromBytes($size, $data);
+
+ if (is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ /**
+ * @return array
+ */
+ public function toByte(): array
+ {
+
+ $size = $this->size();
+
+ if ($size == 0) {
+ return [];
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for ($i = 0; $i < $bytes; $i++) {
+ $v = 0;
+ for ($j = 0; $j < 8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if ($size & 7) {
+ $v = 0;
+ for ($j = 0; $j < ($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Util/Qrcode/Lib/QrCode.php b/src/Util/Qrcode/Lib/QrCode.php
new file mode 100644
index 0000000..7e3bbee
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrCode.php
@@ -0,0 +1,164 @@
+getVersion() < 0 || $input->getVersion() > Constants::QRSPEC_VERSION_MAX) {
+ throw new Exception('wrong version');
+ }
+ if ($input->getErrorCorrectionLevel() > Constants::QR_ECLEVEL_H) {
+ throw new Exception('wrong level');
+ }
+
+ $raw = new QrRawCode($input);
+
+ $version = $raw->version;
+ $width = QrSpec::getWidth($version);
+ $frame = QrSpec::newFrame($version);
+
+ $filler = new FrameFiller($width, $frame);
+ if (is_null($filler)) {
+ return NULL;
+ }
+
+ // inteleaved data and ecc codes
+ for ($i = 0; $i < $raw->dataLength + $raw->eccLength; $i++) {
+ $code = $raw->getCode();
+ $bit = 0x80;
+ for ($j = 0; $j < 8; $j++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+
+ unset($raw);
+
+ // remainder bits
+ $j = QrSpec::getRemainder($version);
+ for ($i = 0; $i < $j; $i++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02);
+ }
+
+ $frame = $filler->frame;
+ unset($filler);
+
+
+ // masking
+ $maskObj = new QrMask();
+ if ($mask < 0) {
+
+ if (Constants::QR_FIND_BEST_MASK) {
+ $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, (intval(Constants::QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());
+ }
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
+ }
+
+ if ($masked == NULL) {
+ return NULL;
+ }
+
+
+ $this->version = $version;
+ $this->width = $width;
+ $this->data = $masked;
+
+ return $this;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeInput(QrInput $input)
+ {
+ return $this->encodeMask($input, -1);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString8bit($string, $version, $level)
+ {
+ if ($string == NULL) {
+ throw new Exception('empty string!');
+ return NULL;
+ }
+
+ $input = new QrInput($version, $level);
+ if ($input == NULL) return NULL;
+
+ $ret = $input->append($input, Constants::QR_MODE_8, strlen($string), str_split($string));
+ if ($ret < 0) {
+ unset($input);
+ return NULL;
+ }
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString($string, $version, $level, $hint, $case_sensitive)
+ {
+
+ if ($hint != Constants::QR_MODE_8 && $hint != Constants::QR_MODE_KANJI) {
+ throw new Exception('bad hint');
+ return NULL;
+ }
+
+ $input = new QrInput($version, $level);
+ if ($input == NULL) return NULL;
+
+ $ret = QrSplit::splitStringToQrInput($string, $input, $hint, $case_sensitive);
+ if ($ret < 0) {
+ return NULL;
+ }
+
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public static function png($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint = false)
+ {
+ $enc = QrEncode::factory($level, $size, $margin);
+ return $enc->encodePNG($text, $outfile, $saveandprint);
+ }
+
+ //----------------------------------------------------------------------
+ public static function text($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QrEncode::factory($level, $size, $margin);
+ return $enc->encode($text, $outfile);
+ }
+
+ //----------------------------------------------------------------------
+ public static function raw($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QrEncode::factory($level, $size, $margin);
+ return $enc->encodeRAW($text, $outfile);
+ }
+}
diff --git a/src/Util/Qrcode/Lib/QrEncode.php b/src/Util/Qrcode/Lib/QrEncode.php
new file mode 100644
index 0000000..d4cdcc6
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrEncode.php
@@ -0,0 +1,76 @@
+size = $size;
+ $enc->margin = $margin;
+
+ $enc->level = match ($level . '') {
+ '0', '1', '2', '3' => $level,
+ 'l', 'L' => Constants::QR_ECLEVEL_L,
+ 'm', 'M' => Constants::QR_ECLEVEL_M,
+ 'q', 'Q' => Constants::QR_ECLEVEL_Q,
+ 'h', 'H' => Constants::QR_ECLEVEL_H,
+ };
+ return $enc;
+ }
+
+ /**
+ * @param string $text
+ * @return array
+ * @throws Exception
+ */
+ public function encode(string $text): array
+ {
+ $code = new QrCode();
+
+ if ($this->eight_bit) {
+ $code->encodeString8bit($text, $this->version, $this->level);
+ } else {
+ $code->encodeString($text, $this->version, $this->level, $this->hint, $this->case_sensitive);
+ }
+
+ return QrTools::binarize($code->data);
+ }
+
+}
diff --git a/src/Util/Qrcode/Lib/QrImage.php b/src/Util/Qrcode/Lib/QrImage.php
new file mode 100644
index 0000000..83993ac
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrImage.php
@@ -0,0 +1,89 @@
+ Constants::QRSPEC_VERSION_MAX || $level > Constants::QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ }
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ /**
+ * @return int
+ */
+ public function getVersion(): int
+ {
+ return $this->version;
+ }
+
+ /**
+ * @param int $version
+ * @return void
+ * @throws Exception
+ */
+ public function setVersion(int $version): void
+ {
+ if ($version < 0 || $version > Constants::QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ }
+ $this->version = $version;
+ }
+
+ /**
+ * @return int
+ */
+ public function getErrorCorrectionLevel(): int
+ {
+ return $this->level;
+ }
+
+ /**
+ * @param $level
+ * @return void
+ * @throws Exception
+ */
+ public function setErrorCorrectionLevel($level): void
+ {
+ if ($level > Constants::QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ }
+ $this->level = $level;
+ }
+
+ /**
+ * @param QrInputItem $entry
+ * @return void
+ */
+ public function appendEntry(QrInputItem $entry): void
+ {
+ $this->items[] = $entry;
+ }
+
+ /**
+ * @param $mode
+ * @param $size
+ * @param $data
+ * @return int
+ */
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QrInputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if ($size > Constants::MAX_STRUCTURED_SYMBOLS) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if ($index <= 0 || $index > Constants::MAX_STRUCTURED_SYMBOLS) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QrInputItem(Constants::QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach ($this->items as $item) {
+ if ($item->mode != Constants::QR_MODE_STRUCTURE) {
+ for ($i = $item->size - 1; $i >= 0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for ($i = 0; $i < $size; $i++) {
+ if ((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch ($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127) ? -1 : self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for ($i = 0; $i < $size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if ($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if ($size & 1)
+ return false;
+
+ for ($i = 0; $i < $size; $i += 2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i + 1]);
+ if ($val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if ($size <= 0)
+ return false;
+
+ switch ($mode) {
+ case Constants::QR_MODE_NUM:
+ return self::checkModeNum($size, $data);
+ case Constants::QR_MODE_AN:
+ return self::checkModeAn($size, $data);
+ case Constants::QR_MODE_KANJI:
+ return self::checkModeKanji($size, $data);
+ case Constants::QR_MODE_STRUCTURE:
+ case Constants::QR_MODE_8:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach ($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QrSpec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QrSpec::lengthIndicator($mode, $version);
+ switch ($mode) {
+ case Constants::QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if ($remain >= 7) {
+ $size += 2;
+ } else if ($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case Constants::QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if ($remain >= 6)
+ $size++;
+ break;
+ case Constants::QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case Constants::QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case Constants::QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QrSpec::maximumWords($mode, $version);
+ if ($size < 0) $size = 0;
+ if ($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach ($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if ($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if ($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for (; ;) {
+ $bits = $this->createBitStream();
+
+ if ($bits < 0)
+ return -1;
+
+ $ver = QrSpec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if ($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QrSpec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QrBitStream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if ($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if ($padlen > 0) {
+
+ $padbuf = [];
+ for ($i = 0; $i < $padlen; $i++) {
+ $padbuf[$i] = ($i & 1) ? 0x11 : 0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if ($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if ($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QrBitStream();
+
+ foreach ($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if ($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if ($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if ($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if ($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+}
+
+
diff --git a/src/Util/Qrcode/Lib/QrInputItem.php b/src/Util/Qrcode/Lib/QrInputItem.php
new file mode 100644
index 0000000..529e4f5
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrInputItem.php
@@ -0,0 +1,280 @@
+mode = $mode;
+ $this->size = $size;
+ $this->data = $setData;
+ $this->bstream = $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeNum($version)
+ {
+ try {
+
+ $words = (int)($this->size / 3);
+ $bs = new QrBitStream();
+
+ $val = 0x1;
+ $bs->appendNum(4, $val);
+ $bs->appendNum(QrSpec::lengthIndicator(Constants::QR_MODE_NUM, $version), $this->size);
+
+ for ($i = 0; $i < $words; $i++) {
+ $val = (ord($this->data[$i * 3]) - ord('0')) * 100;
+ $val += (ord($this->data[$i * 3 + 1]) - ord('0')) * 10;
+ $val += (ord($this->data[$i * 3 + 2]) - ord('0'));
+ $bs->appendNum(10, $val);
+ }
+
+ if ($this->size - $words * 3 == 1) {
+ $val = ord($this->data[$words * 3]) - ord('0');
+ $bs->appendNum(4, $val);
+ } else if ($this->size - $words * 3 == 2) {
+ $val = (ord($this->data[$words * 3]) - ord('0')) * 10;
+ $val += (ord($this->data[$words * 3 + 1]) - ord('0'));
+ $bs->appendNum(7, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeAn($version)
+ {
+ try {
+ $words = (int)($this->size / 2);
+ $bs = new QrBitStream();
+
+ $bs->appendNum(4, 0x02);
+ $bs->appendNum(QrSpec::lengthIndicator(Constants::QR_MODE_AN, $version), $this->size);
+
+ for ($i = 0; $i < $words; $i++) {
+ $val = (int)QrInput::lookAnTable(ord($this->data[$i * 2])) * 45;
+ $val += (int)QrInput::lookAnTable(ord($this->data[$i * 2 + 1]));
+
+ $bs->appendNum(11, $val);
+ }
+
+ if ($this->size & 1) {
+ $val = QrInput::lookAnTable(ord($this->data[$words * 2]));
+ $bs->appendNum(6, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeMode8($version)
+ {
+ try {
+ $bs = new QrBitStream();
+
+ $bs->appendNum(4, 0x4);
+ $bs->appendNum(QrSpec::lengthIndicator(Constants::QR_MODE_8, $version), $this->size);
+
+ for ($i = 0; $i < $this->size; $i++) {
+ $bs->appendNum(8, ord($this->data[$i]));
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeKanji($version)
+ {
+ try {
+
+ $bs = new QrBitStream();
+
+ $bs->appendNum(4, 0x8);
+ $bs->appendNum(QrSpec::lengthIndicator(Constants::QR_MODE_KANJI, $version), (int)($this->size / 2));
+
+ for ($i = 0; $i < $this->size; $i += 2) {
+ $val = (ord($this->data[$i]) << 8) | ord($this->data[$i + 1]);
+ if ($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+
+ $bs->appendNum(13, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeStructure()
+ {
+ try {
+ $bs = new QrBitStream();
+
+ $bs->appendNum(4, 0x03);
+ $bs->appendNum(4, ord($this->data[1]) - 1);
+ $bs->appendNum(4, ord($this->data[0]) - 1);
+ $bs->appendNum(8, ord($this->data[2]));
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSizeOfEntry($version)
+ {
+ $bits = 0;
+
+ if ($version == 0)
+ $version = 1;
+
+ switch ($this->mode) {
+ case Constants::QR_MODE_NUM:
+ $bits = QrInput::estimateBitsModeNum($this->size);
+ break;
+ case Constants::QR_MODE_AN:
+ $bits = QrInput::estimateBitsModeAn($this->size);
+ break;
+ case Constants::QR_MODE_8:
+ $bits = QrInput::estimateBitsMode8($this->size);
+ break;
+ case Constants::QR_MODE_KANJI:
+ $bits = QrInput::estimateBitsModeKanji($this->size);
+ break;
+ case Constants::QR_MODE_STRUCTURE:
+ return Constants::STRUCTURE_HEADER_BITS;
+ default:
+ return 0;
+ }
+
+ $l = QrSpec::lengthIndicator($this->mode, $version);
+ $m = 1 << $l;
+ $num = (int)(($this->size + $m - 1) / $m);
+
+ $bits += $num * (4 + $l);
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeBitStream($version)
+ {
+ try {
+
+ unset($this->bstream);
+ $words = QrSpec::maximumWords($this->mode, $version);
+
+ if ($this->size > $words) {
+
+ $st1 = new QrInputItem($this->mode, $words, $this->data);
+ $st2 = new QrInputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
+
+ $st1->encodeBitStream($version);
+ $st2->encodeBitStream($version);
+
+ $this->bstream = new QrBitStream();
+ $this->bstream->append($st1->bstream);
+ $this->bstream->append($st2->bstream);
+
+ unset($st1);
+ unset($st2);
+
+ } else {
+
+ $ret = 0;
+
+ switch ($this->mode) {
+ case Constants::QR_MODE_NUM:
+ $ret = $this->encodeModeNum($version);
+ break;
+ case Constants::QR_MODE_AN:
+ $ret = $this->encodeModeAn($version);
+ break;
+ case Constants::QR_MODE_8:
+ $ret = $this->encodeMode8($version);
+ break;
+ case Constants::QR_MODE_KANJI:
+ $ret = $this->encodeModeKanji($version);
+ break;
+ case Constants::QR_MODE_STRUCTURE:
+ $ret = $this->encodeModeStructure();
+ break;
+
+ default:
+ break;
+ }
+
+ if ($ret < 0)
+ return -1;
+ }
+
+ return $this->bstream->size();
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+}
diff --git a/src/Util/Qrcode/Lib/QrMask.php b/src/Util/Qrcode/Lib/QrMask.php
new file mode 100644
index 0000000..5b7e331
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrMask.php
@@ -0,0 +1,347 @@
+runLength = array_fill(0, Constants::QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QrSpec::getFormatInfo($mask, $level);
+
+ for ($i = 0; $i < 8; $i++) {
+ if ($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if ($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for ($i = 0; $i < 7; $i++) {
+ if ($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if ($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y)
+ {
+ return ($x + $y) & 1;
+ }
+
+ public function mask1($x, $y)
+ {
+ return ($y & 1);
+ }
+
+ public function mask2($x, $y)
+ {
+ return ($x % 3);
+ }
+
+ public function mask3($x, $y)
+ {
+ return ($x + $y) % 3;
+ }
+
+ public function mask4($x, $y)
+ {
+ return (((int)($y / 2)) + ((int)($x / 3))) & 1;
+ }
+
+ public function mask5($x, $y)
+ {
+ return (($x * $y) & 1) + ($x * $y) % 3;
+ }
+
+ public function mask6($x, $y)
+ {
+ return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
+ }
+
+ public function mask7($x, $y)
+ {
+ return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
+ }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for ($y = 0; $y < $width; $y++) {
+ for ($x = 0; $x < $width; $x++) {
+ if (ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask' . $maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0) ? 1 : 0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = [];
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = [];
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = [];
+
+ $fileName = Constants::QR_CACHE_DIR . 'mask_' . $maskNo . DIRECTORY_SEPARATOR . 'mask_' . $width . '_' . $maskNo . '.dat';
+
+ if (Constants::QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(Constants::QR_CACHE_DIR . 'mask_' . $maskNo))
+ mkdir(Constants::QR_CACHE_DIR . 'mask_' . $maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for ($y = 0; $y < $width; $y++) {
+ for ($x = 0; $x < $width; $x++) {
+ if ($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for ($i = 0; $i < $length; $i++) {
+
+ if ($this->runLength[$i] >= 5) {
+ $demerit += (Constants::N1 + ($this->runLength[$i] - 5));
+ }
+ if ($i & 1) {
+ if (($i >= 3) && ($i < ($length - 2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if (($this->runLength[$i - 2] == $fact) &&
+ ($this->runLength[$i - 1] == $fact) &&
+ ($this->runLength[$i + 1] == $fact) &&
+ ($this->runLength[$i + 2] == $fact)) {
+ if (($this->runLength[$i - 3] < 0) || ($this->runLength[$i - 3] >= (4 * $fact))) {
+ $demerit += Constants::N3;
+ } else if ((($i + 3) >= $length) || ($this->runLength[$i + 3] >= (4 * $fact))) {
+ $demerit += Constants::N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for ($y = 0; $y < $width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y > 0)
+ $frameYM = $frame[$y - 1];
+
+ for ($x = 0; $x < $width; $x++) {
+ if (($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x - 1]) & ord($frameYM[$x]) & ord($frameYM[$x - 1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x - 1]) | ord($frameYM[$x]) | ord($frameYM[$x - 1]);
+
+ if (($b22 | ($w22 ^ 1)) & 1) {
+ $demerit += Constants::N2;
+ }
+ }
+ if (($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if ($x > 0) {
+ if ((ord($frameY[$x]) ^ ord($frameY[$x - 1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head + 1);
+ }
+
+ for ($x = 0; $x < $width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for ($y = 0; $y < $width; $y++) {
+ if ($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if ($y > 0) {
+ if ((ord($frame[$y][$x]) ^ ord($frame[$y - 1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head + 1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = [];
+
+ $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
+
+ if (Constants::QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8 - (Constants::QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand(0, count($checked_masks) - 1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach ($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * Constants::N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if ($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+}
diff --git a/src/Util/Qrcode/Lib/QrRawCode.php b/src/Util/Qrcode/Lib/QrRawCode.php
new file mode 100644
index 0000000..c35ccb5
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrRawCode.php
@@ -0,0 +1,128 @@
+datacode = $input->getByteStream();
+ if (is_null($this->datacode)) {
+ throw new Exception('null input string');
+ }
+
+ QrSpec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);
+
+ $this->version = $input->getVersion();
+ $this->b1 = QrSpec::rsBlockNum1($spec);
+ $this->dataLength = QrSpec::rsDataLength($spec);
+ $this->eccLength = QrSpec::rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = QrSpec::rsBlockNum($spec);
+
+ $ret = $this->init($spec);
+ if ($ret < 0) {
+ throw new Exception('block alloc error');
+ return null;
+ }
+
+ $this->count = 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function init(array $spec)
+ {
+ $dl = QrSpec::rsDataCodes1($spec);
+ $el = QrSpec::rsEccCodes1($spec);
+ $rs = QrRs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ for ($i = 0; $i < QrSpec::rsBlockNum1($spec); $i++) {
+ $ecc = array_slice($this->ecccode, $eccPos);
+ $this->rsblocks[$blockNo] = new QrRsBlock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ if (QrSpec::rsBlockNum2($spec) == 0)
+ return 0;
+
+ $dl = QrSpec::rsDataCodes2($spec);
+ $el = QrSpec::rsEccCodes2($spec);
+ $rs = QrRs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+ if ($rs == NULL) return -1;
+
+ for ($i = 0; $i < QrSpec::rsBlockNum2($spec); $i++) {
+ $ecc = array_slice($this->ecccode, $eccPos);
+ $this->rsblocks[$blockNo] = new QrRsBlock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getCode()
+ {
+ $ret = null;
+
+ if ($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if ($col >= $this->rsblocks[0]->dataLength) {
+ $row += $this->b1;
+ }
+ $ret = (int) $this->rsblocks[$row]->data[$col];
+ } else if ($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]->ecc[$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+
+ return $ret;
+ }
+}
diff --git a/src/Util/Qrcode/Lib/QrRs.php b/src/Util/Qrcode/Lib/QrRs.php
new file mode 100644
index 0000000..cce14c7
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrRs.php
@@ -0,0 +1,44 @@
+pad != $pad) continue;
+ if ($rs->nroots != $nroots) continue;
+ if ($rs->mm != $symsize) continue;
+ if ($rs->gfpoly != $gfpoly) continue;
+ if ($rs->fcr != $fcr) continue;
+ if ($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QrRsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Qrcode/Lib/QrRsBlock.php b/src/Util/Qrcode/Lib/QrRsBlock.php
new file mode 100644
index 0000000..6689708
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrRsBlock.php
@@ -0,0 +1,37 @@
+encode_rs_char($data, $ecc);
+
+ $this->dataLength = $dl;
+ $this->data = $data;
+ $this->eccLength = $el;
+ $this->ecc = $ecc;
+ }
+}
diff --git a/src/Util/Qrcode/Lib/QrRsItem.php b/src/Util/Qrcode/Lib/QrRsItem.php
new file mode 100644
index 0000000..cc42115
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrRsItem.php
@@ -0,0 +1,173 @@
+= $this->nn) {
+ $x -= $this->nn;
+ $x = ($x >> $this->mm) + ($x & $this->nn);
+ }
+
+ return $x;
+ }
+
+ //----------------------------------------------------------------------
+ public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ // Common code for intializing a Reed-Solomon control block (char or int symbols)
+ // Copyright 2004 Phil Karn, KA9Q
+ // May be used under the terms of the GNU Lesser General Public License (LGPL)
+
+ $rs = null;
+
+ // Check parameter ranges
+ if ($symsize < 0 || $symsize > 8) return $rs;
+ if ($fcr < 0 || $fcr >= (1 << $symsize)) return $rs;
+ if ($prim <= 0 || $prim >= (1 << $symsize)) return $rs;
+ if ($nroots < 0 || $nroots >= (1 << $symsize)) return $rs; // Can't have more roots than symbol values!
+ if ($pad < 0 || $pad >= ((1 << $symsize) - 1 - $nroots)) return $rs; // Too much padding
+
+ $rs = new QrRsItem();
+ $rs->mm = $symsize;
+ $rs->nn = (1 << $symsize) - 1;
+ $rs->pad = $pad;
+
+ $rs->alpha_to = array_fill(0, $rs->nn + 1, 0);
+ $rs->index_of = array_fill(0, $rs->nn + 1, 0);
+
+ // PHP style macro replacement ;)
+ $NN =& $rs->nn;
+ $A0 =& $NN;
+
+ // Generate Galois field lookup tables
+ $rs->index_of[0] = $A0; // log(zero) = -inf
+ $rs->alpha_to[$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+
+ for ($i = 0; $i < $rs->nn; $i++) {
+ $rs->index_of[$sr] = $i;
+ $rs->alpha_to[$i] = $sr;
+ $sr <<= 1;
+ if ($sr & (1 << $symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs->nn;
+ }
+
+ if ($sr != 1) {
+ // field generator polynomial is not primitive!
+ $rs = NULL;
+ return $rs;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ $rs->genpoly = array_fill(0, $nroots + 1, 0);
+
+ $rs->fcr = $fcr;
+ $rs->prim = $prim;
+ $rs->nroots = $nroots;
+ $rs->gfpoly = $gfpoly;
+
+ /* Find prim-th root of 1, used in decoding */
+ for ($iprim = 1; ($iprim % $prim) != 0; $iprim += $rs->nn)
+ ; // intentional empty-body loop!
+
+ $rs->iprim = (int)($iprim / $prim);
+ $rs->genpoly[0] = 1;
+
+ for ($i = 0, $root = $fcr * $prim; $i < $nroots; $i++, $root += $prim) {
+ $rs->genpoly[$i + 1] = 1;
+
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; $j--) {
+ if ($rs->genpoly[$j] != 0) {
+ $rs->genpoly[$j] = $rs->genpoly[$j - 1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
+ } else {
+ $rs->genpoly[$j] = $rs->genpoly[$j - 1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
+ }
+
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; $i++)
+ $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
+
+ return $rs;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode_rs_char($data, &$parity)
+ {
+ $MM =& $this->mm;
+ $NN =& $this->nn;
+ $ALPHA_TO =& $this->alpha_to;
+ $INDEX_OF =& $this->index_of;
+ $GENPOLY =& $this->genpoly;
+ $NROOTS =& $this->nroots;
+ $FCR =& $this->fcr;
+ $PRIM =& $this->prim;
+ $IPRIM =& $this->iprim;
+ $PAD =& $this->pad;
+ $A0 =& $NN;
+
+ $parity = array_fill(0, $NROOTS, 0);
+
+ for ($i = 0; $i < ($NN - $NROOTS - $PAD); $i++) {
+
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if ($feedback != $A0) {
+ // feedback term is non-zero
+
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback);
+
+ for ($j = 1; $j < $NROOTS; $j++) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS - $j])];
+ }
+ }
+
+ // Shift
+ array_shift($parity);
+ if ($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Qrcode/Lib/QrSpec.php b/src/Util/Qrcode/Lib/QrSpec.php
new file mode 100644
index 0000000..ae92778
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrSpec.php
@@ -0,0 +1,600 @@
+= $size)
+ return $i;
+ }
+
+ return -1;
+ }
+
+
+ public static $lengthTableBits = array(
+ array(10, 12, 14),
+ array(9, 11, 13),
+ array(8, 16, 16),
+ array(8, 10, 12)
+ );
+
+ //----------------------------------------------------------------------
+ public static function lengthIndicator($mode, $version)
+ {
+ if ($mode == Constants::QR_MODE_STRUCTURE)
+ return 0;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ return self::$lengthTableBits[$mode][$l];
+ }
+
+ //----------------------------------------------------------------------
+ public static function maximumWords($mode, $version)
+ {
+ if ($mode == Constants::QR_MODE_STRUCTURE)
+ return 3;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ $bits = self::$lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+
+ if ($mode == Constants::QR_MODE_KANJI) {
+ $words *= 2; // the number of bytes is required
+ }
+
+ return $words;
+ }
+
+ // Error correction code -----------------------------------------------
+ // Table of the error correction code (Reed-Solomon block)
+ // See Table 12-16 (pp.30-36), JIS X0510:2004.
+
+ public static $eccTable = array(
+ array(array(0, 0), array(0, 0), array(0, 0), array(0, 0)),
+ array(array(1, 0), array(1, 0), array(1, 0), array(1, 0)), // 1
+ array(array(1, 0), array(1, 0), array(1, 0), array(1, 0)),
+ array(array(1, 0), array(1, 0), array(2, 0), array(2, 0)),
+ array(array(1, 0), array(2, 0), array(2, 0), array(4, 0)),
+ array(array(1, 0), array(2, 0), array(2, 2), array(2, 2)), // 5
+ array(array(2, 0), array(4, 0), array(4, 0), array(4, 0)),
+ array(array(2, 0), array(4, 0), array(2, 4), array(4, 1)),
+ array(array(2, 0), array(2, 2), array(4, 2), array(4, 2)),
+ array(array(2, 0), array(3, 2), array(4, 4), array(4, 4)),
+ array(array(2, 2), array(4, 1), array(6, 2), array(6, 2)), //10
+ array(array(4, 0), array(1, 4), array(4, 4), array(3, 8)),
+ array(array(2, 2), array(6, 2), array(4, 6), array(7, 4)),
+ array(array(4, 0), array(8, 1), array(8, 4), array(12, 4)),
+ array(array(3, 1), array(4, 5), array(11, 5), array(11, 5)),
+ array(array(5, 1), array(5, 5), array(5, 7), array(11, 7)), //15
+ array(array(5, 1), array(7, 3), array(15, 2), array(3, 13)),
+ array(array(1, 5), array(10, 1), array(1, 15), array(2, 17)),
+ array(array(5, 1), array(9, 4), array(17, 1), array(2, 19)),
+ array(array(3, 4), array(3, 11), array(17, 4), array(9, 16)),
+ array(array(3, 5), array(3, 13), array(15, 5), array(15, 10)), //20
+ array(array(4, 4), array(17, 0), array(17, 6), array(19, 6)),
+ array(array(2, 7), array(17, 0), array(7, 16), array(34, 0)),
+ array(array(4, 5), array(4, 14), array(11, 14), array(16, 14)),
+ array(array(6, 4), array(6, 14), array(11, 16), array(30, 2)),
+ array(array(8, 4), array(8, 13), array(7, 22), array(22, 13)), //25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
+ array(array(8, 4), array(22, 3), array(8, 26), array(12, 28)),
+ array(array(3, 10), array(3, 23), array(4, 31), array(11, 31)),
+ array(array(7, 7), array(21, 7), array(1, 37), array(19, 26)),
+ array(array(5, 10), array(19, 10), array(15, 25), array(23, 25)), //30
+ array(array(13, 3), array(2, 29), array(42, 1), array(23, 28)),
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35
+ array(array(6, 14), array(6, 34), array(46, 10), array(2, 64)),
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
+ array(array(4, 18), array(13, 32), array(48, 14), array(42, 32)),
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40
+ );
+
+ //----------------------------------------------------------------------
+ // CACHEABLE!!!
+
+ public static function getEccSpec($version, $level, array &$spec)
+ {
+ if (count($spec) < 5) {
+ $spec = array(0, 0, 0, 0, 0);
+ }
+
+ $b1 = self::$eccTable[$version][$level][0];
+ $b2 = self::$eccTable[$version][$level][1];
+ $data = self::getDataLength($version, $level);
+ $ecc = self::getECCLength($version, $level);
+
+ if ($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ }
+
+ // Alignment pattern ---------------------------------------------------
+
+ // Positions of alignment patterns.
+ // This array includes only the second and the third position of the
+ // alignment patterns. Rest of them can be calculated from the distance
+ // between them.
+
+ // See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+
+ public static $alignmentPattern = array(
+ array(0, 0),
+ array(0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40
+ );
+
+
+ /** --------------------------------------------------------------------
+ * Put an alignment marker.
+ * @param frame
+ * @param width
+ * @param ox,oy center coordinate of the pattern
+ */
+ public static function putAlignmentMarker(array &$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+
+ $yStart = $oy - 2;
+ $xStart = $ox - 2;
+
+ for ($y = 0; $y < 5; $y++) {
+ QrStr::set($frame, $xStart, $yStart + $y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function putAlignmentPattern($version, &$frame, $width)
+ {
+ if ($version < 2)
+ return;
+
+ $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
+ if ($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
+ }
+
+ if ($w * $w - 3 == 1) {
+ $x = self::$alignmentPattern[$version][0];
+ $y = self::$alignmentPattern[$version][0];
+ self::putAlignmentMarker($frame, $x, $y);
+ return;
+ }
+
+ $cx = self::$alignmentPattern[$version][0];
+ for ($x = 1; $x < $w - 1; $x++) {
+ self::putAlignmentMarker($frame, 6, $cx);
+ self::putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+
+ $cy = self::$alignmentPattern[$version][0];
+ for ($y = 0; $y < $w - 1; $y++) {
+ $cx = self::$alignmentPattern[$version][0];
+ for ($x = 0; $x < $w - 1; $x++) {
+ self::putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ }
+
+ // Version information pattern -----------------------------------------
+
+ // Version information pattern (BCH coded).
+ // See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+
+ // size: [Constants::QRSPEC_VERSION_MAX - 6]
+
+ public static $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69
+ );
+
+ //----------------------------------------------------------------------
+ public static function getVersionPattern($version)
+ {
+ if ($version < 7 || $version > Constants::QRSPEC_VERSION_MAX)
+ return 0;
+
+ return self::$versionPattern[$version - 7];
+ }
+
+ // Format information --------------------------------------------------
+ // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
+
+ public static $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b)
+ );
+
+ public static function getFormatInfo($mask, $level)
+ {
+ if ($mask < 0 || $mask > 7)
+ return 0;
+
+ if ($level < 0 || $level > 3)
+ return 0;
+
+ return self::$formatInfo[$level][$mask];
+ }
+
+ // Frame ---------------------------------------------------------------
+ // Cache of initial frames.
+
+ public static $frames = [];
+
+ /** --------------------------------------------------------------------
+ * Put a finder pattern.
+ * @param frame
+ * @param width
+ * @param ox,oy upper-left coordinate of the pattern
+ */
+ public static function putFinderPattern(&$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+
+ for ($y = 0; $y < 7; $y++) {
+ QrStr::set($frame, $ox, $oy + $y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function createFrame($version)
+ {
+ $width = self::$capacity[$version][Constants::QRCAP_WIDTH];
+ $frameLine = str_repeat("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+
+ // Finder pattern
+ self::putFinderPattern($frame, 0, 0);
+ self::putFinderPattern($frame, $width - 7, 0);
+ self::putFinderPattern($frame, 0, $width - 7);
+
+ // Separator
+ $yOffset = $width - 7;
+
+ for ($y = 0; $y < 7; $y++) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ $yOffset++;
+ }
+
+ $setPattern = str_repeat("\xc0", 8);
+
+ QrStr::set($frame, 0, 7, $setPattern);
+ QrStr::set($frame, $width - 8, 7, $setPattern);
+ QrStr::set($frame, 0, $width - 8, $setPattern);
+
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ QrStr::set($frame, 0, 8, $setPattern);
+ QrStr::set($frame, $width - 8, 8, $setPattern, 8);
+
+ $yOffset = $width - 8;
+
+ for ($y = 0; $y < 8; $y++, $yOffset++) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+
+ // Timing pattern
+
+ for ($i = 1; $i < $width - 15; $i++) {
+ $frame[6][7 + $i] = chr(0x90 | ($i & 1));
+ $frame[7 + $i][6] = chr(0x90 | ($i & 1));
+ }
+
+ // Alignment pattern
+ self::putAlignmentPattern($version, $frame, $width);
+
+ // Version information
+ if ($version >= 7) {
+ $vinf = self::getVersionPattern($version);
+
+ $v = $vinf;
+
+ for ($x = 0; $x < 6; $x++) {
+ for ($y = 0; $y < 3; $y++) {
+ $frame[($width - 11) + $y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+
+ $v = $vinf;
+ for ($y = 0; $y < 6; $y++) {
+ for ($x = 0; $x < 3; $x++) {
+ $frame[$y][$x + ($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+
+ return $frame;
+ }
+
+
+ /**
+ * @param int $frame
+ * @return bool|string
+ */
+ public static function serial(int $frame): bool|string
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ /**
+ * @param string $code
+ * @return array
+ */
+ public static function unserial(string $code): array
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ /**
+ * @param int $version
+ * @return array|mixed|string[]|null
+ */
+ public static function newFrame(int $version): mixed
+ {
+ if ($version < 1 || $version > Constants::QRSPEC_VERSION_MAX)
+ return null;
+
+ if (!isset(self::$frames[$version])) {
+
+ $fileName = Constants::QR_CACHE_DIR . 'frame_' . $version . '.dat';
+
+ if (Constants::QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if (is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsBlockNum(array $spec): mixed
+ {
+ return $spec[0] + $spec[3];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsBlockNum1(array $spec): mixed
+ {
+ return $spec[0];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsDataCodes1(array $spec): mixed
+ {
+ return $spec[1];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsEccCodes1(array $spec): mixed
+ {
+ return $spec[2];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsBlockNum2(array $spec): mixed
+ {
+ return $spec[3];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsDataCodes2(array $spec): mixed
+ {
+ return $spec[4];
+ }
+
+ /**
+ * @param array $spec
+ * @return mixed
+ */
+ public static function rsEccCodes2(array $spec): mixed
+ {
+ return $spec[2];
+ }
+
+ /**
+ * @param array $spec
+ * @return float|int
+ */
+ public static function rsDataLength(array $spec): float|int
+ {
+ return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]);
+ }
+
+ /**
+ * @param array $spec
+ * @return float|int
+ */
+ public static function rsEccLength(array $spec): float|int
+ {
+ return ($spec[0] + $spec[3]) * $spec[2];
+ }
+
+}
\ No newline at end of file
diff --git a/src/Util/Qrcode/Lib/QrSplit.php b/src/Util/Qrcode/Lib/QrSplit.php
new file mode 100644
index 0000000..655bff7
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrSplit.php
@@ -0,0 +1,340 @@
+data_str = $data_str;
+ $this->input = $input;
+ $this->mode_hint = $mode_hint;
+ }
+
+ /**
+ * @param string $str
+ * @param int $pos
+ * @return bool
+ */
+ public static function isDigit(string $str, int $pos): bool
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0')) && (ord($str[$pos]) <= ord('9')));
+ }
+
+ /**
+ * @param string $str
+ * @param int $pos
+ * @return bool
+ */
+ public static function isalnumat(string $str, int $pos): bool
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QrInput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ /**
+ * @param int $pos
+ * @return int
+ */
+ public function identifyMode(int $pos): int
+ {
+ if ($pos >= strlen($this->data_str))
+ return Constants::QR_MODE_NUL;
+
+ $c = $this->data_str[$pos];
+
+ if (self::isDigit($this->data_str, $pos)) {
+ return Constants::QR_MODE_NUM;
+ } else if (self::isalnumat($this->data_str, $pos)) {
+ return Constants::QR_MODE_AN;
+ } else if ($this->mode_hint == Constants::QR_MODE_KANJI) {
+
+ if ($pos + 1 < strlen($this->data_str)) {
+ $d = $this->data_str[$pos + 1];
+ $word = (ord($c) << 8) | ord($d);
+ if (($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return Constants::QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return Constants::QR_MODE_8;
+ }
+
+ /**
+ * @return int|mixed
+ */
+ public function eatNum(): mixed
+ {
+ $ln = QrSpec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while (self::isDigit($this->data_str, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if ($mode == Constants::QR_MODE_8) {
+ $dif = QrInput::estimateBitsModeNum($run) + 4 + $ln
+ + QrInput::estimateBitsMode8(1) // + 4 + l8
+ - QrInput::estimateBitsMode8($run + 1); // - 4 - l8
+ if ($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if ($mode == Constants::QR_MODE_AN) {
+ $dif = QrInput::estimateBitsModeNum($run) + 4 + $ln
+ + QrInput::estimateBitsModeAn(1) // + 4 + la
+ - QrInput::estimateBitsModeAn($run + 1);// - 4 - la
+ if ($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_NUM, $run, str_split($this->data_str));
+ if ($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ /**
+ * @return int|mixed
+ */
+ public function eatAn(): mixed
+ {
+ $la = QrSpec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion());
+ $ln = QrSpec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while (self::isalnumat($this->data_str, $p)) {
+ if (self::isDigit($this->data_str, $p)) {
+ $q = $p;
+ while (self::isDigit($this->data_str, $q)) {
+ $q++;
+ }
+
+ $dif = QrInput::estimateBitsModeAn($p) // + 4 + la
+ + QrInput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QrInput::estimateBitsModeAn($q); // - 4 - la
+
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if (!self::isalnumat($this->data_str, $p)) {
+ $dif = QrInput::estimateBitsModeAn($run) + 4 + $la
+ + QrInput::estimateBitsMode8(1) // + 4 + l8
+ - QrInput::estimateBitsMode8($run + 1); // - 4 - l8
+ if ($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_AN, $run, str_split($this->data_str));
+ if ($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ /**
+ * @return int
+ */
+ public function eatKanji(): int
+ {
+ $p = 0;
+
+ while ($this->identifyMode($p) == Constants::QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_KANJI, $p, str_split($this->data_str));
+ if ($ret < 0)
+ return -1;
+
+ return $ret;
+ }
+
+ /**
+ * @return int|mixed
+ */
+ public function eat8(): mixed
+ {
+ $la = QrSpec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion());
+ $ln = QrSpec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $data_strLen = strlen($this->data_str);
+
+ while ($p < $data_strLen) {
+
+ $mode = $this->identifyMode($p);
+ if ($mode == Constants::QR_MODE_KANJI) {
+ break;
+ }
+ if ($mode == Constants::QR_MODE_NUM) {
+ $q = $p;
+ while (self::isDigit($this->data_str, $q)) {
+ $q++;
+ }
+ $dif = QrInput::estimateBitsMode8($p) // + 4 + l8
+ + QrInput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QrInput::estimateBitsMode8($q); // - 4 - l8
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if ($mode == Constants::QR_MODE_AN) {
+ $q = $p;
+ while (self::isalnumat($this->data_str, $q)) {
+ $q++;
+ }
+ $dif = QrInput::estimateBitsMode8($p) // + 4 + l8
+ + QrInput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QrInput::estimateBitsMode8($q); // - 4 - l8
+ if ($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(Constants::QR_MODE_8, $run, str_split($this->data_str));
+
+ if ($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+
+ /**
+ * @return int|void
+ */
+ public function splitString()
+ {
+ while (strlen($this->data_str) > 0) {
+ if ($this->data_str == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case Constants::QR_MODE_NUM:
+ $length = $this->eatNum();
+ break;
+ case Constants::QR_MODE_AN:
+ $length = $this->eatAn();
+ break;
+ case Constants::QR_MODE_KANJI:
+ if ($hint == Constants::QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default:
+ $length = $this->eat8();
+ break;
+
+ }
+
+ if ($length == 0) return 0;
+ if ($length < 0) return -1;
+
+ $this->data_str = substr($this->data_str, $length);
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function toUpper(): string
+ {
+ $stringLen = strlen($this->data_str);
+ $p = 0;
+
+ while ($p < $stringLen) {
+ $mode = self::identifyMode((int)substr($this->data_str, $p), $this->mode_hint);
+ if ($mode == Constants::QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->data_str[$p]) >= ord('a') && ord($this->data_str[$p]) <= ord('z')) {
+ $this->data_str[$p] = chr(ord($this->data_str[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->data_str;
+ }
+
+ /**
+ * @param string|null $string $string
+ * @param QrInput $input
+ * @param $mode_hint
+ * @param bool $case_sensitive
+ * @return int|void
+ * @throws Exception
+ */
+ public static function splitStringToQrInput(?string $string, QrInput $input, $mode_hint, bool $case_sensitive = true)
+ {
+ if ($string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QrSplit($string, $input, $mode_hint);
+
+ if (!$case_sensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+}
diff --git a/src/Util/Qrcode/Lib/QrStr.php b/src/Util/Qrcode/Lib/QrStr.php
new file mode 100644
index 0000000..e95719d
--- /dev/null
+++ b/src/Util/Qrcode/Lib/QrStr.php
@@ -0,0 +1,34 @@
+mm' : ' ';
+ $pxMap[1] = ' ';
+ //
+ $text = QrConsole::text($text);
+ $length = strlen($text[0]);
+ $output->write("\n");
+ //
+ foreach ($text as $line) {
+ $output->write($pxMap[0]);
+ for ($i = 0; $i < $length; $i++) {
+ $type = substr($line, $i, 1);
+ $output->write($pxMap[$type]);
+ }
+ $output->writeln($pxMap[0]);
+ }
+ }
+
+ /**
+ * @use determine the console is windows or linux.
+ * @return bool
+ */
+ protected static function isWin(): bool
+ {
+ return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
+ }
+
+ /**
+ * @use init qrCode style.
+ * @param OutputInterface $output
+ */
+ protected static function initQrcodeStyle(OutputInterface $output): void
+ {
+ $style = new OutputFormatterStyle('black', 'black', ['bold']);
+ $output->getFormatter()->setStyle('blackc', $style);
+ $style = new OutputFormatterStyle('white', 'white', ['bold']);
+ $output->getFormatter()->setStyle('whitec', $style);
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Qrcode/VERSION b/src/Util/Qrcode/VERSION
new file mode 100644
index 0000000..9df7dbd
--- /dev/null
+++ b/src/Util/Qrcode/VERSION
@@ -0,0 +1,3 @@
+aferrandini/phpqrcode
+1.1.5
+2012021604
diff --git a/src/Util/Resource/BaseResource.php b/src/Util/Resource/BaseResource.php
new file mode 100644
index 0000000..af8de48
--- /dev/null
+++ b/src/Util/Resource/BaseResource.php
@@ -0,0 +1,154 @@
+resource = $this->resource->set($key, $value);
+ }
+
+ /**
+ * @use 根据类型获取值
+ * @param string $key
+ * @param mixed|null $default
+ * @param string $type
+ * @return mixed
+ */
+ public function get(string $key, mixed $default = null, string $type = 'default'): mixed
+ {
+ // 判断是否被修改,否则重新加载文件
+ if (fileatime($this->filepath) != $this->last_access) {
+ // TODO 此处逻辑好像重复了
+ $this->loadResource($this->filename, $this->parser);
+ }
+ return match ($type) {
+ 'int' => $this->resource->getInt($key, $default),
+ 'string' => $this->resource->getString($key, $default),
+ 'bool' => $this->resource->getBool($key, $default),
+ 'array' => $this->resource->getArray($key, $default),
+ default => $this->resource->get($key, $default),
+ };
+ }
+
+ /**
+ * @use 加载资源文件
+ * @param string $filename
+ * @param string $parser
+ * @return void
+ */
+ protected function loadResource(string $filename, string $parser): void
+ {
+ // 获取
+ $filepath = $this->getFilePath($filename);
+ // 验证
+ $this->validateFile($filepath, $filename);
+ // 加载
+ $resource = (new Resource())->loadF($filepath, $parser);
+ // 赋值
+ $this->saveInfo($filename, $filepath, $parser, $resource);
+ }
+
+ /**
+ * @use 检查文件是否存在
+ * @param string $filepath
+ * @param string $filename
+ * @return void
+ */
+ protected function validateFile(string $filepath, string $filename): void
+ {
+ if (!is_file($filepath)) {
+ die("资源文件 $filename 加载失败,请参照文档查看或添加资源文件!");
+ }
+ }
+
+ /**
+ * @param string $filename
+ * @param string $filepath
+ * @param string $parser
+ * @param Resource $resource
+ * @return void
+ */
+ protected function saveInfo(string $filename, string $filepath, string $parser, Resource $resource): void
+ {
+ $this->filename = $filename;
+ // dirname($conf_filepath).DIRECTORY_SEPARATOR.$conf_filename;
+ $this->filepath = $filepath;
+ $this->resource = $resource;
+ $this->parser = $parser;
+ $this->updateLastAccess();
+ }
+
+ /**
+ * @use 更新最新修改时间
+ * @return void
+ */
+ protected function updateLastAccess(): void
+ {
+ $this->last_access = fileatime($this->filepath);
+ }
+
+ /**
+ * @use 获取文件真实路径
+ * @param string $filename
+ * @return string
+ */
+ abstract protected function getFilePath(string $filename): string;
+
+}
\ No newline at end of file
diff --git a/src/Util/Resource/BaseResourcePoly.php b/src/Util/Resource/BaseResourcePoly.php
new file mode 100644
index 0000000..21ef381
--- /dev/null
+++ b/src/Util/Resource/BaseResourcePoly.php
@@ -0,0 +1,153 @@
+resource = $this->resource->set($key, $value);
+ }
+
+ /**
+ * @use 根据类型获取值
+ * @param string $key
+ * @param mixed|null $default
+ * @param string $type
+ * @return mixed
+ */
+ public function get(string $key, mixed $default = null, string $type = 'default'): mixed
+ {
+ // 判断是否被修改,否则重新加载文件
+ if (fileatime($this->filepath) != $this->last_access) {
+ // TODO 此处逻辑好像重复了
+ $this->loadResource($this->filename, $this->parser);
+ }
+ return match ($type) {
+ 'int' => $this->resource->getInt($key, $default),
+ 'string' => $this->resource->getString($key, $default),
+ 'bool' => $this->resource->getBool($key, $default),
+ 'array' => $this->resource->getArray($key, $default),
+ default => $this->resource->get($key, $default),
+ };
+ }
+
+ /**
+ * @use 加载资源文件
+ * @param string $filename
+ * @param string $parser
+ * @return void
+ */
+ protected function loadResource(string $filename, string $parser): void
+ {
+ // 获取
+ $filepath = $this->getFilePath($filename);
+ // 验证
+ $this->validateFile($filepath, $filename);
+ // 加载
+ $resource = (new Resource())->loadF($filepath, $parser);
+ // 赋值
+ $this->saveInfo($filename, $filepath, $parser, $resource);
+ }
+
+ /**
+ * @use 检查文件是否存在
+ * @param string $filepath
+ * @param string $filename
+ * @return void
+ */
+ protected function validateFile(string $filepath, string $filename): void
+ {
+ if (!is_file($filepath)) {
+ die("资源文件 $filename 加载失败,请参照文档查看或添加资源文件!");
+ }
+ }
+
+ /**
+ * @param string $filename
+ * @param string $filepath
+ * @param string $parser
+ * @param Resource $resource
+ * @return void
+ */
+ protected function saveInfo(string $filename, string $filepath, string $parser, Resource $resource): void
+ {
+ $this->filename = $filename;
+ // dirname($conf_filepath).DIRECTORY_SEPARATOR.$conf_filename;
+ $this->filepath = $filepath;
+ $this->resource = $resource;
+ $this->parser = $parser;
+ $this->updateLastAccess();
+ }
+
+ /**
+ * @use 更新最新修改时间
+ * @return void
+ */
+ protected function updateLastAccess(): void
+ {
+ $this->last_access = fileatime($this->filepath);
+ }
+
+ /**
+ * @use 获取文件真实路径
+ * @param string $filename
+ * @return string
+ */
+ abstract protected function getFilePath(string $filename): string;
+
+}
\ No newline at end of file
diff --git a/src/Util/Resource/Collection.php b/src/Util/Resource/Collection.php
new file mode 100644
index 0000000..3db27a7
--- /dev/null
+++ b/src/Util/Resource/Collection.php
@@ -0,0 +1,218 @@
+ https://github.com/phppkg/config
+ *
+ * @package PhpPkg\Config
+ *
+ * 支持 链式的子节点 设置 和 值获取
+ * e.g:
+ * ```
+ * $data = [
+ * 'foo' => [
+ * 'bar' => [
+ * 'yoo' => 'value'
+ * ]
+ * ]
+ * ];
+ * $config = new Collection();
+ * $config->get('foo.bar.yoo')` equals to $data['foo']['bar']['yoo'];
+ * ```
+ */
+class Collection extends \Toolkit\Stdlib\Std\Collection
+{
+ /**
+ * @var int
+ */
+ public int $mergeDepth = 3;
+
+ /**
+ * The key path separator.
+ *
+ * @var string
+ */
+ public string $keyPathSep = '.';
+
+ /**
+ * set config value by key/path
+ *
+ * @param string $key
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ public function set(string $key, mixed $value): self
+ {
+ if ($this->keyPathSep && strpos($key, $this->keyPathSep) > 0) {
+ Arr::setByPath($this->data, $key, $value, $this->keyPathSep);
+ return $this;
+ }
+
+ return parent::set($key, $value);
+ }
+
+ /**
+ * @param string $key
+ * @param mixed|null $default
+ *
+ * @return mixed
+ */
+ public function get(string $key, mixed $default = null): mixed
+ {
+ if ($this->keyPathSep && strpos($key, $this->keyPathSep) > 0) {
+ return Arr::getByPath($this->data, $key, $default, $this->keyPathSep);
+ }
+
+ return parent::get($key, $default);
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function exists(string $key): bool
+ {
+ return $this->get($key) !== null;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function has(string $key): bool
+ {
+ return $this->exists($key);
+ }
+
+ /**
+ * @return array
+ */
+ public function getData(): array
+ {
+ return $this->data;
+ }
+
+ /**
+ * @return string
+ */
+ public function getKeyPathSep(): string
+ {
+ return $this->keyPathSep;
+ }
+
+ /**
+ * @param string $keyPathSep
+ */
+ public function setKeyPathSep(string $keyPathSep): void
+ {
+ $this->keyPathSep = $keyPathSep;
+ }
+
+ /**
+ * @param array|Traversable $data
+ *
+ * @return $this
+ */
+ public function load(array|Traversable $data): self
+ {
+ $this->bindData($this->data, $data);
+
+ return $this;
+ }
+
+ /**
+ * @param array|Traversable $data
+ *
+ * @return $this
+ */
+ public function loadData(array|Traversable $data): self
+ {
+ $this->bindData($this->data, $data);
+
+ return $this;
+ }
+
+ /**
+ * @param array $parent
+ * @param array|Traversable $data
+ * @param int $depth
+ */
+ protected function bindData(array &$parent, array|Traversable $data, int $depth = 1): void
+ {
+ foreach ($data as $key => $value) {
+ if ($value === null) {
+ continue;
+ }
+
+ if (is_array($value) && isset($parent[$key]) && is_array($parent[$key])) {
+ if ($depth > $this->mergeDepth) {
+ $parent[$key] = $value;
+ } else {
+ $this->bindData($parent[$key], $value, ++$depth);
+ }
+ } else {
+ $parent[$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getKeys(): array
+ {
+ return array_keys($this->data);
+ }
+
+ /**
+ * @return RecursiveArrayIterator
+ */
+ public function getIterator(): Traversable
+ {
+ return new RecursiveArrayIterator($this->data);
+ }
+
+ /**
+ * Unset an offset in the iterator.
+ *
+ * @param mixed $offset
+ */
+ public function offsetUnset($offset): void
+ {
+ $this->set($offset, null);
+ }
+
+ public function __clone()
+ {
+ $this->data = unserialize(serialize($this->data), [
+ 'allowed_classes' => self::class
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Resource/CollectionInterface.php b/src/Util/Resource/CollectionInterface.php
new file mode 100644
index 0000000..3c45701
--- /dev/null
+++ b/src/Util/Resource/CollectionInterface.php
@@ -0,0 +1,62 @@
+file_path = $file_path;
+ // 存储解析器
+ $this->parser = $parser;
+ // 加载文件
+ $this->config = $this->switchParser($file_path, $parser);
+ // 加载数据
+ $this->reload();
+ //
+ return $this;
+ }
+
+ /**
+ * @use 切换解析器
+ * @param string $filepath
+ * @param string $format
+ * @return Data
+ */
+ protected function switchParser(string $filepath, string $format): Data
+ {
+ return match ($format) {
+ Resource::FORMAT_INI => ini($filepath),
+ Resource::FORMAT_PHP => phpArray($filepath),
+ Resource::FORMAT_YML, Resource::FORMAT_YAML => yml($filepath),
+ Resource::FORMAT_JSON => json($filepath),
+ default => data($filepath),
+ };
+ }
+
+ /**
+ * @use 清空并重载数据
+ * @return Resource
+ */
+ protected function reload(): Resource
+ {
+ // 转换一次 ${}
+ $expander = new Expander();
+ $expander->setStringifier(new Stringifier());
+ $expanded = $expander->expandArrayProperties($this->config->getArrayCopy());
+ // 清除数据
+ $this->clear();
+ // 加载数据
+ $this->load($expanded);
+ //
+ return $this;
+ }
+
+
+}
+
\ No newline at end of file
diff --git a/src/core/App.php b/src/core/App.php
deleted file mode 100644
index 7c1262e..0000000
--- a/src/core/App.php
+++ /dev/null
@@ -1,234 +0,0 @@
-inspect_configure()->inspect_extension();
- return $this;
- }
-
- /**
- * @use 加载配置
- * @param $argv
- * @return $this
- */
- public function load($argv): App
- {
- $this->initDataFolder();
- $args = (new BCommand($argv))->run();
- $filename = $args->args()[0] ?? 'user.ini';
- // 加载配置
- Config::getInstance()->load($filename);
- // 加载设备
- Device::getInstance()->load($filename);
- // 引导参数
- $this->selectMode($args);
- $this->restoreMode($args);
-
- return $this;
- }
-
-
- /**
- * @use 新任务
- * @param string $taskName
- * @param string $dir
- */
- private function newTask(string $taskName, string $dir)
- {
- asyncCall(function () use ($taskName, $dir) {
- while (true) {
- try {
- call_user_func(array("BiliHelper\\$dir\\" . $taskName, 'run'), []);
- } catch (Throwable $e) {
- // TODO 多次错误删除tasks_***.json文件
- $error_msg = "MSG: {$e->getMessage()} CODE: {$e->getCode()} FILE: {$e->getFile()} LINE: {$e->getLine()}";
- Log::error($error_msg);
- // Notice::push('error', $error_msg);
- }
- if ($dir == 'Plugin')
- yield call_user_func(array("BiliHelper\\$dir\\" . $taskName, 'Delayed'), []);
- else
- break;
- }
- });
- }
-
- /**
- * @use Script模式
- */
- private function script_m()
- {
- $scripts = [
- 'UnFollow' => '批量取消关注(暂测试)',
- 'DelDynamic' => '批量清理动态(未完成)'
- ];
-
- $choice = BaseTask::choice($scripts, 'UnFollow');
- $this->newTask($choice, 'Script');
- }
-
- /**
- * @use Loop模式
- */
- private function loop_m()
- {
- $plugins = [
- 'CheckUpdate',
- 'Login',
- 'Schedule',
- 'MainSite',
- 'DailyBag',
- 'ManGa',
- 'ActivityLottery',
- 'Competition',
- 'DoubleHeart',
- 'DailyTask',
- 'Barrage',
- 'Silver2Coin',
- 'Judge',
- 'GiftSend',
- 'GroupSignIn',
- 'GiftHeart',
- 'SmallHeart',
- 'MaterialObject',
- 'AloneTcpClient',
- 'ZoneTcpClient',
- 'StormRaffle',
- 'GiftRaffle',
- 'PkRaffle',
- 'GuardRaffle',
- 'AnchorRaffle',
- 'AwardRecord',
- 'Forward',
- 'CapsuleLottery',
- 'PolishTheMedal',
- 'VipPrivilege',
- 'BpConsumption',
- 'LiveReservation',
- // 'Silver', // Abandoned
- 'Statistics',
-
- ];
- foreach ($plugins as $plugin) {
- $this->newTask($plugin, 'Plugin');
- }
- Loop::run();
- }
-
- /**
- * @use 选择模式
- * @param object $args
- */
- private function selectMode(object $args)
- {
- // 可能会有其他模式出现 暂定
- // 0 默认值 默认模式,1 脚本模式 ...
- if ($args->script) {
- $this->mode = 1;
- }
- }
-
- /**
- * @use 复位模式
- * @param object $args
- */
- private function restoreMode(object $args)
- {
- // 复位 后期添加其他复位
- if ($args->restore) {
- Task::getInstance()->restore();
- }
- }
-
-
- /**
- * @use 核心运行
- */
- public function start()
- {
- switch ($this->mode) {
- case 0:
- // 默认
- Log::info('执行Loop模式');
- $this->loop_m();
- break;
- case 1:
- // 脚本
- Log::info('执行Script模式');
- $this->script_m();
- break;
- default:
- Log::error("请检查,没有选定的执行模式");
- exit();
- }
- }
-}
diff --git a/src/core/BCommand.php b/src/core/BCommand.php
deleted file mode 100644
index a337a9c..0000000
--- a/src/core/BCommand.php
+++ /dev/null
@@ -1,59 +0,0 @@
-argv = $argv;
- }
-
- public function run()
- {
- $cli = new Command('BHP-S', 'BHP命令行工具.');
- $cli->version('0.0.1-dev')
- ->option('-s --script', '执行的Script模式.', null, false)
- ->option('-r --restore', '任务排程复位(暂定).', null, false);
- try {
- $args = $cli->parse($this->argv);
- } catch (Exception $e) {
- Log::error($e->getMessage());
- die('解析命令行参数错误');
- }
- return $args;
- }
-
-}
diff --git a/src/core/Cache.php b/src/core/Cache.php
deleted file mode 100644
index bd216b9..0000000
--- a/src/core/Cache.php
+++ /dev/null
@@ -1,126 +0,0 @@
-caches[$classname])) {
- $username = getConf('username', 'login.account');
- // 判断字符串中是否有中文
- if (preg_match("/[\x7f-\xff]/", $username)) {
- $pinyin = new Pinyin(); // 默认
- $username = $pinyin->permalink($username); // yong-hu-ming
- }
- // 如果不存在缓存 初始化 "BHP_username_APP.dat"
- $this->caches[$classname] = new Flintstone(
- $this->removeSpecStr('BHP_' . $username . '_' . $classname),
- [
- 'dir' => APP_CACHE_PATH,
- 'gzip' => true,
- 'formatter' => new JsonFormatter()
- ]
- );
- }
- $this->cache = $this->caches[$classname];
- return $this;
- // self::$instance->set('bob', ['email' => 'bob@site.com', 'password' => '123456']);
- }
-
- /**
- * @use 获取调用链类
- * @return mixed
- */
- private function backtraceClass(): mixed
- {
- // TODO 耦合度过高 需要解耦
- $backtraces = debug_backtrace();
- array_shift($backtraces);
- return pathinfo(basename($backtraces[2]['file']))['filename'];
- }
-
- /**
- * @use 获取调用类
- * @param string $classname
- * @return $this
- */
- private function getClassObj(string $classname): static
- {
- if ($classname == '') {
- $classname = $this->backtraceClass();
- }
- return $this->load($classname);
- }
-
- /**
- * @use 获取值
- * @param string $key
- * @param string $extra_name
- * @return mixed
- */
- public function _get(string $key, string $extra_name = ''): mixed
- {
- // Get a key
- // $user = $users->get('bob');
- // echo 'Bob, your email is ' . $user['email'];
- return $this->getClassObj($extra_name)->cache->get($key);
- }
-
- /**
- * @use 写入值
- * @param string $key
- * @param $data
- * @param string $extra_name
- */
- public function _set(string $key, $data, string $extra_name = '')
- {
- // Set a key
- // $users->set('bob', ['email' => 'bob@site.com', 'password' => '123456']);
- $this->getClassObj($extra_name)->cache->set($key, $data);
- }
-
- /**
- * @use 去除特殊符号
- * @param string $data
- * @return string
- */
- private function removeSpecStr(string $data): string
- {
- $specs = str_split("-.,:;'*?~`!@#$%^&+=)(<>{}]|\/、");
- foreach ($specs as $spec) {
- $data = str_replace($spec, '', $data);
- }
- return $data;
- }
-}
-
-
diff --git a/src/core/Config.php b/src/core/Config.php
deleted file mode 100644
index 09dfded..0000000
--- a/src/core/Config.php
+++ /dev/null
@@ -1,94 +0,0 @@
-load_file = $load_file;
- $this->config_path = $config_path;
- // $config_path = dirname($config_path).DIRECTORY_SEPARATOR.$load_file;
- $this->app_config = new IniModifier($this->config_path);
- $this->last_time = fileatime($this->config_path);
- }
-
-
- /**
- * @use 写入值
- * @param $name
- * @param $value
- * @param int|string $section
- * @param null $key
- * @throws \Jelix\IniFile\IniException
- */
- public function _set($name, $value, int|string $section = 0, $key = null)
- {
- $this->app_config->setValue($name, $value, $section, $key);
- $this->app_config->save();
- // 保存修改时间
- $this->last_time = fileatime($this->config_path);
- }
-
- /**
- * @use 获取值
- * @param $name
- * @param int|string $section
- * @param null $key
- * @return mixed
- */
- public function _get($name, int|string $section = 0, $key = null): mixed
- {
- // 判断是否被修改 重新加载文件
- // echo $this->last_time.PHP_EOL;
- // echo fileatime($this->config_path);
- if (fileatime($this->config_path) != $this->last_time) {
- $this->load($this->load_file);
- }
- return $this->app_config->getValue($name, $section, $key);
- }
-}
-
-
-
diff --git a/src/core/Core.php b/src/core/Core.php
new file mode 100644
index 0000000..9fa46af
--- /dev/null
+++ b/src/core/Core.php
@@ -0,0 +1,106 @@
+global_path = $global_path;
+ $this->profile_name = $profile_name;
+ // 定义全局常量
+ $this->initSystemConstant();
+ // 初始化全局文件夹
+ $this->initSystemPath();
+ }
+
+ /**
+ * @use 初始化全局常量
+ * @return void
+ */
+ protected function initSystemConstant(): void
+ {
+ define('APP_MICROSECOND', 1000000);
+ define('APP_RESOURCES_PATH', $this->global_path . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
+ define('APP_PLUGIN_PATH', $this->global_path . DIRECTORY_SEPARATOR . 'plugin' . DIRECTORY_SEPARATOR);
+ // Profile
+ define('PROFILE_CONFIG_PATH', $this->fillPath('config'));
+ define('PROFILE_DEVICE_PATH', $this->fillPath('device'));
+ define('PROFILE_LOG_PATH', $this->fillPath('log'));
+ define('PROFILE_TASK_PATH', $this->fillPath('task'));
+ define('PROFILE_CACHE_PATH', $this->fillPath('cache'));
+
+ // 判断profile/*是否存在存在
+ if (!is_dir(PROFILE_CONFIG_PATH)) {
+ die("加载 {$this->profile_name} 用户文档失败,请检查路径!");
+ }
+ }
+
+ /**
+ * @use 补充目录
+ * @param string $path
+ * @return string
+ */
+ protected function fillPath(string $path): string
+ {
+ // */profile/*/*
+ return $this->global_path . DIRECTORY_SEPARATOR . 'profile' . DIRECTORY_SEPARATOR . $this->profile_name . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ * @use 初始化系统目录(创建、设置权限)
+ * @return void
+ */
+ protected function initSystemPath(): void
+ {
+ $system_paths = [
+ PROFILE_CONFIG_PATH,
+ PROFILE_DEVICE_PATH,
+ PROFILE_LOG_PATH,
+ PROFILE_TASK_PATH,
+ PROFILE_CACHE_PATH
+ ];
+ foreach ($system_paths as $path) {
+ if (!file_exists($path)) {
+ Path::CreateFolder($path);
+ Path::SetFolderPermissions($path);
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/core/Curl.php b/src/core/Curl.php
deleted file mode 100644
index 4627ae5..0000000
--- a/src/core/Curl.php
+++ /dev/null
@@ -1,362 +0,0 @@
-getBody();
- Log::debug($body);
- return $body;
- }
-
- /**
- * @use GET请求
- * @param $os
- * @param $url
- * @param array $params
- * @param array $headers
- * @param float $timeout
- * @return mixed
- */
- public static function get($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): mixed
- {
- Log::debug("GET: $url");
- $headers = self::getHeaders($os, $headers);
- $payload['query'] = count($params) ? $params : [];
- $options = self::getClientOpt($payload, $headers, $timeout);
- $request = self::clientHandle($url, 'get', $options);
- $body = $request->getBody();
- Log::debug($body);
- return $body;
- }
-
- /**
- * @use PUT请求
- * @param $os
- * @param $url
- * @param array $params
- * @param array $headers
- * @param float $timeout
- * @return mixed
- */
- public static function put($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): mixed
- {
- Log::debug("PUT: $url");
- $headers = self::getHeaders($os, $headers);
- $payload['json'] = count($params) ? $params : [];
- $options = self::getClientOpt($payload, $headers, $timeout);
- $request = self::clientHandle($url, 'post', $options);
- $body = $request->getBody();
- Log::debug($body);
- return $body;
- }
-
- /**
- * @use 并发POST请求
- * @param $os
- * @param $url
- * @param array $tasks
- * @param array $headers
- * @param float $timeout
- * @return array
- */
- public static function async($os, $url, array $tasks = [], array $headers = [], float $timeout = 30.0): array
- {
- self::$async_opt = [
- 'tasks' => $tasks,
- 'counter' => 1,
- 'count' => count($tasks),
- 'concurrency' => min(count($tasks), 10)
- ];
- Log::debug("ASYNC: $url");
- $headers = self::getHeaders($os, $headers);
- $requests = function ($total) use ($url, $headers, $tasks, $timeout) {
- foreach ($tasks as $task) {
- yield function () use ($url, $headers, $task, $timeout) {
- $payload['form_params'] = $task['payload'];
- $options = self::getClientOpt($payload, $headers, $timeout);
- return self::clientHandle($url, 'postAsync', $options);
- };
- }
- };
- $pool = new Pool(self::$client, $requests(self::$async_opt['count']), [
- 'concurrency' => self::$async_opt['concurrency'],
- 'fulfilled' => function ($response, $index) {
- $res = $response->getBody();
- // Log::notice("启动多线程 {$index}");
- self::$results[] = [
- 'content' => $res,
- 'source' => self::$async_opt['tasks'][$index]['source']
- ];
- self::countedAndCheckEnded();
- },
- 'rejected' => function ($reason, $index) {
- Log::error("多线程第 $index 个请求失败, ERROR: $reason");
- self::countedAndCheckEnded();
- },
- ]);
- // 开始发送请求
- $promise = $pool->promise();
- $promise->wait();
- return self::getResults();
- }
-
- /**
- * @use 单次请求
- * @param $method
- * @param $url
- * @param array $payload
- * @param array $headers
- * @param int $timeout
- * @return false|string|null
- */
- public static function request($method, $url, array $payload = [], array $headers = [], int $timeout = 10): bool|string|null
- {
- Log::debug("REQUEST: $url");
- $options = array(
- 'http' => array(
- 'method' => strtoupper($method),
- 'header' => self::arr2str($headers),
- 'content' => http_build_query($payload),
- 'timeout' => $timeout,
- ),
- );
- $result = $url ? @file_get_contents($url, false, stream_context_create($options)) : null;
- Log::debug($result);
- return $result ?: null;
- }
-
- /**
- * @use 计数搭配并发使用
- */
- private static function countedAndCheckEnded()
- {
- if (self::$async_opt['counter'] < self::$async_opt['count']) {
- self::$async_opt['counter']++;
- return;
- }
- // 请求结束
- self::$async_opt = [];
- }
-
- /**
- * @use 请求中心异常处理
- * @param string $url
- * @param string $method
- * @param array $options
- * @return mixed
- */
- private static function clientHandle(string $url, string $method, array $options): mixed
- {
- $max_retry = range(1, 40);
- foreach ($max_retry as $retry) {
- try {
- $response = call_user_func_array([self::$client, $method], [$url, $options]);
- if (is_null($response) or empty($response)) throw new Exception("Value IsEmpty");
- return $response;
- } catch (RequestException $e) {
- // var_dump($e->getRequest());
- if ($e->hasResponse()) var_dump($e->getResponse());
- } catch (Exception $e) {
- // $e->getHandlerContext()
- // var_dump($e);
- }
- Log::warning("Target -> URL: $url METHOD: $method");
- Log::warning("CURl -> RETRY: $retry ERROR: {$e->getMessage()} ERRNO: {$e->getCode()} STATUS: Waiting for recovery!");
- sleep(15);
- }
- exit('网络异常,超出最大尝试次数,退出程序~');
- }
-
- /**
- * @use 获取请求配置
- * @param array $add_options
- * @param array $headers
- * @param float $timeout
- * @return array
- */
- private static function getClientOpt(array $add_options, array $headers = [], float $timeout = 30.0): array
- {
- self::$client = new Client();
- $default_options = [
- 'headers' => $headers,
- 'timeout' => $timeout,
- 'http_errors' => false,
- 'verify' => getConf('verify_ssl', 'network.ssl'),
- ];
- if (getConf('enable', 'network.proxy')) {
- $default_options['proxy'] = getConf('proxy', 'network.proxy');
- }
- return array_merge($default_options, $add_options);
- }
-
- /**
- * @use 获取Headers
- * @param string $os
- * @param array $headers
- * @return array
- */
- private static function getHeaders(string $os = 'app', array $headers = []): array
- {
- if (!self::$buvid) {
- // 缓存开始 如果存在就赋值 否则默认值
- if ($temp = getCache('buvid')) {
- self::$buvid = $temp;
- } else {
- self::$buvid = Generator::buvid();
- }
- // 缓存结束 需要的数据的放进缓存
- setCache('buvid', self::$buvid);
- }
-
- $app_headers = [
- 'env' => 'prod',
- 'APP-KEY' => 'android',
- 'Buvid' => self::$buvid,
- 'Accept' => '*/*',
- 'Accept-Encoding' => 'gzip',
- 'Accept-Language' => 'zh-cn',
- 'Connection' => 'keep-alive',
- 'User-Agent' => getDevice('device.app_headers.ua'),
- // 'Content-Type' => 'application/x-www-form-urlencoded',
- // 'User-Agent' => 'Mozilla/5.0 BiliDroid/5.51.1 (bbcallen@gmail.com)',
- // 'Referer' => 'https://live.bilibili.com/',
- ];
- $pc_headers = [
- 'Accept' => "application/json, text/plain, */*",
- 'Accept-Encoding' => 'gzip, deflate',
- 'Accept-Language' => "zh-CN,zh;q=0.9",
- 'User-Agent' => getDevice('device.pc_headers.ua'),
- // 'Content-Type' => 'application/x-www-form-urlencoded',
- // 'Referer' => 'https://live.bilibili.com/',
- ];
- $other_headers = [
- 'User-Agent' => getDevice('device.other_headers.ua')
- ];
- $default_headers = ${$os . "_headers"} ?? $other_headers;
- if (in_array($os, ['app', 'pc']) && getCookie() != "") {
- $default_headers['Cookie'] = getCookie();
- }
- // return self::formatHeaders(array_merge($default_headers, $headers));
- return array_merge($default_headers, $headers);
- }
-
- /**
- * @use 数组
- * @return array
- */
- private static function getResults(): array
- {
- $results = self::$results;
- self::$results = [];
- return $results;
- }
-
- /**
- * @use 关联数组转字符串
- * @param array $array
- * @return string
- */
- private static function arr2str(array $array): string
- {
- $tmp = '';
- foreach ($array as $key => $value) {
- $tmp .= "$key:$value\r\n";
- }
- return $tmp;
- }
-
- /**
- * @use GET请求
- * @param $os
- * @param $url
- * @param array $params
- * @param array $headers
- * @param float $timeout
- * @return mixed
- */
- public static function headers($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): mixed
- {
- Log::debug('HEADERS: ' . $url);
- $headers = self::getHeaders($os, $headers);
- $payload['query'] = count($params) ? $params : [];
- $payload['allow_redirects'] = false;
- $options = self::getClientOpt($payload, $headers, $timeout);
- $request = self::clientHandle($url, 'get', $options);
- Log::debug("获取Headers");
- return $request->getHeaders();
- }
-
- /**
- * @use 格式化Headers
- * @param array $headers
- * @return array
- */
- private static function formatHeaders(array $headers): array
- {
- return array_map(function ($k, $v) {
- return $k . ': ' . $v;
- }, array_keys($headers), $headers);
- }
-
- /**
- * @use 字符串or其他
- * @return array
- */
- private static function getResult(): array
- {
- $result = self::$result;
- self::$result = [];
- return array_shift($result);
- }
-}
diff --git a/src/core/Device.php b/src/core/Device.php
deleted file mode 100644
index c805a91..0000000
--- a/src/core/Device.php
+++ /dev/null
@@ -1,92 +0,0 @@
-fileRealPath($custom_file . $this->bili_file))) {
- $this->bili_file = $custom_file . $this->bili_file;
- Log::info('使用自定义' . $this->bili_file);
- }
- // 自定义设备
- if (is_file($this->fileRealPath($custom_file . $this->device_file))) {
- $this->device_file = $custom_file . $this->device_file;
- Log::info('使用自定义' . $this->device_file);
- }
- // 加载数据
- $this->device = new Config();
- $loader = new YamlConfigLoader();
- $processor = new ConfigProcessor();
- $files = [$this->bili_file, $this->device_file];
- // 循环加载
- foreach ($files as $file) {
- $processor->extend($loader->load($this->fileRealPath($file)));
- }
- $this->device->import($processor->export());
- }
-
- /**
- * @use 获取值
- * @param $key
- * @param null $defaultFallback
- * @return mixed
- */
- public function _get($key, $defaultFallback = null): mixed
- {
- return $this->device->get($key, $defaultFallback);
- }
-
-
-}
\ No newline at end of file
diff --git a/src/core/Env.php b/src/core/Env.php
deleted file mode 100644
index 1c6a04f..0000000
--- a/src/core/Env.php
+++ /dev/null
@@ -1,101 +0,0 @@
-loadJsonData();
- }
-
- /**
- * @use 检查扩展
- */
- public function inspect_extension()
- {
- $default_extensions = ['curl', 'openssl', 'sockets', 'json', 'zlib', 'mbstring'];
- foreach ($default_extensions as $extension) {
- if (!extension_loaded($extension)) {
- Log::error("检查到项目依赖 $extension 扩展未加载。");
- Log::error("请在 php.ini中启用 $extension 扩展后重试。");
- Log::error("程序常见问题请移步 $this->app_source 文档部分查看。");
- exit();
- }
- }
- }
-
- /**
- * @use 检查环境
- */
- public function inspect_configure(): Env
- {
- Log::info("欢迎使用 项目: $this->app_name@$this->app_branch 版本: $this->app_version");
- Log::info("使用说明请移步 $this->app_source 查看");
-
- if (PHP_SAPI != 'cli') {
- die("Please run this script from command line .");
- }
-// if (version_compare(PHP_VERSION, '7.4.0', '<')) {
-// die("Please upgrade PHP version > 7.4.0 .");
-// }
- if (version_compare(PHP_VERSION, '8.0.0', '<')) {
- die("Please upgrade PHP version < 8.0.0 .");
- }
- return $this;
- }
-
- /**
- * @use 加载本地JSON DATA
- */
- private function loadJsonData()
- {
- $conf = json($this->repository);
- $this->app_name = $conf->get('project', 'BiliHelper-personal');
- $this->app_version = $conf->get('version', '0.0.0.000000');
- $this->app_branch = $conf->get('branch', 'master');
- $this->app_source = $conf->get('source', 'https://github.com/lkeme/BiliHelper-personal');
- }
-}
diff --git a/src/core/Helpers.php b/src/core/Helpers.php
deleted file mode 100644
index c358543..0000000
--- a/src/core/Helpers.php
+++ /dev/null
@@ -1,141 +0,0 @@
-_get($name, $section, $key);
-}
-
-/**
- * @use 配置写入
- * @param $name
- * @param $value
- * @param int|string $section
- * @param null $key
- * @throws \Jelix\IniFile\IniException
- */
-function setConf($name, $value, int|string $section = 0, $key = null)
-{
- Config::getInstance()->_set($name, $value, $section, $key);
-}
-
-/**
- * @use 开关
- * @param string $plugin
- * @param bool $default
- * @return bool
- */
-function getEnable(string $plugin, bool $default = false): bool
-{
- return getConf('enable', $plugin) ?: $default;
-}
-
-/**
- * @use 获取ACCESS_TOKEN
- * @return string
- */
-function getAccessToken(): string
-{
- return getConf('access_token', 'login.auth');
-}
-
-/**
- * @use 获取REFRESH_TOKEN
- * @return string
- */
-function getRefreshToken(): string
-{
- return getConf('refresh_token', 'login.auth');
-}
-
-/**
- * @use 获取COOKIE
- * @return string
- */
-function getCookie(): string
-{
- return getConf('cookie', 'login.auth');
-}
-
-/**
- * @use 获取UID
- * @return int
- */
-function getUid(): int
-{
- return getConf('uid', 'login.auth');
-}
-
-/**
- * @use 获取CSRF
- * @return string
- */
-function getCsrf(): string
-{
- return getConf('csrf', 'login.auth');
-}
-
-/**
- * @use 获取设备信息
- * @param $key
- * @param null $defaultFallback
- * @return mixed
- */
-function getDevice($key, $defaultFallback = null): mixed
-{
- return Device::getInstance()->_get($key, $defaultFallback);
-}
-
-/**
- * @use 缓存读取
- * @param string $key
- * @param string $extra_name
- * @return mixed
- */
-function getCache(string $key, string $extra_name = ''): mixed
-{
- return Cache::getInstance()->_get($key, $extra_name);
-}
-
-/**
- * @use 缓存写入
- * @param string $key
- * @param $data
- * @param string $extra_name
- */
-function setCache(string $key, $data, string $extra_name = '')
-{
- Cache::getInstance()->_set($key, $data, $extra_name);
-}
diff --git a/src/core/HttpClient.php b/src/core/HttpClient.php
deleted file mode 100644
index a9ca76b..0000000
--- a/src/core/HttpClient.php
+++ /dev/null
@@ -1,169 +0,0 @@
-ch = curl_init();
-// $this->url = $url;
-// curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false); //忽略 HTTPS 证书错误
-// $this->setUA("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36");
-// }
-//
-// function __destruct()
-// {
-// //防止内存泄露
-// curl_close($this->ch);
-// }
-//
-// /**
-// * @use 设置UA
-// * @param string $ua
-// * @return $this
-// */
-// public function setUA(string $ua): HttpClient
-// {
-// curl_setopt($this->ch, CURLOPT_USERAGENT, $ua); //设置 UA
-// return $this;
-// }
-//
-// /**
-// * @use 添加Header
-// * @param string $header
-// * @return $this
-// */
-// public function addHeader(string $header): HttpClient
-// {
-// array_push($this->headers, $header);
-// return $this;
-// }
-//
-// /**
-// * @use 添加Headers
-// * @param array $headers
-// * @return $this
-// */
-// public function addHeaders(array $headers): HttpClient
-// {
-// foreach ($headers as $key => $value) {
-// array_push($this->headers, "{$key}: {$value}");
-// }
-// return $this;
-// }
-//
-// /**
-// * @use 设置Cookie
-// * @param string $cookie
-// * @return $this
-// */
-// public function setCookie(string $cookie): HttpClient
-// {
-// curl_setopt($this->ch, CURLOPT_COOKIE, $cookie);
-// return $this;
-// }
-//
-// /**
-// * @use 设置 url 参数
-// */
-// public function buildQuery(array $query): HttpClient
-// {
-// $this->query = http_build_query($query);
-// return $this;
-// }
-//
-// /**
-// * @use 自动将 json 文本解码
-// */
-// public function asJSON(): object
-// {
-// return json_decode($this->ret);
-// }
-//
-// /**
-// * @use 获取返回结果
-// */
-// public function asString(): string
-// {
-// return $this->ret;
-// }
-//
-// /**
-// * @use 构造POST表单
-// * @param array $form
-// * @return $this
-// */
-// public function buildPostForm(array $form): HttpClient
-// {
-// $this->form = http_build_query($form);
-//
-// return $this;
-// }
-//
-// /**
-// * @use 发送 Get 请求
-// */
-// public function get(): HttpClient
-// {
-// curl_setopt($this->ch, CURLOPT_URL, $this->url . "?" . $this->query);
-// curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); //返回内容储存到变量中
-// curl_setopt($this->ch, CURLOPT_HEADER, $this->headers);
-// $this->ret = curl_exec($this->ch);
-// return $this;
-// }
-//
-// /**
-// * @use 发送 POST 请求
-// * @param string|null $data 要 POST 的数据
-// * @return \HttpClient
-// */
-// public function post(string $data = null): HttpClient
-// {
-// curl_setopt($this->ch, CURLOPT_URL, $this->url . "?" . $this->query);
-// curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); //返回内容储存到变量中
-// curl_setopt($this->ch, CURLOPT_POST, true); // 发送 POST 请求
-// curl_setopt($this->ch, CURLOPT_HEADER, $this->headers);
-// curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
-// $this->ret = curl_exec($this->ch);
-// return $this;
-// }
-//
-//
-// /**
-// * @use POST表单
-// * @param array $form
-// * @return $this
-// */
-// public function postForm(array $form): HttpClient
-// {
-// return $this->post(http_build_query($form));
-// }
-}
-
-///**
-// * @use HttpClient
-// * @param string $url
-// * @return \BiliHelper\Core\HttpClient
-// */
-//function newHttp(string $url): HttpClient
-//{
-// return new HttpClient($url);
-//}
-
diff --git a/src/core/Log.php b/src/core/Log.php
deleted file mode 100644
index ed64fa4..0000000
--- a/src/core/Log.php
+++ /dev/null
@@ -1,180 +0,0 @@
-setFormatter(new ColoredLineFormatter());
- $logger->pushHandler($handler);
- self::$instance = $logger;
- }
-
- /**
- * @use 前缀
- * @return string
- */
- private static function prefix(): string
- {
- if (getConf('multiple', 'print')) {
- // return '[' . (getConf('user_identity', 'print') ?? getConf('username', 'login.account')) . ']';
- return sprintf("[%s]", getConf('user_identity', 'print') ?? getConf('username', 'login.account'));
- }
- return '';
- }
-
- /**
- * @use 写日志
- * @param $type
- * @param $message
- */
- private static function writeLog($type, $message)
- {
- if (getConf('enable', 'log')) {
- if ($type == 'DEBUG' && !getConf('enable', 'debug')) {
- return;
- }
-
- $filename = APP_LOG_PATH . getConf('username', 'login.account') . ".log";
- $date = date('[Y-m-d H:i:s] ');
- $data = $date . ' Log.' . $type . ' ' . $message . PHP_EOL;
- file_put_contents($filename, $data, FILE_APPEND);
- }
- }
-
- /**
- * @use 堆栈
- * @return string
- */
- private static function backtrace(): string
- {
- $backtraces = debug_backtrace();
- return "(" . pathinfo(basename($backtraces[1]['file']))['filename'] . ") => ";
- }
-
- /**
- * @use 调试
- * @param $message
- * @param array $context
- */
- public static function debug($message, array $context = [])
- {
- self::writeLog('DEBUG', $message);
- self::getLogger()->addDebug($message, $context);
- }
-
- /**
- * @use 信息
- * @param $message
- * @param array $context
- */
- public static function info($message, array $context = [])
- {
- $message = self::prefix() . self::backtrace() . $message;
- self::writeLog('INFO', $message);
- self::getLogger()->addInfo($message, $context);
- self::callback(Logger::INFO, 'INFO', $message);
- }
-
- /**
- * @use 提醒
- * @param $message
- * @param array $context
- */
- public static function notice($message, array $context = [])
- {
- $message = self::prefix() . self::backtrace() . $message;
- self::writeLog('NOTICE', $message);
- self::getLogger()->addNotice($message, $context);
- self::callback(Logger::NOTICE, 'NOTICE', $message);
- }
-
- /**
- * @use 警告
- * @param $message
- * @param array $context
- */
- public static function warning($message, array $context = [])
- {
- $message = self::prefix() . self::backtrace() . $message;
- self::writeLog('WARNING', $message);
- self::getLogger()->addWarning($message, $context);
- self::callback(Logger::WARNING, 'WARNING', $message);
- }
-
- /**
- * @use 错误
- * @param $message
- * @param array $context
- */
- public static function error($message, array $context = [])
- {
- $message = self::prefix() . self::backtrace() . $message;
- self::writeLog('ERROR', $message);
- self::getLogger()->addError($message, $context);
- self::callback(Logger::ERROR, 'ERROR', $message);
- }
-
- /**
- * @use 回调
- * @param $levelId
- * @param $level
- * @param $message
- */
- public static function callback($levelId, $level, $message)
- {
- // $callback_level = Logger::ERROR ?? getConf('callback_level', 'log');
- $callback_level = getConf('callback_level', 'log') ?? Logger::ERROR;
- if ($levelId >= $callback_level) {
- $url = str_replace('{account}', self::prefix(), getConf('callback', 'log'));
- $url = str_replace('{level}', $level, $url);
- $url = str_replace('{message}', urlencode($message), $url);
- Curl::request('get', str_replace(' ', '%20', $url));
- }
- }
-}
\ No newline at end of file
diff --git a/src/core/Task.php b/src/core/Task.php
deleted file mode 100644
index 7e728bf..0000000
--- a/src/core/Task.php
+++ /dev/null
@@ -1,114 +0,0 @@
-repository == '') {
- $this->repository = APP_TASK_PATH . 'tasks_' . getConf('username', 'login.account') . '.json';
- // 仓库不存在自动创建
- if (!file_exists($this->repository)) {
- $fh = fopen($this->repository, "w");
- fwrite($fh, "{}");
- fclose($fh);
- Log::info('任务排程文件不存在,初始化所有任务。');
- } else {
- Log::info('任务排程文件存在,继续执行所有任务。');
- }
- }
- }
-
- /**
- * @use 读
- * @return array
- */
- private function read(): array
- {
- $data = file_get_contents($this->repository);
- return json_decode($data, true);
- }
-
- /**
- * @use 写
- * @param array $data
- * @return bool
- */
- private function write(array $data): bool
- {
- return file_put_contents($this->repository, json_encode($data));
- }
-
- /**
- * @use 写入
- * @param string $class
- * @param int $lock
- * @return bool
- */
- public function _setLock(string $class, int $lock): bool
- {
- $data = $this->read();
- $data[$class] = $lock;
- return $this->write($data);
- }
-
- /**
- * @use 读取
- * @param string $class
- * @return int
- */
- public function _getLock(string $class): int
- {
- $data = $this->read();
- if (array_key_exists($class, $data)) {
- return $data[$class];
- }
- return 0;
- }
-
- /**
- * @use 复位
- */
- public function restore()
- {
- Log::info('复位任务排程文件。');
- File::del($this->repository);
- $this->repository = '';
- $this->init();
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/ActivityLottery.php b/src/plugin/ActivityLottery.php
deleted file mode 100644
index ce05b5b..0000000
--- a/src/plugin/ActivityLottery.php
+++ /dev/null
@@ -1,246 +0,0 @@
- time() || !getEnable('main_activity')) {
- return;
- }
- self::allotTasks();
- if (self::workTask()) {
- self::setLock(5 * 60);
- } else {
- self::setLock(self::timing(5) + mt_rand(1, 180));
- }
- }
-
- /**
- * @use 分配任务
- * @return bool
- */
- private static function allotTasks(): bool
- {
- if (self::$work_status['work_updated'] == date("Y/m/d")) {
- return false;
- }
- $parser = self::loadJsonData();
- foreach ($parser->get('data', []) as $act) {
- // todo 优化处理
- $act = json_decode(json_encode($act));
- // 活动无效
- if (is_null($act->sid)) {
- continue;
- }
- // 活动实效过期
- if (strtotime($act->expire_at) < time()) {
- continue;
- }
- // init
- if ($act->login == 'true') {
- self::pushTask('login', $act);
- }
- // follow
- if ($act->follow == 'true') {
- self::pushTask('follow', $act);
- }
- // share
- if ($act->share == 'true') {
- self::pushTask('share', $act);
- }
- // draw_times
- $arr = range(1, $act->draw_times);
- foreach ($arr as $ignored) {
- self::pushTask('draw', $act);
- }
- }
- self::$work_status['work_updated'] = date("Y/m/d");
- Log::info('活动抽奖任务分配完成 ' . count(self::$tasks) . ' 个任务待执行');
- return true;
- }
-
- /**
- * @use 执行任务
- * @return bool
- */
- private static function workTask(): bool
- {
- if (self::$work_status['work_completed'] == date("Y/m/d")) {
- return false;
- }
- $task = self::pullTask();
- // 所有任务完成 标记
- if (!$task) {
- self::$work_status['work_completed'] = date("Y/m/d");
- return false;
- }
- Log::info("执行 {$task['act']->title} #{$task['operation']} 任务");
- // 执行任务
- switch ($task['operation']) {
- case 'login':
- self::initTimes($task['act']->sid, $task['act']->url);
- break;
- case 'follow':
- self::addTimes($task['act']->sid, $task['act']->url, 4);
- break;
- case 'share':
- self::addTimes($task['act']->sid, $task['act']->url, 3);
- break;
- case 'draw':
- // 有抽奖机会才抽奖
- if (self::initTimes($task['act']->sid, $task['act']->url, false)) {
- self::doLottery($task['act']->sid, $task['act']->url, 0);
- }
- break;
- default:
- Log::info("当前 {$task['act']->title} #{$task['operation']} 任务不存在哦");
- break;
- }
- return true;
- }
-
- /**
- * @use 获取抽奖机会
- * @param string $sid
- * @param string $referer
- * @param bool $init
- * @return bool
- */
- private static function initTimes(string $sid, string $referer, bool $init = true): bool
- {
- $url = 'https://api.bilibili.com/x/activity/lottery/mytimes';
- $headers = [
- 'origin' => 'https://www.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'sid' => $sid,
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"times":2}}
- // {"code":0,"message":"0","ttl":1,"data":{"times":3}}
- if ($init) {
- if ($de_raw['code'] == 0) {
- Log::notice("剩余抽奖次数 {$de_raw['data']['times']}");
- return true;
- }
- Log::warning("获取抽奖次数失败 $raw");
- return false;
- }
- if ($de_raw['code'] == 0) {
- Log::notice("剩余抽奖次数 {$de_raw['data']['times']}");
- if ($de_raw['data']['times'] <= 0) {
- return false;
- }
- return true;
- }
- Log::warning("获取抽奖次数失败 $raw");
- return false;
-
- }
-
- /**
- * @use 增加抽奖机会
- * @param string $sid
- * @param string $referer
- * @param int $action_type
- * @return bool
- */
- private static function addTimes(string $sid, string $referer, int $action_type): bool
- {
- $url = 'https://api.bilibili.com/x/activity/lottery/addtimes';
- $headers = [
- 'origin' => 'https://www.bilibili.com',
- 'referer' => $referer
- ];
- // $action_type 4 关注 3 分享
- $payload = [
- 'sid' => $sid,
- 'action_type' => $action_type,
- 'csrf' => getCsrf()
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":75405,"message":"抽奖机会用尽啦","ttl":1}
- // {"code":75003,"message":"活动已结束","ttl":1}
- // {"code":0,"message":"0","ttl":1}
- $de_raw = json_decode($raw, true);
- Log::notice("增加抽奖机会#$action_type $raw");
-
- if ($de_raw['code'] == 0) {
- return true;
- }
- return false;
- }
-
- /**
- * @use 开始抽奖
- * @param string $sid
- * @param string $referer
- * @param int $num
- * @return bool
- */
- private static function doLottery(string $sid, string $referer, int $num): bool
- {
- $url = 'https://api.bilibili.com/x/activity/lottery/do';
- $headers = [
- 'origin' => 'https://www.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'sid' => $sid,
- 'type' => 1,
- 'csrf' => getCsrf()
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- Log::notice("开始抽奖#$num $raw");
- // {"code":0,"message":"0","ttl":1,"data":[{"id":0,"mid":4133274,"num":1,"gift_id":1152,"gift_name":"硬币x6","gift_type":0,"img_url":"https://i0.hdslb.com/bfs/activity-plat/static/b6e956937ee4aefd1e19c01283145fc0/JQ9Y9-KCm_w96_h102.png","type":5,"ctime":1596255796,"cid":0}]}
- // {"code":0,"message":"0","ttl":1,"data":[{"id":0,"mid":4133274,"ip":0,"num":1,"gift_id":0,"gift_name":"未中奖0","gift_type":0,"img_url":"","type":1,"ctime":1616825625,"cid":0,"extra":{}}]}
- if ($de_raw['code'] == 0) {
- $result = "活动->$referer 获得->{$de_raw['data'][0]['gift_name']}";
- Notice::push('activity_lottery', $result);
- return true;
- }
- return false;
- }
-
-
-}
\ No newline at end of file
diff --git a/src/plugin/AloneTcpClient.php b/src/plugin/AloneTcpClient.php
deleted file mode 100644
index 022e6c4..0000000
--- a/src/plugin/AloneTcpClient.php
+++ /dev/null
@@ -1,298 +0,0 @@
- time() || !getEnable('alone_monitor')) {
- return;
- }
- self::setPauseStatus();
- self::init();
- self::heartBeat();
- self::receive();
- }
-
- /**
- * @use 初始化
- */
- private static function init()
- {
- if (empty(getConf('server_addr', 'alone_monitor')) || empty(getConf('server_key', 'alone_monitor'))) {
- exit('推送服务器信息不完整, 请检查配置文件!');
- }
- if (!self::$server_addr || !self::$server_key) {
- self::$server_addr = getConf('server_addr', 'alone_monitor');
- self::$server_key = getConf('server_key', 'alone_monitor');
- }
- if (!self::$client) {
- self::openConnect();
- }
- }
-
- /**
- * @use 数据封装
- * @param $value
- * @param string $fmt
- * @return string
- */
- private static function packMsg($value, string $fmt = "N"): string
- {
- $head = pack($fmt, strlen($value));
- return $head . $value;
- }
-
- /**
- * @use 数据解包
- * @param $value
- * @param string $fmt
- * @return mixed
- * @throws \Exception
- */
- private static function unPackMsg($value, string $fmt = "N"): mixed
- {
- return unpack($fmt, $value)[1];
- }
-
- /**
- * @use 连接认证
- */
- private static function handShake()
- {
- self::writer(
- json_encode([
- 'code' => 0,
- 'type' => 'ask',
- 'data' => [
- 'key' => self::$server_key,
- ]
- ])
- );
- }
-
- /**
- * @use 心跳
- */
- private static function heartBeat()
- {
- if (self::$heart_lock <= time()) {
- if (self::writer("")) {
- // 心跳默认35s 调整数据错开错误
- self::$heart_lock = time() + 25;
- }
- }
- }
-
- /**
- * @use 读数据
- * @param $length
- * @return mixed
- */
- private static function reader($length): mixed
- {
- $data = false;
- try {
- while (self::$client->selectRead(self::$socket_timeout)) {
- $data = self::$client->read($length);
- if (!$data || strlen($data) > 65535 || strlen($data) < 0) {
- throw new Exception("Connection failure");
- }
- if ($length == 4) $data = self::unPackMsg($data);
- break;
- }
- } catch (Exception $exception) {
- self::reConnect();
- }
- return $data;
- }
-
- /**
- * @use 写数据
- * @param $data
- * @return bool
- */
- private static function writer($data): bool
- {
- $status = false;
- try {
- while (self::$client->selectWrite(self::$socket_timeout)) {
- $data = self::packMsg($data);
- $status = self::$client->write($data);
- break;
- }
- } catch (Exception $exception) {
- self::reConnect();
- }
- return $status;
- }
-
- /**
- * @use 打开连接
- */
- private static function openConnect()
- {
- if (!self::$client) {
- try {
- $socket = (new Factory())->createClient(self::$server_addr, 40);
- $socket->setBlocking(false);
- self::$client = $socket;
- self::handShake();
- Log::info("连接到 {$socket->getPeerName()} 推送服务器");
- } catch (Exception $e) {
- Log::error("连接到推送服务器失败, {$e->getMessage()}");
- self::setLock(60);
- }
- }
- }
-
- /**
- * @use 重新连接
- */
- private static function reConnect()
- {
- Log::info('重新连接到推送服务器');
- self::closeConnect();
- self::openConnect();
- }
-
- /**
- * @use 断开连接
- */
- private static function closeConnect()
- {
- Log::info('断开推送服务器');
- try {
- self::$client->shutdown();
- self::$client->close();
- } catch (Exception $exception) {
- // var_dump($exception);
- }
- self::$client = null;
- }
-
- /**
- * @use 读取数据
- */
- private static function receive()
- {
- $len_body = self::reader(4);
- if (!$len_body) {
- // 长度为0 ,空信息
- return;
- }
- Log::debug("(len=$len_body)");
- $body = self::reader($len_body);
- $raw_data = json_decode($body, true);
- // 数据可能出现不全 DECODE返回NULL 索引错误 交给default处理
- $data_type = is_null($raw_data) ? "unknown" : $raw_data['type'];
- switch ($data_type) {
- case 'raffle':
- // 抽奖推送
- Log::debug("(receive=$body)");
- DataTreating::distribute($raw_data['data']);
- break;
- case 'entered':
- // 握手确认
- Log::info("确认到推送服务器 {$raw_data['type']}");
- self::$max_errors_num = 0;
- break;
- case 'error':
- // 产生错误
- Log::error("推送服务器异常 {$raw_data['data']['msg']}, 程序错误5次后将挂起, 请手动关闭!");
- if (self::$max_errors_num == 5) {
- // KEY到期推送提醒
- Notice::push('key_expired');
- // 程序挂起 防止systemd无限重启导致触发过多推送提醒
- sleep(86400);
- exit();
- }
- self::$max_errors_num += 1;
- break;
- case 'heartbeat':
- // 服务端心跳推送
- // Log::info("推送服务器心跳推送 {$body}");
- Log::debug("(heartbeat={$raw_data['data']['now']})");
- break;
- case 'sleep':
- // 服务器发布命令
- Log::warning("服务器发布休眠命令 {$raw_data['data']['msg']}");
- sleep($raw_data['data']['hour']);
- break;
- case 'update':
- // 服务器发布命令
- Log::notice("服务器发布更新命令 {$raw_data['data']['msg']}");
- Notice::push('update', $raw_data['data']['msg']);
- break;
- case 'exit':
- // 服务器发布命令
- Log::error("服务器发布退出命令 {$raw_data['data']['msg']}");
- exit();
- default:
- // 未知信息
- var_dump($raw_data);
- Log::info("出现未知信息 $body");
- // 出现未知信息 处理重连 防止内存益处
- self::reConnect();
- break;
- }
- }
-
- /**
- * @use 写入log
- * @param $message
- */
- private static function writeLog($message)
- {
- $path = './danmu/';
- if (!file_exists($path)) {
- mkdir($path);
- chmod($path, 0777);
- }
- $filename = $path . getConf('username', 'login.account') . ".log";
- $date = date('[Y-m-d H:i:s] ');
- $data = "[$date]$message" . PHP_EOL;
- file_put_contents($filename, $data, FILE_APPEND);
- }
-}
\ No newline at end of file
diff --git a/src/plugin/AnchorRaffle.php b/src/plugin/AnchorRaffle.php
deleted file mode 100644
index 8f0830e..0000000
--- a/src/plugin/AnchorRaffle.php
+++ /dev/null
@@ -1,259 +0,0 @@
- $need_follow_uid,
- 'anchor_id' => $anchor_id,
- 'time' => $time,
- ];
- }
- }
-
- /**
- * @use 删除值并重置数组
- * @param int $uid
- * @param int $anchor_id
- */
- private static function delValue(int $uid, int $anchor_id)
- {
- $new_list = [];
- foreach (self::$wait_un_follows as $wait_un_follow) {
- if ($wait_un_follow['uid'] == $uid && $wait_un_follow['uid'] == $anchor_id) {
- continue;
- }
- $new_list[] = $wait_un_follow;
- }
- self::$wait_un_follows = $new_list;
- }
-
- /**
- * @use 获取默认关注
- * @return array
- */
- private static function getDefaultFollows(): array
- {
- if (!empty(self::$default_follows)) {
- return self::$default_follows;
- }
- // 如果获取默认关注错误 或者 为空则补全一个
- self::$default_follows = User::fetchTagFollowings();
- if (empty(self::$default_follows)) {
- self::$default_follows[] = 1;
- }
- return self::$default_follows;
- }
-
- /**
- * @use 过滤奖品
- * @param string $prize_name
- * @return bool
- */
- protected static function filterPrizeWords(string $prize_name): bool
- {
- $default_words = self::$store->get("Anchor.default");
- $custom_words = empty($words = getConf('filter_words', 'live_anchor')) ? [] : explode(',', $words);
- $total_words = array_merge($default_words, $custom_words);
- foreach ($total_words as $word) {
- if (str_contains($prize_name, $word)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @use 解析数据
- * @param int $room_id
- * @param array $data
- * @return bool
- */
- protected static function parseLotteryInfo(int $room_id, array $data): bool
- {
- // 防止异常
- if (!array_key_exists('anchor', $data['data'])) {
- return false;
- }
- $de_raw = $data['data']['anchor'];
- if (empty($de_raw)) {
- return false;
- }
- // 无效抽奖
- if ($de_raw['join_type'] || $de_raw['lot_status']) {
- return false;
- }
- // 过滤抽奖范围
- self::$filter_type = empty(self::$filter_type) ? explode(',', getConf('limit_type', 'live_anchor')) : self::$filter_type;
- if (!in_array((string)$de_raw['require_type'], self::$filter_type)) {
- return false;
- }
- // 过滤奖品关键词
- if (self::filterPrizeWords($de_raw['award_name'])) {
- return false;
- }
- // 去重
- if (self::toRepeatLid($de_raw['id'])) {
- return false;
- }
- // 分组操作
- if (getConf('auto_unfollow', 'live_anchor') && $de_raw['require_text'] == '关注主播') {
- self::addToGroup($room_id, $de_raw['id'], time() + $de_raw['time'] + 5);
- }
- // 推入列表
- $data = [
- 'room_id' => $room_id,
- 'raffle_id' => $de_raw['id'],
- 'raffle_name' => $de_raw['award_name'],
- 'remarks' => self::ACTIVE_TITLE,
- 'wait' => time() + mt_rand(5, 25)
- ];
-// Statistics::addPushList($data['raffle_name']);
- Statistics::addPushList(self::ACTIVE_TITLE);
- self::$wait_list[] = $data;
- return true;
- }
-
- /**
- * @use 创建抽奖任务
- * @param array $raffles
- * @return array
- */
- protected static function createLottery(array $raffles): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/Anchor/Join';
- $tasks = [];
- foreach ($raffles as $raffle) {
- $payload = [
- 'id' => $raffle['raffle_id'],
- 'roomid' => $raffle['room_id'],
- 'platform' => 'pc',
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- $tasks[] = [
- 'payload' => Sign::common($payload),
- 'source' => [
- 'room_id' => $raffle['room_id'],
- 'raffle_id' => $raffle['raffle_id'],
- 'raffle_name' => $raffle['raffle_name']
- ]
- ];
- }
- // print_r($results);
- return Curl::async('app', $url, $tasks);
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- foreach ($results as $result) {
- $data = $result['source'];
- $content = $result['content'];
- $de_raw = json_decode($content, true);
- // {"code":-403,"data":null,"message":"访问被拒绝","msg":"访问被拒绝"}
- if (isset($de_raw['code']) && $de_raw['code'] == 0) {
- Statistics::addSuccessList(self::ACTIVE_TITLE);
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . "-(" . $data['raffle_name'] . "): 参与抽奖成功~");
- } elseif (isset($de_raw['msg']) && $de_raw['code'] == -403 && $de_raw['msg'] == '访问被拒绝') {
- Log::debug("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . "-(" . $data['raffle_name'] . "): {$de_raw['message']}");
- self::pauseLock();
- } else {
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . "-(" . $data['raffle_name'] . "): {$de_raw['message']}");
- }
- }
- return '';
- }
-}
diff --git a/src/plugin/AwardRecord.php b/src/plugin/AwardRecord.php
deleted file mode 100644
index 9802c1e..0000000
--- a/src/plugin/AwardRecord.php
+++ /dev/null
@@ -1,161 +0,0 @@
- time() || !getEnable('award_record')) {
- return;
- }
- if (self::$anchor_lock < time()) {
- self::anchorAward();
- }
- if (self::$raffle_lock < time()) {
- self::raffleAward();
- }
- // if (self::$gift_lock < time()) {
- // self::giftAward();
- // }
- self::setLock(5 * 60);
- }
-
- /**
- * @use 获取天选时刻中奖纪录
- */
- private static function anchorAward()
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/Anchor/AwardRecord';
- $payload = [
- 'page' => '1',
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- // 防止异常
- if (!isset($de_raw['data']) || !isset($de_raw['data']['list'])) {
- Log::warning("获取天选时刻获奖记录错误: " . json_encode($de_raw, JSON_FORCE_OBJECT));
- self::$anchor_lock = time() + 60 * 60;
- return;
- }
- foreach ($de_raw['data']['list'] as $anchor) {
- $win_time = strtotime($anchor['end_time']); //礼物时间
- $day = ceil((time() - $win_time) / 86400); //60s*60min*24h
- // 去重
- if (in_array($anchor['id'], self::$anchor_list)) {
- continue;
- }
- // 范围
- if ($day <= 2) {
- $info = $anchor['award_name'] . 'x' . $anchor['award_num'];
- Log::notice("天选时刻于" . $anchor['end_time'] . "获奖: $info ,请留意查看...");
- Notice::push('anchor', $info);
- }
- self::$anchor_list[] = $anchor['id'];
- }
- // 处理取关操作
- foreach (AnchorRaffle::$wait_un_follows as $wait_un_follow) {
- if ($wait_un_follow['time'] > time()) {
- continue;
- }
- if (in_array($wait_un_follow['anchor_id'], self::$anchor_list)) {
- AnchorRaffle::delToGroup($wait_un_follow['uid'], $wait_un_follow['anchor_id'], false);
- } else {
- AnchorRaffle::delToGroup($wait_un_follow['uid'], $wait_un_follow['anchor_id']);
- }
- }
-
- self::$anchor_lock = time() + 10 * 60;
- }
-
- /**
- * @use 获取实物抽奖中奖纪录
- */
- private static function raffleAward()
- {
- $url = 'https://api.live.bilibili.com/lottery/v1/award/award_list';
- $payload = [
- 'page' => '1',
- 'month' => '',
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
-
- // 防止异常
- if (!isset($de_raw['data']) || !isset($de_raw['data']['list']) || $de_raw['code']) {
- Log::warning("获取实物奖励获奖记录错误: " . $de_raw['msg']);
- self::$raffle_lock = time() + 60 * 60;
- return;
- }
- foreach ($de_raw['data']['list'] as $raffle) {
- $win_time = strtotime($raffle['create_time']); //礼物时间
- $day = ceil((time() - $win_time) / 86400); //60s*60min*24h
- // 去重
- if (in_array($raffle['id'], self::$raffle_list)) {
- continue;
- }
- // 范围
- if ($day <= 2 && empty($raffle['update_time'])) {
- $info = $raffle['gift_name'] . 'x' . $raffle['gift_num'];
- Log::notice("实物奖励于" . $raffle['create_time'] . "获奖: $info ,请留意查看...");
- Notice::push('raffle', $info);
- }
- self::$raffle_list[] = $raffle['id'];
- }
- self::$raffle_lock = time() + 6 * 60 * 60;
- }
-
- /**
- * @use 获取活动礼物中奖纪录
- */
- private static function giftAward()
- {
- // Web V3 Notice
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v3/smalltv/Notice';
- $payload = [
- 'type' => 'type',
- 'raffleId' => 'raffle_id'
- ];
- // 请求 && 解码
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- }
-}
\ No newline at end of file
diff --git a/src/plugin/Barrage.php b/src/plugin/Barrage.php
deleted file mode 100644
index c2c6552..0000000
--- a/src/plugin/Barrage.php
+++ /dev/null
@@ -1,117 +0,0 @@
- time() || !getEnable('barrage')) {
- return;
- }
- self::setPauseStatus();
- if (self::sendMsg()) {
- self::setLock(mt_rand(240, 300) * 60);
- return;
- }
- self::setLock(15 * 60);
- }
-
- /**
- * 获取一言api消息
- * @param bool $sep
- * @return string
- */
- private static function getMsgInfo(bool $sep = true): string
- {
- /**
- * 整理一部分API,收集于网络,侵权麻烦联系我删除.
- * 如果设置项不能用可以选择,只保证代码发布时正常.
- * 格式全部为TEXT,可以自己替换.
- */
- $punctuations = $sep ? [',', ',', '。', '!', '.', ';', '——'] : [];
- $apis = [
- 'https://v1.hitokoto.cn/?encode=text',
- 'https://api.ly522.com/Api/YiYan?format=text',
- 'https://api.jysafe.cn/yy/',
- 'https://api.imjad.cn/hitokoto/',
- 'https://www.ly522.com/hitokoto/',
- 'https://api.guoch.xyz/',
- 'https://api.gushi.ci/rensheng.txt',
- 'https://api.itswincer.com/hitokoto/v2/',
- // 'http://www.ooomg.cn/dutang/',
- // 'http://api.dsecret.com/yiyan/',
- ];
- shuffle($apis);
- try {
- foreach ($apis as $url) {
- $data = Curl::request('get', $url);
- if (is_null($data)) continue;
- foreach ($punctuations as $punctuation) {
- if (strpos($data, $punctuation)) {
- $data = explode($punctuation, $data)[0];
- break;
- }
- }
- return $data;
- }
- } catch (Exception $e) {
- return $e->getMessage();
- }
- }
-
-
- /**
- * @use 活跃弹幕
- * @return bool
- */
- private static function sendMsg(): bool
- {
- $room_id = empty($room_id = getConf('room_id', 'barrage')) ? Live::getUserRecommend() : Live::getRealRoomID($room_id);
- $content = empty($msg = getConf('content', 'barrage')) ? self::getMsgInfo() : $msg;
-
- $response = Live::sendBarragePC($room_id, $content);
- // {"code":0,"data":[],"message":"","msg":""}
- // {"code":0,"message":"你被禁言啦","msg":"你被禁言啦"}
- // Todo 长度限制
- if (isset($response['code']) && $response['code'] == 0 && isset($response['data'])) {
- Log::notice("在直播间@$room_id 发送活跃弹幕成功 CODE -> {$response['code']}");
- return true;
- } else {
- Log::warning("在直播间@$room_id 发送活跃弹幕失败 CODE -> {$response['code']} MSG -> {$response['message']} ");
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/src/plugin/BpConsumption.php b/src/plugin/BpConsumption.php
deleted file mode 100644
index 801b469..0000000
--- a/src/plugin/BpConsumption.php
+++ /dev/null
@@ -1,165 +0,0 @@
- time() || !getEnable('bp_consumption')) {
- return;
- }
- // 定时14点 + 随机120分钟| 根据逻辑前置
- self::setLock(self::timing(14) + mt_rand(1, 120) * 60);
-
- // 如果为年度大会员
- if (User::isYearVip()) {
- // 获取B币余额
- $bp_balance = self::getUserWallet();
- // 最大支持5
- if ($bp_balance != 5) return;
- // 消费B币充电
- if (getConf('bp2charge', 'bp_consumption')) {
- // UID为空就切换成自己的
- $uid = empty($uid = getConf('bp2charge_uid', 'bp_consumption')) ? getUid() : $uid;
- self::BP2charge($uid, $bp_balance);
- return;
- }
- // 消费B币充值金瓜子
- if (getConf('bp2gold', 'bp_consumption')) {
- self::BP2gold($bp_balance);
- }
- }
- }
-
- /**
- * @use 获取钱包B币券余额
- * @return int
- */
- private static function getUserWallet(): int
- {
- $url = 'https://pay.bilibili.com/paywallet/wallet/getUserWallet';
- $headers = [
- 'Content-Type' => 'application/json;charset=utf-8',
- 'origin' => 'https://pay.bilibili.com',
- 'referer' => 'https://pay.bilibili.com/paywallet-fe/bb_balance.html'
- ];
- $ts = Common::getUnixTimestamp();
- $payload = [
- 'panelType' => 3,
- 'platformType' => 3,
- 'timestamp' => $ts,
- 'traceId' => $ts,
- 'version' => "1.0",
- ];
- $raw = Curl::put('pc', $url, $payload, $headers);
- // {"errno":0,"msg":"SUCCESS","showMsg":"","errtag":0,"data":{"mid":1234,"totalBp":5.00,"defaultBp":0.00,"iosBp":0.00,"couponBalance":5.00,"availableBp":5.00,"unavailableBp":0.00,"unavailableReason":"苹果设备上充值的B币不能在其他平台的设备上进行使用","tip":null}}
- $de_raw = json_decode($raw, true);
- if ($de_raw['errno'] == 0 && isset($de_raw['data']['couponBalance'])) {
- Log::notice('获取钱包成功 B币券余额剩余' . $de_raw['data']['couponBalance']);
- return intval($de_raw['data']['couponBalance']);
- } else {
- Log::warning("获取钱包失败 $raw");
- return 0;
- }
- }
-
- /**
- * @use B币充电
- * @param int $uid
- * @param int $num
- */
- private static function BP2charge(int $uid, int $num = 5)
- {
- $url = 'https://api.bilibili.com/x/ugcpay/web/v2/trade/elec/pay/quick';
- $payload = [
- 'bp_num' => $num, // 数量
- 'is_bp_remains_prior' => true, // 是否优先扣除B币余额
- 'up_mid' => $uid, // 目标UID
- 'otype' => 'up', // 来源 up:空间充电 archive:视频充电
- 'oid' => $uid, // 目标UID or 稿件avid
- 'csrf' => getCsrf()
- ];
- $raw = Curl::post('pc', $url, $payload);
- // {"code":0,"message":"0","ttl":1,"data":{"mid":12324,"up_mid":1234,"order_no":"PAY4567","bp_num":"5","exp":5,"status":4,"msg":""}}
- // {"code":0,"message":"0","ttl":1,"data":{"mid":12324,"up_mid":1234,"order_no":"ABCD","bp_num":2,"exp":2,"status":4,"msg":""}}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- // data.status 4 成功 -2:低于20电池下限 -4:B币不足
- if ($de_raw['data']['status'] == 4) {
- Log::notice("给{$uid}B币充电成功 NUM -> {$de_raw['data']['bp_num']} EXP -> {$de_raw['data']['exp']} ORDER -> {$de_raw['data']['order_no']}");
- } else {
- Log::warning("给{$uid}B币充电失败 STATUS -> {$de_raw['data']['status']} MSG -> {$de_raw['data']['msg']}");
- }
- } else {
- Log::warning("给{$uid}B币充电失败 CODE -> {$de_raw['code']} MSG -> {$de_raw['message']} ");
- }
- }
-
- /**
- * B币充值金瓜子
- * @param int $num
- */
- private static function BP2gold(int $num)
- {
- $url = 'https://api.live.bilibili.com/xlive/revenue/v1/order/createOrder';
- $headers = [
- 'origin' => 'https://link.bilibili.com',
- 'referer' => 'https://link.bilibili.com/p/center/index'
- ];
- $payload = [
- 'platform' => 'pc',
- 'pay_bp' => $num * 1000, // 瓜子数量
- 'context_id' => 1, // 直播间
- 'context_type' => 11,
- 'goods_id' => 1, // 商品ID
- 'goods_num' => $num, // B币数量
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => '',
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":1300014,"message":"b币余额不足","ttl":1,"data":null}
- // {"code":0,"message":"0","ttl":1,"data":{"status":2,"order_id":"1234171134577071132741234","gold":0,"bp":5000}}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice("B币充值金瓜子成功 NUM -> {$de_raw['data']['bp']} ORDER -> {$de_raw['data']['order_id']}");
- } else {
- Log::warning("B币充值金瓜子失败 CODE -> {$de_raw['code']} MSG -> {$de_raw['message']}");
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/CapsuleLottery.php b/src/plugin/CapsuleLottery.php
deleted file mode 100644
index 9e3c99b..0000000
--- a/src/plugin/CapsuleLottery.php
+++ /dev/null
@@ -1,315 +0,0 @@
- time() || !getEnable('live_capsule')) {
- return;
- }
- self::allotTasks();
- if (self::workTask()) {
- self::setLock(self::$interval);
- } else {
- self::setLock(self::timing(5) + mt_rand(1, 180));
- }
- }
-
- /**
- * @use 分配任务
- * @return bool
- */
- private static function allotTasks(): bool
- {
- if (self::$work_status['work_updated'] == date("Y/m/d")) {
- return false;
- }
- $parser = self::loadJsonData();
- // Converting to array
- foreach ($parser->get('data', []) as $act) {
- // todo 优化处理
- $act = json_decode(json_encode($act));
- // 活动无效
- if (is_null($act->coin_id)) {
- continue;
- }
- // 活动实效过期
- if (strtotime($act->expire_at) < time()) {
- continue;
- }
- if ($act->room_id == 0) {
- $room_ids = Live::getAreaRoomList($act->parent_area_id, $act->area_id);
- $act->room_id = array_shift($room_ids);
- }
- // 观看时间
- self::pushTask('watch', $act, true);
- // 抽奖次数
- $arr = range(1, $act->draw_times);
- foreach ($arr as $ignored) {
- self::pushTask('draw', $act);
- }
- }
- self::$work_status['work_updated'] = date("Y/m/d");
- Log::info('扭蛋抽奖任务分配完成 ' . count(self::$tasks) . ' 个任务待执行');
- return true;
- }
-
- /**
- * @use 执行任务
- * @return bool
- */
- private static function workTask(): bool
- {
- if (self::$work_status['work_completed'] == date("Y/m/d")) {
- return false;
- }
- $task = self::pullTask();
- // 所有任务完成 标记
- if (!$task) {
- self::$work_status['work_completed'] = date("Y/m/d");
- return false;
- }
- if ($task['time'] && is_null(self::$work_status['estimated_time'])) {
- self::$work_status['estimated_time'] = time() + $task['act']->watch_time;
- }
- Log::info("执行 {$task['act']->title} #{$task['operation']} 任务");
- // 执行任务
- switch ($task['operation']) {
- // Todo 观看 分享 签到任务
- case 'watch':
- // 处理值为空
- if (!is_null($task['act']->room_id)) {
- $interval = self::xliveHeartBeatTask($task['act']->room_id, 999, 999);
- self::$interval = ($interval == 0 ? 60 : $interval);
- }
- break;
- case 'draw':
- // 抽奖次数 > 0 开始抽奖
- if (self::getLuckyNum($task['act']->coin_id, $task['act']->url)) {
- self::doLottery($task['act']->coin_id, $task['act']->url, 0);
- }
- break;
- default:
- Log::info("当前 {$task['act']->title} #{$task['operation']} 任务不存在哦");
- break;
- }
- return true;
- }
-
- /**
- * @use 开始抽奖
- * @param int $coin_id
- * @param string $referer
- * @param int $num
- * @return bool
- */
- private static function doLottery(int $coin_id, string $referer, int $num): bool
- {
- $url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/open_capsule_by_id';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'id' => $coin_id,
- 'count' => 1,
- 'type' => 1,
- 'platform' => 'web',
- '_' => time() * 1000,
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf(),
- 'visit_id' => ''
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- Log::notice("开始抽奖#$num $raw");
- // {"code":0,"message":"0","ttl":1,"data":{"status":false,"isEntity":false,"info":{"coin":1},"awards":[{"name":"谢谢参与","num":1,"text":"谢谢参与 X 1","web_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","mobile_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","usage":{"text":"很遗憾您未能中奖","url":""},"type":32,"expire":"当天","gift_type":"7290bc172e5ab9e151eb141749adb9dd","gift_value":""}],"text":["谢谢参与 X 1"],"isExCode":false}}
- if ($de_raw['code'] == 0) {
- $result = "活动->$referer 获得->{$de_raw['data']['text'][0]}";
- Notice::push('capsule_lottery', $result);
- return true;
- }
- return false;
-
- }
-
- /**
- * @use 分享任务
- * @param int $act_id
- * @param string $referer
- */
- private static function taskShare(int $act_id, string $referer)
- {
- $url = 'https://api.live.bilibili.com/xlive/activity-interface/v1/task/UserShare';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => 'https://live.bilibili.com/'
- ];
- $payload = [
- 'act_id' => $act_id,
- 'share_type' => 1,
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => '',
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1,"data":{"status":1}}
- $de_raw = json_decode($raw, true);
- }
-
- /**
- * @use 获取扭蛋池子信息
- * @param int $pool_id
- * @param string $referer
- */
- private static function getPoolDetail(int $pool_id, string $referer)
- {
- $url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/get_pool_detail';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'pool_id' => $pool_id,
- '_' => Common::getUnixTimestamp()
- ];
- $raw = Curl::get('pc', $url, $payload);
- // {"code":0,"message":"0","ttl":1,"data":{"id":145,"coin":3,"coin_id":131,"title":"OWL2021","open_num_1":1,"open_num_2":10,"open_num_3":100,"status":0,"gift_list":[{"id":1237,"name":"谢谢参与","num":1,"web_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","mobile_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","usage":{"text":"谢谢参与","url":""},"type":32,"expire":"当天","gift_type":"7290bc172e5ab9e151eb141749adb9dd"},{"id":1238,"name":"杭州闪电队主场队服","num":1,"web_url":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_url":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","usage":{"text":"新款杭州闪电队队服","url":""},"type":100024,"expire":"当天","gift_type":"895ff9e63081033072a1bc5694d10095"},{"id":1239,"name":"上海龙之队主场队服","num":1,"web_url":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_url":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","usage":{"text":"新款上海龙之队队服","url":""},"type":100024,"expire":"当天","gift_type":"a314efd84276f0be571492823b1c0edb"},{"id":1240,"name":"广州冲锋队主场队服","num":1,"web_url":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","mobile_url":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","usage":{"text":"新款广州冲锋队队服","url":""},"type":100024,"expire":"当天","gift_type":"6d46360bc93b8b07b4c64efb9350579a"},{"id":1241,"name":"谢谢参与","num":1,"web_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","mobile_url":"https://i0.hdslb.com/bfs/live/b0fccfb3bac2daae35d7e514a8f6d31530b9add2.png","usage":{"text":"谢谢参与","url":""},"type":32,"expire":"当天","gift_type":"7290bc172e5ab9e151eb141749adb9dd"},{"id":1242,"name":"成都猎人队主场队服","num":1,"web_url":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","mobile_url":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","usage":{"text":"新款成都猎人队队服","url":""},"type":100024,"expire":"当天","gift_type":"51ecabc603848979410202a93391cd35"},{"id":1243,"name":"随机战队客场队服","num":1,"web_url":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_url":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","usage":{"text":"随机一件OWL战队客场队服","url":""},"type":100024,"expire":"当天","gift_type":"b2ec92156e57708caf923b1c18309fea"},{"id":1244,"name":"哔哩哔哩月度大会员","num":1,"web_url":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","mobile_url":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","usage":{"text":"bilibili月度大会员","url":""},"type":100024,"expire":"当天","gift_type":"78db25f308b8b8db86ba1a172c2deb35"}],"is_login":true,"list":[{"num":1,"gift":"哔哩哔哩月度大会员","date":"2021-05-10","name":"初夏的戏匣子","web_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","mobile_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","count":1},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-10","name":"hentaiDIAO","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-05-10","name":"哗哗はな","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-10","name":"Promissio小约定","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-10","name":"小新笔记","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"广州冲锋队主场队服","date":"2021-05-10","name":"晚来天欲雪yyy","web_image":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","mobile_image":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","count":1},{"num":1,"gift":"成都猎人队主场队服","date":"2021-05-10","name":"Histo2y","web_image":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","mobile_image":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-05-10","name":"蜀黍我最怕亏钱啦","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-10","name":"国王大道修车师傅","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-10","name":"丁马的早晨","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":10},{"num":1,"gift":"随机战队客场队服","date":"2021-05-10","name":"丁马的早晨","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":10},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-10","name":"-St-John","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"哔哩哔哩月度大会员","date":"2021-05-03","name":"那个臭打游戏的","web_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","mobile_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","count":1},{"num":1,"gift":"成都猎人队主场队服","date":"2021-05-03","name":"金小伍","web_image":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","mobile_image":"https://i0.hdslb.com/bfs/live/625c55aa7a020040466cc7a842ea0c57ac4f484c.png","count":1},{"num":1,"gift":"广州冲锋队主场队服","date":"2021-05-03","name":"不灵梦Blame","web_image":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","mobile_image":"https://i0.hdslb.com/bfs/live/cdc33523a1b018ee6af447f528c56848f5556eb8.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-05-03","name":"幻化叶之韵","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":10},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-03","name":"-莉娅菠萝-","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-03","name":"康斯坦丁Constantine.","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-05-03","name":"夜魅三清","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-03","name":"夜魅三清","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-05-03","name":"蓝岚染","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-03","name":"用了五级经验卡","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-05-03","name":"蜀黍我最怕亏钱啦","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-05-03","name":"aii01992","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":10},{"num":1,"gift":"哔哩哔哩月度大会员","date":"2021-04-26","name":"妄想逃离现实的星轨君","web_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","mobile_image":"https://i0.hdslb.com/bfs/live/49b6d12eadc81acf68d5378b257df78ec38b4355.png","count":1},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-04-26","name":"乌鲁乌鲁崽","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-04-26","name":"思维回廊","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":1},{"num":1,"gift":"随机战队客场队服","date":"2021-04-26","name":"思维回廊","web_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","mobile_image":"https://i0.hdslb.com/bfs/live/2198f9659645d28a7fc20c15e93f050a30706016.png","count":1},{"num":1,"gift":"上海龙之队主场队服","date":"2021-04-26","name":"LilPank","web_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","mobile_image":"https://i0.hdslb.com/bfs/live/1e53fc5f1f13c1e2e30eaa7504bc0d939e77df42.png","count":10},{"num":1,"gift":"杭州闪电队主场队服","date":"2021-04-26","name":"一日学滴滴","web_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","mobile_image":"https://i0.hdslb.com/bfs/live/e7cc2043c85c43f8bdd338a693eef3fe3fc18757.png","count":10}]}}
- $de_raw = json_decode($raw, true);
- }
-
- /**
- * @use 获取扭蛋信息
- * @param int $coin_id
- * @param string $referer
- * @return array
- */
- private static function getCapsuleInfo(int $coin_id, string $referer): array
- {
- $url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/get_capsule_info_v3';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'id' => $coin_id,
- 'from' => 'web',
- '-' => Common::getUnixTimestamp()
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- // data -> status 0||2
- // {"code":0,"message":"0","ttl":1,"data":{"coin":9,"rule":"2020年英雄联盟职业联赛春季赛抽奖奖池","gift_list":[{"name":"辣条","num":1,"web_url":"https://i0.hdslb.com/bfs/live/48605b0fe9eca5aba87f93da0fa0aa361c419835.png","mobile_url":"https://i0.hdslb.com/bfs/live/8e7a4dc8de374faee22fca7f9a3f801a1712a36b.png","usage":{"text":"辣条是一种直播虚拟礼物,可以在直播间送给自己喜爱的主播哦~","url":""},"type":1,"expire":"3天","gift_type":"325a347f91903c0353385e343dd358f0"},{"name":"3天头衔续期卡","num":1,"web_url":"https://i0.hdslb.com/bfs/live/48aecec2d7243b6f8bd17f20ff715db89f9adcec.png","mobile_url":"https://i0.hdslb.com/bfs/live/48aecec2d7243b6f8bd17f20ff715db89f9adcec.png","usage":{"text":"3天头衔续期卡*1","url":""},"type":21,"expire":"1周","gift_type":"4bda2f960342d86a426ebc067d3633ed"},{"name":"LPL2020助威","num":1,"web_url":"https://i0.hdslb.com/bfs/live/d9ee9558fcc438c99deb00ed1f6bd3707bac3452.png","mobile_url":"https://i0.hdslb.com/bfs/live/d9ee9558fcc438c99deb00ed1f6bd3707bac3452.png","usage":{"text":"2020LPL限定头衔","url":""},"type":2,"expire":"1周","gift_type":"b114c47920fce2aca5fca7a27cca5915"},{"name":"随机英雄联盟角色手办","num":1,"web_url":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_url":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","usage":{"text":"随机英雄联盟角色手办*1","url":""},"type":100024,"expire":"当天","gift_type":"6a4ae5853753d67d07cea2b1750795f4"},{"name":"2020LPL彩色弹幕","num":1,"web_url":"https://i0.hdslb.com/bfs/live/9a571f9d82c2a8cbbe869fd92796e70b19f9c2cc.png","mobile_url":"https://i0.hdslb.com/bfs/live/9a571f9d82c2a8cbbe869fd92796e70b19f9c2cc.png","usage":{"text":"LPL专属彩色弹幕","url":""},"type":20,"expire":"3天","gift_type":"14e40c6949800b5d840011e47e54d0c5"},{"name":"7天头衔续期卡","num":1,"web_url":"https://i0.hdslb.com/bfs/live/a2ffb62dc90d4896ddc3d1dcdbe83ac5d1dd7328.png","mobile_url":"https://i0.hdslb.com/bfs/live/a2ffb62dc90d4896ddc3d1dcdbe83ac5d1dd7328.png","usage":{"text":"7天头衔续期卡*1","url":""},"type":21,"expire":"1周","gift_type":"bbfc114b65126486a40c81daedd911e5"},{"name":"2020LPL春季赛助威券","num":1,"web_url":"https://i0.hdslb.com/bfs/live/be4cdecc4809caf8aa21817880a3283672b5a477.png","mobile_url":"https://i0.hdslb.com/bfs/live/be4cdecc4809caf8aa21817880a3283672b5a477.png","usage":{"text":"再来一次!(✪ω✪)","url":""},"type":22,"expire":"当天","gift_type":"96d2b8187ec6564fa40733153a41ac14"},{"name":"30天头衔续期卡","num":1,"web_url":"https://i0.hdslb.com/bfs/live/fc49e08115db6edd0276fba69ed8835a64714441.png","mobile_url":"https://i0.hdslb.com/bfs/live/fc49e08115db6edd0276fba69ed8835a64714441.png","usage":{"text":"30天头衔续期卡*1","url":""},"type":21,"expire":"1周","gift_type":"02810fd04244c47952bd4ed0b35617db"},{"name":"辣条","num":233,"web_url":"https://i0.hdslb.com/bfs/live/48605b0fe9eca5aba87f93da0fa0aa361c419835.png","mobile_url":"https://i0.hdslb.com/bfs/live/8e7a4dc8de374faee22fca7f9a3f801a1712a36b.png","usage":{"text":"辣条是一种直播虚拟礼物,可以在直播间送给自己喜爱的主播哦~","url":""},"type":1,"expire":"3天","gift_type":"a6d260760dfb1fe9f5375b3c8c7bd7ad"},{"name":"随机提伯斯熊毛绒公仔","num":1,"web_url":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_url":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","usage":{"text":"提伯斯熊毛绒公仔*1","url":""},"type":100024,"expire":"当天","gift_type":"2df71ff3306a4a2b4a627889cbd63c5b"}],"change_num":10000,"status":0,"is_login":true,"user_score":90000,"list":[{"num":1,"gift":"随机英雄联盟角色手办","date":"2020-04-23","name":"nXBo7p0svjm","web_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","count":1},{"num":1,"gift":"随机提伯斯熊毛绒公仔","date":"2020-04-20","name":"z98rwt","web_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","count":1},{"num":1,"gift":"随机英雄联盟角色手办","date":"2020-04-18","name":"wBQW6Z6jgbb","web_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","count":1},{"num":1,"gift":"随机提伯斯熊毛绒公仔","date":"2020-04-13","name":"dU9449p1zkz","web_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","count":1},{"num":1,"gift":"随机英雄联盟角色手办","date":"2020-04-10","name":"ckcs8151","web_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","count":1},{"num":1,"gift":"随机提伯斯熊毛绒公仔","date":"2020-04-06","name":"l1d9fgn1gl","web_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","count":1},{"num":1,"gift":"随机提伯斯熊毛绒公仔","date":"2020-03-31","name":"rlBF7ivbffe","web_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","count":1},{"num":1,"gift":"随机英雄联盟角色手办","date":"2020-03-29","name":"卟要悔","web_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","count":1},{"num":1,"gift":"随机提伯斯熊毛绒公仔","date":"2020-03-23","name":"就这样8丶","web_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","mobile_image":"https://i0.hdslb.com/bfs/live/61414ab727c55cd1de8fb5c1c79a5a05dada3a55.png","count":1},{"num":1,"gift":"随机英雄联盟角色手办","date":"2020-03-17","name":"bIud77Vsory","web_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","mobile_image":"https://i0.hdslb.com/bfs/live/4a2f604ef7b3dad583c054d4ffdb30e37f37ad9c.png","count":1}]}}
- return json_decode($raw, true);
- }
-
- /**
- * @获取剩余抽奖次数
- * @param int $coin_id
- * @param string $referer
- * @return int
- */
- private static function getLuckyNum(int $coin_id, string $referer): int
- {
- $capsule_info = self::getCapsuleInfo($coin_id, $referer);
- if ($capsule_info['code'] == 0) {
- Log::info("获取剩余抽奖次数成功 {$capsule_info['data']['coin']}");
- return $capsule_info['data']['coin'];
- }
- Log::warning("获取剩余抽奖次数失败 " . json_encode($capsule_info, true));
- return 0;
- }
-
-
- /**
- * @use 获取用户活动任务
- * @param int $act_id
- * @param string $referer
- */
- private static function userActTask(int $act_id, string $referer)
- {
- $url = 'https://api.live.bilibili.com/xlive/activity-interface/v1/activitytask/user_acttask/info';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'act_id' => $act_id,
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1,"data":{"act_info":{"id":30008,"title":"OWL2021","desc":"","start_time":0,"end_time":0,"sys_time":1621059078,"act_status":0},"task_list":[{"task_id":19,"task_name":"分享有礼","task_desc":"每日首次分享赛事直播间","task_cycle_type":0,"task_cycle_id":20210515,"progress":{"list":[{"progress_list":[{"task_id":19,"task_type":4,"task_level":1,"target_num":1,"current_num":0,"progress_status":0,"task_status":0,"real_num":0}],"reward_list":["OWL2021补给券"],"task_status":0,"draw_status":0,"task_title":"分享有礼","task_desc":"每日首次分享赛事直播间"}],"task_map":{"4":{"task_id":19,"task_type":4,"task_level":1,"target_num":1,"current_num":0,"progress_status":0,"task_status":0,"real_num":0}},"level":0,"is_finish":0}},{"task_id":18,"task_name":"关注有礼","task_desc":"每日首次关注任意3位推荐主播","task_cycle_type":0,"task_cycle_id":20210515,"progress":{"list":[{"progress_list":[{"task_id":18,"task_type":5,"task_level":1,"target_num":3,"current_num":0,"progress_status":0,"task_status":0,"real_num":0}],"reward_list":["OWL2021补给券"],"task_status":0,"draw_status":0,"task_title":"关注有礼","task_desc":"每日首次关注任意3位推荐主播"}],"task_map":{"5":{"task_id":18,"task_type":5,"task_level":1,"target_num":3,"current_num":0,"progress_status":0,"task_status":0,"real_num":0}},"level":0,"is_finish":0}}]}}
- $de_raw = json_decode($raw, true);
- }
-
- /**
- * @use 领取任务奖励
- * @param int $act_id
- * @param int $task_id
- * @param int $level_id
- * @param int $cycle_id
- * @param string $referer
- */
- private static function getTaskAward(int $act_id, int $task_id, int $level_id, int $cycle_id, string $referer)
- {
- $url = 'https://api.live.bilibili.com/xlive/activity-interface/v1/activitytask/user_acttask/getaward';
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => $referer
- ];
- $payload = [
- 'act_id' => $act_id,
- 'task_id' => $task_id,
- 'level_id' => 1,
- 'cycle_id' => $cycle_id,
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf(),
- 'visit_id' => ''
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1,"data":{}}
- $de_raw = json_decode($raw, true);
-
- }
-}
\ No newline at end of file
diff --git a/src/plugin/CheckUpdate.php b/src/plugin/CheckUpdate.php
deleted file mode 100644
index fc692fd..0000000
--- a/src/plugin/CheckUpdate.php
+++ /dev/null
@@ -1,130 +0,0 @@
- time()) {
- return;
- }
- self::check();
- self::setLock(8 * 60 * 60);
- }
-
- /**
- * @use 检查
- */
- private static function check()
- {
- Log::info('开始检查项目更新');
- self::loadJsonData();
- Log::info('拉取线上最新配置');
- self::fetchLatest();
- // 检测项目版本
- if (!self::compareVersion()) {
- Log::info('程序已是最新版本');
- } else {
- // Todo 完善提示信息
- $time = self::$latest_conf->get('time');
- $version = self::$latest_conf->get('version');
- $des = self::$latest_conf->get('des');
- $info = "请注意程序有版本更新变动哦~\n\n版本号: $version\n\n更新日志: $des\n\n更新时间: $time\n\n";
- Log::notice($info);
- Notice::push('update', $info);
- }
-
- // 检测配置版本
- if (!self::compareINIVersion()) {
- Log::info('配置已是最新版本');
- } else {
- $time = self::$latest_conf->get('ini_time');
- $version = self::$latest_conf->get('ini_version');
- $des = self::$latest_conf->get('ini_des');
- $info = "请注意配置有版本变动更新哦~\n\n版本号: $version\n\n更新日志: $des\n\n更新时间: $time\n\n";
- Log::notice($info);
- Notice::push('update', $info);
- }
- }
-
- /**
- * @use 拉取最新
- */
- private static function fetchLatest()
- {
- $url = self::$current_conf->get('raw_url');
- $payload = [];
- $raw = Curl::get('other', $url, $payload);
- self::$latest_conf = json(json_decode($raw, true));
- }
-
- /**
- * @use 加载本地JSON DATA
- */
- private static function loadJsonData()
- {
- self::$current_conf = json(self::$repository);
- }
-
- /**
- * @use 比较版本号
- * @return bool
- */
- private static function compareVersion(): bool
- {
- $current_version = self::$current_conf->get('version');
- $latest_version = self::$latest_conf->get('version');
- // true 有更新 false 无更新
- return !($current_version == $latest_version);
- }
-
- /**
- * @use 比较INI版本号
- * @return bool
- */
- private static function compareINIVersion(): bool
- {
- $current_version = self::$current_conf->get('ini_version');
- $latest_version = self::$latest_conf->get('ini_version');
- // true 有更新 false 无更新
- return !($current_version == $latest_version);
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/Competition.php b/src/plugin/Competition.php
deleted file mode 100644
index 105077f..0000000
--- a/src/plugin/Competition.php
+++ /dev/null
@@ -1,185 +0,0 @@
- time() || !getEnable('match_forecast')) {
- return;
- }
- self::startStake();
- self::setLock(self::timing(1, 30));
- }
-
- /**
- * @use 开始破产
- */
- private static function startStake()
- {
- $questions = self::fetchQuestions();
- $max_guess = getConf('max_num', 'match_forecast');
- foreach ($questions as $index => $question) {
- if ($index >= $max_guess) {
- break;
- }
- $guess = self::parseQuestion($question);
- self::addGuess($guess);
- }
- }
-
- /**
- * @use 添加竞猜
- * @param array $guess
- */
- private static function addGuess(array $guess)
- {
- Log::info($guess['title']);
- Log::info($guess['estimate']);
- $url = 'https://api.bilibili.com/x/esports/guess/add';
- $payload = [
- 'oid' => $guess['oid'],
- 'main_id' => $guess['main_id'],
- 'detail_id' => $guess['detail_id'],
- 'count' => $guess['count'],
- 'is_fav' => 0,
- 'csrf' => getCsrf()
- ];
- $headers = [
- 'origin' => 'https://www.bilibili.com',
- 'referer' => 'https://www.bilibili.com/v/game/match/competition'
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1}
- if ($de_raw['code'] == 0) {
- Log::notice("破产成功: {$de_raw['message']}");
- } else {
- Log::warning("破产失败: $raw");
- }
- }
-
- /**
- * @use 预计猜测结果
- * @param array $question
- * @return array
- */
- private static function parseQuestion(array $question): array
- {
- $guess = [];
- $guess['oid'] = $question['contest']['id'];
- $guess['main_id'] = $question['questions'][0]['id'];
- $details = $question['questions'][0]['details'];
- $guess['count'] = (($count = getConf('max_coin', 'match_forecast')) <= 10) ? $count : 10;
- $guess['title'] = $question['questions'][0]['title'];
- foreach ($details as $detail) {
- $guess['title'] .= " 队伍: {$detail['option']} 赔率: {$detail['odds']}";
- }
- array_multisort(array_column($details, "odds"), SORT_ASC, $details);
- switch (getConf('bet', 'match_forecast')) {
- case 1:
- // 压大
- $detail = array_pop($details);
- break;
- case 2:
- // 压小
- $detail = array_shift($details);
- break;
- case 3:
- // 随机
- $detail = $details[array_rand($details)];
- break;
- default:
- // 乱序
- shuffle($details);
- $detail = $details[array_rand($details)];
- break;
- }
- $guess['detail_id'] = $detail['detail_id'];
- $profit = ceil($guess['count'] * $detail['odds']);
- $guess['estimate'] = "竞猜队伍: {$detail['option']} 预计下注: {$guess['count']} 预计赚取: $profit 预计亏损: {$guess['count']} (硬币)";
- return $guess;
- }
-
- /**
- * @use 获取所有问题
- * @param int $page_max
- * @return array
- */
- private static function fetchQuestions(int $page_max = 10): array
- {
- $questions = [];
- $url = 'https://api.bilibili.com/x/esports/guess/collection/question';
- for ($i = 1; $i < $page_max; $i++) {
- $payload = [
- 'pn' => $i,
- 'ps' => 50,
- 'stime' => date("Y-m-d H:i:s", strtotime(date("Y-m-d", time()))),
- 'etime' => date("Y-m-d H:i:s", strtotime(date("Y-m-d", time())) + 86400 - 1)
- ];
- $headers = [
- 'origin' => 'https://www.bilibili.com',
- 'referer' => 'https://www.bilibili.com/v/game/match/competition',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data']['list'])) {
- // 为空跳出
- if (count($de_raw['data']['list']) == 0) {
- break;
- }
- // 添加到集合
- foreach ($de_raw['data']['list'] as $question) {
- // 判断是否有效 正2分钟
- if (($question['contest']['stime'] - 600 - 120) > time()) {
- $questions[] = $question;
- }
- }
- // 和页面的不匹配 跳出
- if (count($de_raw['data']['list']) != $de_raw['data']['page']['size']) {
- break;
- }
- } else {
- // 错误跳出
- break;
- }
- }
- Log::info('获取到 ' . count($questions) . ' 个有效竞猜');
- return $questions;
- }
-
-
-}
\ No newline at end of file
diff --git a/src/plugin/DailyBag.php b/src/plugin/DailyBag.php
deleted file mode 100644
index f9dfadb..0000000
--- a/src/plugin/DailyBag.php
+++ /dev/null
@@ -1,84 +0,0 @@
- time() || !getEnable('daily_bag')) {
- return;
- }
- self::dailyBagPC();
- self::dailyBagAPP();
- self::setLock(12 * 60 * 60);
- }
-
- /**
- * @use 领取每日包裹PC
- */
- private static function dailyBagPC()
- {
- sleep(1);
- $url = 'https://api.live.bilibili.com/gift/v2/live/receive_daily_bag';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code']) {
- Log::warning('[PC] 日常/周常礼包领取失败', ['msg' => $data['message']]);
- } else {
- Log::notice('[PC] 日常/周常礼包领取成功');
- }
- }
-
- /**
- * @use 领取每日包裹APP
- */
- private static function dailyBagAPP()
- {
- sleep(1);
- $url = 'https://api.live.bilibili.com/AppBag/sendDaily';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code']) {
- Log::warning('[APP] 日常/周常礼包领取失败', ['msg' => $data['message']]);
- } else {
- Log::notice('[APP] 日常/周常礼包领取成功');
- }
- }
-
-}
diff --git a/src/plugin/DailyTask.php b/src/plugin/DailyTask.php
deleted file mode 100644
index ae0e06d..0000000
--- a/src/plugin/DailyTask.php
+++ /dev/null
@@ -1,137 +0,0 @@
- time() || !getEnable('daily_task')) {
- return;
- }
-
- $data = self::check();
- // if (isset($data['data']['double_watch_info'])) {
- // self::double_watch_info($data['data']['double_watch_info']);
- // }
- if (isset($data['data']['sign_info'])) {
- self::sign_info($data['data']['sign_info']);
- }
- self::setLock(mt_rand(8, 12) * 60 * 60);
- }
-
- /**
- * @use 检查每日任务
- * @return mixed
- */
- private static function check(): mixed
- {
- $url = 'https://api.live.bilibili.com/i/api/taskInfo';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- Log::info('正在检查每日任务...');
- if (isset($data['code']) && $data['code']) {
- Log::warning('每日任务检查失败!', ['msg' => $data['message']]);
- }
- return $data;
- }
-
- /**
- * @use 每日签到
- * @param $info
- */
- private static function sign_info($info)
- {
- Log::info('检查任务「每日签到」...');
-
- if ($info['status'] == 1) {
- Log::notice('该任务已完成');
- return;
- }
- $url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign';
- $headers = [
- 'origin' => 'https://link.bilibili.com',
- 'referer' => 'https://link.bilibili.com/p/center/index'
- ];
- $url = 'https://api.live.bilibili.com/sign/doSign';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- // 您被封禁了,无法进行操作
- // {"code":1011040,"message":"今日已签到过,无法重复签到","ttl":1,"data":null}
- // {"code":0,"message":"0","ttl":1,"data":{"text":"3000点用户经验,2根辣条","specialText":"再签到3天可以获得666银瓜子","allDays":31,"hadSignDays":2,"isBonusDay":0}}
- // {"code":0,"message":"0","ttl":1,"data":{"text":"3000点用户经验,2根辣条,50根辣条","specialText":"","allDays":31,"hadSignDays":20,"isBonusDay":1}}
- if (isset($data['code']) && $data['code']) {
- Log::warning("签到失败: {$data['message']}");
- } else {
- Log::notice("签到成功: {$data['data']['text']}");
- // 推送签到信息
- Notice::push('todaySign', $data['data']['text']);
- }
- }
-
- /**
- * @use 双端任务
- * @param $info
- */
- private static function double_watch_info($info)
- {
- Log::info('检查任务「双端观看直播」...');
-
- if ($info['status'] == 2) {
- Log::notice('已经领取奖励');
- return;
- }
- if ($info['mobile_watch'] != 1 || $info['web_watch'] != 1) {
- Log::notice('任务未完成,请等待');
- return;
- }
- $url = 'https://api.live.bilibili.com/activity/v1/task/receive_award';
- $payload = [
- 'task_id' => 'double_watch_task',
- ];
- $data = Curl::post('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code']) {
- Log::warning("「双端观看直播」任务奖励领取失败,{$data['message']}!");
- } else {
- Log::info('奖励领取成功!');
- foreach ($info['awards'] as $vo) {
- Log::notice(sprintf("获得 %s × %d", $vo['name'], $vo['num']));
- }
- }
- }
-}
diff --git a/src/plugin/DataTreating.php b/src/plugin/DataTreating.php
deleted file mode 100644
index 9e1e8cb..0000000
--- a/src/plugin/DataTreating.php
+++ /dev/null
@@ -1,78 +0,0 @@
- $data['room_id'], 'lid' => $data['raffle_id']];
- } catch (Exception $e) {
- return;
- }
- switch ($data['raffle_type']) {
- case 'storm':
- // 风暴
- StormRaffle::pushToQueue($info);
- break;
- case 'raffle':
- // 礼物
- GiftRaffle::pushToQueue($info);
- break;
- case 'guard':
- // 舰长
- GuardRaffle::pushToQueue($info);
- break;
- case 'small_tv':
- // 电视
- GiftRaffle::pushToQueue($info);
- break;
- case 'pk':
- // 乱斗
- PkRaffle::pushToQueue($info);
- break;
- case 'anchor':
- // 天选时刻
- AnchorRaffle::pushToQueue($info);
- break;
- case 'red_pocket':
- // 利是包
- RedPocketRaffle::pushToQueue($info);
- break;
- default:
- break;
- }
- }
-}
diff --git a/src/plugin/DoubleHeart.php b/src/plugin/DoubleHeart.php
deleted file mode 100644
index e8b6d3c..0000000
--- a/src/plugin/DoubleHeart.php
+++ /dev/null
@@ -1,94 +0,0 @@
- time() || !getEnable('double_heart')) {
- return;
- }
- self::setPauseStatus();
- self::webHeart();
- self::appHeart();
- self::setLock(5 * 60);
- }
-
- /**
- * @use Web 心跳
- */
- protected static function webHeart()
- {
- User::webGetUserInfo();
- $url = 'https://api.live.bilibili.com/User/userOnlineHeart';
- $payload = [
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf(),
- 'room_id' => getConf('room_id', 'global_room'),
- '_' => time() * 1000,
- ];
- $headers = [
- 'Referer' => 'https://live.bilibili.com/' . $payload['room_id'],
- ];
- $data = Curl::post('app', $url, $payload, $headers);
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code']) {
- Log::warning('[PC] 发送在线心跳失败', ['msg' => $data['message']]);
- } else {
- Log::notice('[PC] 发送在线心跳成功');
- }
- }
-
- /**
- * @use 手机端心跳
- */
- protected static function appHeart()
- {
- User::appGetUserInfo();
- $url = 'https://api.live.bilibili.com/mobile/userOnlineHeart';
- $payload = [
- 'room_id' => getConf('room_id', 'global_room'),
- ];
- $data = Curl::post('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code']) {
- Log::warning('[APP] 发送在线心跳失败', ['msg' => $data['message']]);
- } else {
- Log::notice('[APP] 发送在线心跳成功');
- }
- }
-}
diff --git a/src/plugin/Dynamic.php b/src/plugin/Dynamic.php
deleted file mode 100644
index 3ee220a..0000000
--- a/src/plugin/Dynamic.php
+++ /dev/null
@@ -1,305 +0,0 @@
- '3230836',
- '转发抽奖' => '434405',
- '动态抽奖' => '7146512',
- '关注抽奖' => '5608480',
- // '关注+转发' => '7544627',
- // '评论抽奖'=>'2630459',
- // '转发关注评论抽奖'=>'8339319',
- // '转发+评论抽奖'=> '7169938',
- // '关注评论抽奖'=>'8078587',
- // '转发评论抽奖' => '7019788',
- // '抽奖'=>'228584',
-
- ];
-
-
- private static array $article_list = [];
-
- /**
- * 获取抽奖话题下的帖子
- */
- public static function getAwardTopic(): array
- {
- foreach (self::$topic_list as $t_name => $t_id) {
- Log::info("获取关键字 $t_name - $t_id");
- $url = 'https://api.vc.bilibili.com/topic_svr/v1/topic_svr/topic_new?topic_id=' . $t_id;
- $data = Curl::request('get', $url);
- // 失败跳过
- if (is_null($data)) continue;
- $data = json_decode($data, true);
- // new
- foreach ($data['data']['cards'] as $article) {
- $article_id = $article['desc']['dynamic_id'];
- // 获取 description
- $card = json_decode($article['card'], true);
-
- if (isset($card['category']) && isset($card['categories'])) {
- // 处理专栏 提前处理
- continue;
- } elseif (isset($card['aid']) && isset($card['cid'])) {
- // 处理视频转发
- $description = $card['dynamic'];
- } elseif (array_key_exists("description", $card['item'])) {
- // 主动态
- $description = $card['item']['description'];
- } elseif (array_key_exists("content", $card['item'])) {
- // 子动态
- // Todo 暂时跳过 需要合适的处理方法
- // description = $card['item']['content'];
- continue;
- } else {
- // 链接到视频的动态 少数 跳过
- // print_r($card);
- continue;
- }
- $item = [
- 'uid' => $article['desc']['uid'],
- 'rid' => $article['desc']['rid'],
- 'did' => $article_id,
- 'tm' => $article['desc']['timestamp'],
- 'desc' => $description
- ];
- // 过滤为true 就跳过
- if (self::filterLayer($item)) continue;
- // 不要原始desc
- unset($item['desc']);
- self::$article_list[$article_id] = $item;
- }
- // $has_more = 0;
- // more ??
- // https://api.vc.bilibili.com/topic_svr/v1/topic_svr/topic_history?topic_name=转发抽奖&offset_dynamic_id=454347930068783808
- }
- $num = count(self::$article_list);
- Log::info("获取到 $num 条有效动态");
- return self::$article_list;
- }
-
- /**
- * 动态转发
- * @param $rid
- * @param string $content
- * @param int $type
- * @param int $repost_code
- * @param string $from
- * @param string $extension
- * @return bool
- */
- public static function dynamicRepost($rid, string $content = "", int $type = 1, int $repost_code = 3000, string $from = "create.comment", string $extension = '{"emoji_type":1}'): bool
- {
- $url = "https://api.vc.bilibili.com/dynamic_repost/v1/dynamic_repost/reply";
- $payload = [
- "uid" => getUid(),
- "rid" => $rid,
- "type" => $type,
- "content" => $content,
- "extension" => $extension,
- "repost_code" => $repost_code,
- "from" => $from,
- ];
- $raw = Curl::post('app', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data'])) {
- return true;
- }
- return false;
- }
-
- /**
- * 发表评论
- * @param int $rid
- * @param string $message
- * @param int $type
- * @param int $plat
- * @return bool
- */
- public static function dynamicReplyAdd(int $rid, string $message = "", int $type = 11, int $plat = 1): bool
- {
- $url = "https://api.bilibili.com/x/v2/reply/add";
- $payload = [
- "oid" => $rid,
- "plat" => $plat,
- "type" => $type,
- "message" => $message,
- "csrf" => getCsrf(),
- ];
- $raw = Curl::post('app', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data'])) {
- return true;
- }
- return false;
- }
-
- /**
- * 删除指定动态
- * @param $did
- * @return bool
- */
- public static function removeDynamic($did): bool
- {
- $url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/rm_dynamic';
- $payload = [
- "dynamic_id" => $did,
- "csrf_token" => getCsrf(),
- ];
- $raw = Curl::post('app', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data'])) {
- return true;
- }
- return false;
- }
-
- /**
- * 获取个人发布的动态
- * @param int $uid
- * @return array
- */
- public static function getMyDynamic(int $uid = 0): array
- {
- $uid = $uid == 0 ? getUid() : $uid;
- $url = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history";
- $offset = '';
- $has_more = true;
- $card_list = [];
- while ($has_more) {
- $payload = [
- "host_uid" => $uid,
- "need_top" => 1,
- "offset_dynamic_id" => $offset,
- ];
- $raw = Curl::get('app', $url, $payload);
- $de_raw = json_decode($raw, true);
- $has_more = $de_raw['data']['has_more'] == 1;
- if (!isset($de_raw['data']['cards'])) {
- continue;
- }
- $card_list = array_merge($card_list, $de_raw['data']['cards']);
- foreach ($de_raw['data']['cards'] as $card) {
- $offset = $card['desc']['dynamic_id_str'];
- }
- }
- return $card_list;
- }
-
- /**
- * 获取动态详情
- * @param $did
- * @return mixed
- */
- public static function getDynamicDetail($did): mixed
- {
- $url = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail";
- $payload = [
- "dynamic_id" => $did,
- ];
- $raw = Curl::get('app', $url, $payload);
- return json_decode($raw, true);
-
- }
-
- /**
- * 获取抽奖动态信息
- * @param $did
- * @return mixed
- */
- public static function getLotteryNotice($did): mixed
- {
- $url = 'https://api.vc.bilibili.com/lottery_svr/v1/lottery_svr/lottery_notice';
- $payload = [
- "dynamic_id" => $did,
- ];
- $raw = Curl::get('app', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * 获取个人动态TAB列表
- * @param int $uid
- * @param int $type_list
- * @return mixed
- */
- public static function getDynamicTab(int $uid = 0, int $type_list = 268435455): mixed
- {
- $uid = $uid == 0 ? getUid() : $uid;
- $url = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new";
- $offset = '';
- $has_more = true;
- $card_list = [];
- while ($has_more) {
- $payload = [
- "uid" => $uid,
- "type_list" => $type_list,
- "offset_dynamic_id" => $offset,
- ];
- $raw = Curl::get('app', $url, $payload);
- $de_raw = json_decode($raw, true);
- if (!isset($de_raw['data']['cards'])) {
- continue;
- }
- $card_list = $de_raw['data']['cards'];
- $has_more = $de_raw['data']['has_more'] == 1;
- foreach ($de_raw['data']['cards'] as $card) {
- $offset = $card['desc']['dynamic_id_str'];
- }
- }
- return $card_list;
- }
-
- /**
- * @use 过滤层
- * @param array $item
- * @return bool
- */
- protected static function filterLayer(array $item): bool
- {
- self::loadJsonData();
- // 过滤描述
- $default_words = self::$store->get("DynamicForward.default");
- $common_words = self::$store->get("Common.default");
- $custom_words = empty($words = getConf('filter_words', 'dynamic')) ? [] : explode(',', $words);
- $total_words = array_merge($default_words, $custom_words, $common_words);
- foreach ($total_words as $word) {
- if (str_contains($item['desc'], $word)) {
- Log::warning("当前动态#{$item['did']}触发关键字过滤 $word");
- return true;
- }
- }
- // 过滤UID
- $uid_list = self::$store->get("Common.uid_list");
- if (array_key_exists((int)$item['uid'], $uid_list)) {
- Log::warning("当前动态#{$item['did']}触发UP黑名单过滤 {$item['uid']}");
- return true;
- }
- // 过滤粉丝数量
- if (($num = Live::getMidFollower((int)$item['uid'])) < getConf('min_fans_num', 'dynamic')) {
- Log::warning("当前动态#{$item['did']}触发UP粉丝数量过滤 $num");
- return true;
- }
- return false;
- }
-}
diff --git a/src/plugin/Forward.php b/src/plugin/Forward.php
deleted file mode 100644
index 14ac603..0000000
--- a/src/plugin/Forward.php
+++ /dev/null
@@ -1,314 +0,0 @@
- time()) {
- return;
- }
- self::setPauseStatus();
- if (!self::start()) {
- self::setLock(60 * 60);
- return;
- }
- self::setLock(5 * 60);
- }
-
-
- public static function start(): bool
- {
- // 取关未中奖
- if (getConf('clear_group_follow', 'dynamic')) {
- self::clearDynamic();
- }
- // 自动转发关注评论
- if (getConf('enable', 'dynamic')) {
- self::autoRepost();
- }
- // 强制清除抽奖关注组
- if (getConf('clear_group_follow', 'dynamic')) {
- self::clearAllDynamic();
- self::clearFollowGroup();
- }
- return true;
- }
-
- /**
- * 自动转发抽奖
- */
- public static function autoRepost()
- {
- $article_list = Dynamic::getAwardTopic();
- foreach ($article_list as $did => $article) {
-
- if (isset(self::$already[$did])) {
- //重复
- Log::info("[动态抽奖]-已转发 跳过: $did {$article['uid']}");
- continue;
- }
- // 评论
- Log::info("[动态抽奖]-评论: $did {$article['rid']}");
- if (Dynamic::dynamicReplyAdd($article['rid'], self::getReplyMsg())) {
- // 转发
- Log::info("[动态抽奖]-转发: $did");
- if (Dynamic::dynamicRepost($did, self::getReplyMsg())) {
- // 关注
- Log::info("[动态抽奖]-关注: $did {$article['uid']}");
- self::addToGroup($article['uid']); //
- self::$already[$did] = 1;
- }
- }
- sleep(1);
- }
- }
-
- /**
- * 清理无效的动态
- */
- private static function clearDynamic()
- {
- $dynamicList = Dynamic::getMyDynamic();
-
- Log::info("[动态抽奖]-检测中奖 动态数: " . count($dynamicList));
- foreach ($dynamicList as $dynamic) {
- $flag = false;
- $msg = '';
- $did = $dynamic['desc']['dynamic_id'];
- $card = json_decode($dynamic['card'], true);
-
-
- if (isset($card["item"]["miss"]) && $card["item"]["miss"] == 1) {
- $flag = true;
- $msg = "[动态抽奖]-删除动态 源动态已删除 $did";
- }
- if (isset($card["origin_extension"]['lott'])) {
- $lott = json_decode($card["origin_extension"]["lott"], true);
- if (isset($lott["lottery_time"]) && $lott["lottery_time"] <= time()) {
- $flag = true;
- $msg = "[动态抽奖]-删除动态 抽奖已过期 $did";
- }
- }
- if (isset($card["item"]["orig_dy_id"])) {
- $ret = Dynamic::getLotteryNotice($card["item"]["orig_dy_id"]);
- if (isset($ret['data']['lottery_time']) && $ret['data']['lottery_time'] <= time()) {
- $flag = true;
- $msg = "[动态抽奖]-删除动态 抽奖已过期 $did";
- }
- }
-
-// if (isset($card['origin'])) {
-// $origin = json_decode($card["origin"], true);
-// if (isset($origin['item']['description'])) {
-// if (isset($card["item"]['description'])) {
-// $text = $origin["item"]["description"];
-// } elseif (isset($card["item"]['content'])) {
-// $text = $card["item"]["content"];
-// } else {
-// $text = null;
-// }
-// if ($text) {
-// continue;
-// // 关键字过滤
-// }
-// }
-// }
-
-
- if ($flag) {
- self::$del_dynamic[$did] = $msg;
- if (isset($card['origin_user']['info']['uid'])) {
- self::$un_follows[] = $card['origin_user']['info']['uid'];
- }
- }
- }
-
- // 取关
- foreach (self::$del_dynamic as $did => $msg) {
- Dynamic::removeDynamic($did);
- Log::info($msg);
- unset(self::$del_dynamic[$did]);
- }
- // 取关
- foreach (self::$un_follows as $uid) {
- // 非转发抽奖动态关注的up 不取关
- if (isset(self::$draw_follow[$uid])) {
- Log::info("[动态抽奖]-未中奖-取关 $uid");
- User::setUserFollow($uid, true);
- }
- }
- }
-
- /**
- * @use 取关
- */
- private static function clearFollowGroup()
- {
- $tags = User::fetchTags();
- foreach ($tags as $gid => $name) {
- if (!in_array($name, ['玄不改非', '氪可改命'])) {
- continue;
- }
- $r = User::fetchTagFollowings($gid);
- foreach ($r as $uid) {
- Log::info("[清除抽奖组关注] : $uid");
- User::setUserFollow($uid, true);
- }
- }
-
- }
-
- /**
- * @use 清理动态
- */
- private static function clearAllDynamic()
- {
- $dynamicList = Dynamic::getMyDynamic();
- $msg_list = self::getReplyMsgList();
- foreach ($dynamicList as $dynamic) {
- $did = $dynamic['desc']['dynamic_id'];
- $card = json_decode($dynamic['card'], true);
- foreach ($msg_list as $msg) {
- // 异常情况 跳过
- if (!isset($card['item']['content']) || !$msg) {
- continue;
- }
- if (str_contains($card['item']['content'], $msg)) {
- Log::info("[删除所有动态] 删除动态 $did");
- Dynamic::removeDynamic($did);
- }
- }
- }
- }
-
- /**
- * @use 添加分组
- * @param int $need_follow_uid
- * @param int $anchor_id
- * @param int $time
- */
- private static function addToGroup(int $need_follow_uid, int $anchor_id = 0, int $time = 0)
- {
- // 获取分组id
- if (is_null(self::$group_id)) {
- $tags = User::fetchTags();
- $tag_id = array_search(self::$group_name, $tags);
- // 如果不存在则调用创建
- self::$group_id = $tag_id ?: User::createRelationTag(self::$group_name);
- }
- // 是否在关注里
- $default_follows = self::getDefaultFollows();
- if (!in_array($need_follow_uid, $default_follows)) {
- User::setUserFollow($need_follow_uid); // 关注
- User::tagAddUsers($need_follow_uid, self::$group_id); // 转到分组中
- self::$draw_follow[$need_follow_uid] = 1; // 记录转发抽奖关注的up
- }
- }
-
- /**
- * @use 获取默认关注
- * @return array
- */
- private static function getDefaultFollows(): array
- {
- if (!empty(self::$default_follows)) {
- return self::$default_follows;
- }
- // 如果获取默认关注错误 或者 为空则补全一个
- self::$default_follows = User::fetchTagFollowings();
- if (empty(self::$default_follows)) {
- self::$default_follows[] = 1;
- }
- return self::$default_follows;
- }
-
- /**
- * @use 获取回复 all
- * @return array
- */
- private static function getReplyMsgList(): array
- {
- $data = json(self::$repository);
- // 给个默认值
- $data = $data->find("DynamicForward.default", []);
- $data[] = getConf('auto_reply_text', 'dynamic');
- return $data;
- }
-
- /**
- * @use 获取回复 1
- * @return string
- */
- private static function getReplyMsg(): string
- {
- //更改自动回复
- if (getConf('auto_reply_text', 'dynamic') != '') {
- $msg = getConf('auto_reply_text', 'dynamic');
- } else {
- $data = self::getReplyMsgList();
- shuffle($data);
- $msg = array_pop($data);
- }
- Log::info("已将自动回复改为\"$msg\"");
- return $msg;
- }
-
-}
diff --git a/src/plugin/GiftHeart.php b/src/plugin/GiftHeart.php
deleted file mode 100644
index 87279d6..0000000
--- a/src/plugin/GiftHeart.php
+++ /dev/null
@@ -1,99 +0,0 @@
- time() || !getEnable('gift_heart')) {
- return;
- }
- self::setPauseStatus();
- if (self::giftHeart()) {
- self::setLock(60 * 60);
- return;
- }
- self::setLock(5 * 60);
- }
-
- /**
- * @use 礼物心跳
- * @return bool
- */
- private static function giftHeart(): bool
- {
- $url = 'https://api.live.bilibili.com/gift/v2/live/heart_gift_receive';
- $payload = [
- 'roomid' => getConf('room_id', 'global_room'),
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
-
- // {"code":400,"msg":"访问被拒绝","message":"访问被拒绝","data":[]}
- if (isset($de_raw['msg']) && $de_raw['code'] == 400 && $de_raw['msg'] == '访问被拒绝') {
- self::pauseLock();
- return false;
- }
-
- if ($de_raw['code'] == -403) {
- Log::info($de_raw['msg']);
- $payload = [
- 'ruid' => 17561885,
- ];
- $url = 'https://api.live.bilibili.com/eventRoom/index';
- Curl::get('app', $url, Sign::common($payload));
- return true;
- }
-
- if ($de_raw['code'] != 0) {
- Log::warning($de_raw['msg']);
- return false;
- }
-
- if ($de_raw['data']['heart_status'] == 0) {
- Log::info('没有礼物可以领了呢!');
- return true;
- }
-
- if (isset($de_raw['data']['gift_list'])) {
- foreach ($de_raw['data']['gift_list'] as $vo) {
- Log::info("{$de_raw['msg']},礼物 {$vo['gift_name']} ({$vo['day_num']}/{$vo['day_limit']})");
- }
- return false;
- }
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/GiftRaffle.php b/src/plugin/GiftRaffle.php
deleted file mode 100644
index fbefab1..0000000
--- a/src/plugin/GiftRaffle.php
+++ /dev/null
@@ -1,142 +0,0 @@
- $room_id,
- 'raffle_id' => $gift['raffleId'],
- 'raffle_name' => $gift['title'],
- 'type' => $gift['type'],
- 'wait' => $gift['time_wait'] + time(),
- ];
- Statistics::addPushList($data['raffle_name']);
- self::$wait_list[] = $data;
- }
- return true;
- }
-
- /**
- * @use 创建抽奖任务
- * @param array $raffles
- * @return array
- */
- protected static function createLottery(array $raffles): array
- {
- // V3接口 暂做保留处理
- // $url = 'https://api.live.bilibili.com/gift/v3/smalltv/join';
- // $url = 'https://api.live.bilibili.com/gift/v4/smalltv/getAward';
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v5/smalltv/join';
- $tasks = [];
- foreach ($raffles as $raffle) {
- $payload = [
- 'id' => $raffle['raffle_id'],
- 'roomid' => $raffle['room_id'],
- 'type' => $raffle['type'],
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- $tasks[] = [
- 'payload' => Sign::common($payload),
- 'source' => [
- 'room_id' => $raffle['room_id'],
- 'raffle_id' => $raffle['raffle_id'],
- 'raffle_name' => $raffle['raffle_name']
- ]
- ];
- }
- // print_r($results);
- return Curl::async('app', $url, $tasks);
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- foreach ($results as $result) {
- $data = $result['source'];
- $content = $result['content'];
- $de_raw = json_decode($content, true);
- // { "code": -403, "data": null, "message": "访问被拒绝", "msg": "访问被拒绝", }
- if (isset($de_raw['code']) && !$de_raw['code']) {
- // 推送中奖信息
- if ($de_raw['data']['award_name'] != '辣条' && $de_raw['data']['award_name'] != '') {
- $info = $de_raw['data']['award_name'] . 'x' . $de_raw['data']['award_num'];
- Notice::push('gift', $info);
- }
- Statistics::addSuccessList($data['raffle_name']);
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: {$de_raw['data']['award_name']}x{$de_raw['data']['award_num']}");
- Statistics::addProfitList($data['raffle_name'] . '-' . $de_raw['data']['award_name'], $de_raw['data']['award_num']);
- } elseif (isset($de_raw['msg']) && $de_raw['code'] == -403 && $de_raw['msg'] == '访问被拒绝') {
- Log::debug("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: {$de_raw['msg']}");
- self::pauseLock();
- } else {
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: " . isset($de_raw['msg']) ? $de_raw['msg'] : $de_raw);
- }
- }
- return '';
- }
-}
diff --git a/src/plugin/GiftSend.php b/src/plugin/GiftSend.php
deleted file mode 100644
index 6e7565e..0000000
--- a/src/plugin/GiftSend.php
+++ /dev/null
@@ -1,302 +0,0 @@
- time() || !self::inTime('23:50:00', '23:59:50')) {
- return;
- }
- if (!self::$uid) {
- self::getUserInfo();
- }
- // 方案一未通过使用方案2
- if (!self::procOne()) {
- self::procTwo();
- }
- self::$room_list = [];
- self::$medal_list = [];
- self::$tid = 0;
- // 如果在每日最后5分钟内 就50s执行一次 否则 第二天固定时间执行
- if (self::inTime('23:52:00', '23:59:59')) {
- // 减少0点左右请求损耗
- self::setLock(100);
- } else {
- self::setLock(self::timing(23, 55));
- }
- }
-
- /**
- * @use 方案1
- */
- protected static function procOne(): bool
- {
- if (!self::setTargetList()) {
- return false;
- }
- self::getMedalList();
- foreach (self::$medal_list as $room_id => $total_intimacy) {
- $bag_list = self::fetchBagList();
- if (!getConf('feed_fill', 'intimacy')) {
- $bag_list = self::checkExpireGift($bag_list);
- }
- if (count($bag_list)) {
- self::$tid = $room_id;
- self::getRoomInfo();
- // array_multisort(array_column($bag_list, "expire_at"), SORT_ASC, $bag_list);
- } else {
- break;
- }
- $current_intimacy = 0;
- foreach ($bag_list as $gift) {
- // 是辣条、亿元 && 不是过期礼物 加入小心心,暂不清楚是否有逻辑冲突
- if (!in_array($gift['gift_id'], [1, 6, 30607])) {
- continue;
- }
- Log::notice("直播间 $room_id 需赠送亲密度 $total_intimacy 剩余亲密度 " . ($total_intimacy - $current_intimacy));
- $amt = self::calcAmt($gift, $total_intimacy - $current_intimacy);
- self::sendGift($gift, $amt);
- $current_intimacy += ($gift['gift_id'] == 30607) ? ($amt * 50) : (($gift['gift_id'] == 6) ? ($amt * 10) : $amt);
- if (!($current_intimacy - $total_intimacy)) {
- Log::notice("直播间 $room_id 亲密度 $total_intimacy 送满啦~送满啦~");
- break;
- }
- }
- }
- if (!count(self::$medal_list)) {
- return false;
- }
- return true;
- }
-
- /**
- * @use 方案2
- */
- protected static function procTwo()
- {
- $bag_list = self::fetchBagList();
- $expire_gift = self::checkExpireGift($bag_list);
- if (count($expire_gift)) {
- self::getRoomInfo();
- foreach ($expire_gift as $gift) {
- self::sendGift($gift, $gift['gift_num']);
- }
- }
- }
-
- /**
- * @use 设置房间列表
- * @return bool
- */
- protected static function setTargetList(): bool
- {
- $temp = empty($temp = getConf('room_list', 'intimacy')) ? null : $temp;
- if (is_null($temp)) return false;
- self::$room_list = explode(',', $temp);
- return true;
- }
-
- /**
- * @use 获取背包列表
- * @return array
- */
- protected static function fetchBagList(): array
- {
- $new_bag_list = [];
- $payload = [];
- $url = 'https://api.live.bilibili.com/gift/v2/gift/bag_list';
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::warning('背包查看失败!', ['msg' => $data['message']]);
- return $new_bag_list;
- }
- if (isset($data['data']['list'])) {
- $bag_list = $data['data']['list'];
- if (count($bag_list)) {
- // 按过期时间 升序
- // array_multisort(array_column($bag_list, "gift_id"), SORT_DESC, $bag_list);
- array_multisort(array_column($bag_list, "expire_at"), SORT_ASC, $bag_list);
- }
- foreach ($bag_list as $vo) {
- // 去除永久礼物
- if ($vo['corner_mark'] == '永久') {
- continue;
- }
- $new_bag_list[] = $vo;
- }
- }
- return $new_bag_list;
- }
-
- /**
- * @use 查找过期礼物
- * @param array $bag_list
- * @return array
- */
- protected static function checkExpireGift(array $bag_list): array
- {
- $expire_gift_list = [];
- foreach ($bag_list as $gift) {
- if ($gift['expire_at'] >= time() && $gift['expire_at'] <= time() + 3600) {
- $expire_gift_list[] = $gift;
- }
- }
- return $expire_gift_list;
- }
-
- /**
- * @use 获取勋章列表(过滤无勋章或已满)
- */
- protected static function getMedalList()
- {
- self::$medal_list = [];
- $data = Live::fetchMedalList();
- $fans_medals = [];
- foreach ($data as $vo) {
- // 过滤主站勋章
- if (!isset($vo['roomid']) || $vo['roomid'] == 0) continue;
- $fans_medals[(string)$vo['roomid']] = $vo;
- }
- // 基于配置
- foreach (self::$room_list as $room_id) {
- // 配置是否存在获取
- if (!array_key_exists((string)$room_id, $fans_medals)) {
- continue;
- }
- $vo = $fans_medals[(string)$room_id];
- // 是否还需要投喂
- if ($vo['day_limit'] - $vo['today_feed']) {
- self::$medal_list[(string)$vo['roomid']] = ($vo['day_limit'] - $vo['today_feed']);
- }
- }
- }
-
- /**
- * @use 获取UID
- */
- protected static function getUserInfo()
- {
- $url = 'https://api.live.bilibili.com/xlive/web-ucenter/user/get_user_info';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::warning('获取帐号信息失败!', ['msg' => $data['message']]);
- Log::warning('清空礼物功能禁用!');
- self::setLock(100000000);
- return;
- }
- self::$uid = $data['data']['uid'];
- }
-
- /**
- * @use 获取直播间信息
- */
- protected static function getRoomInfo()
- {
- Log::info('正在生成直播间信息...');
- $room_id = empty(self::$tid) ? getConf('room_id', 'global_room') : self::$tid;
- $data = Live::getRoomInfoV1($room_id);
- if (isset($data['code']) && $data['code']) {
- Log::warning('获取主播房间号失败!', ['msg' => $data['message']]);
- Log::warning('清空礼物功能禁用!');
- self::setLock(100000000);
- return;
- }
- Log::info('直播间信息生成完毕!');
- self::$r_uid = (string)$data['data']['uid'];
- self::$room_id = (string)$data['data']['room_id'];
- self::$short_id = $data['data']['short_id'] ? (string)$data['data']['short_id'] : self::$room_id;
- }
-
- /**
- * @use 计算赠送数量
- * @param array $gift
- * @param int $surplus_num
- * @return int
- */
- protected static function calcAmt(array $gift, int $surplus_num): int
- {
- $amt = $gift['gift_num'];
- if ($gift['gift_id'] == 1) {
- $amt = ($surplus_num > $gift['gift_num']) ? $gift['gift_num'] : floor($surplus_num);
- }
- if ($gift['gift_id'] == 6) {
- $amt = (floor($surplus_num / 10) > $gift['gift_num']) ? $gift['gift_num'] : floor($surplus_num / 10);
- }
- if ($gift['gift_id'] == 30607) {
- $amt = (floor($surplus_num / 50) > $gift['gift_num']) ? $gift['gift_num'] : floor($surplus_num / 50);
- }
- return ($amt < 1) ? 1 : $amt;
- }
-
- /**
- * @use 赠送礼物
- * @param array $value
- * @param int $amt
- */
- protected static function sendGift(array $value, int $amt)
- {
- $url = 'https://api.live.bilibili.com/gift/v2/live/bag_send';
- $payload = [
- 'coin_type' => 'silver',
- 'gift_id' => $value['gift_id'],
- 'ruid' => self::$r_uid,
- 'uid' => self::$uid,
- 'biz_id' => self::$room_id,
- 'gift_num' => $amt,
- 'data_source_id' => '',
- 'data_behavior_id' => '',
- 'bag_id' => $value['bag_id']
- ];
- $data = Curl::post('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::warning('送礼失败!', ['msg' => $data['message']]);
- } else {
- Log::notice("成功向 {$payload['biz_id']} 投喂了 $amt 个{$value['gift_name']}");
- }
- }
-}
diff --git a/src/plugin/GroupSignIn.php b/src/plugin/GroupSignIn.php
deleted file mode 100644
index ee50bdb..0000000
--- a/src/plugin/GroupSignIn.php
+++ /dev/null
@@ -1,109 +0,0 @@
- time() || !getEnable('love_club')) {
- return;
- }
-
- $groups = self::getGroupList();
- if (empty($groups)) {
- self::setLock(self::timing(10));
- return;
- }
-
- foreach ($groups as $group) {
- self::signInGroup($group);
- }
-
- self::setLock(mt_rand(8, 12) * 60 * 60);
- }
-
- /**
- * @use 获取友爱社列表
- * @return array
- */
- protected static function getGroupList(): array
- {
- $url = 'https://api.vc.bilibili.com/link_group/v1/member/my_groups';
- $payload = [];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
-
- if (empty($de_raw['data']['list'])) {
- Log::warning('你没有需要签到的应援团!');
- return [];
- }
- return $de_raw['data']['list'];
- }
-
- /**
- * @use 签到
- * @param array $groupInfo
- * @return bool
- */
- protected static function signInGroup(array $groupInfo): bool
- {
- $url = 'https://api.vc.bilibili.com/link_setting/v1/link_setting/sign_in';
- $payload = [
- 'group_id' => $groupInfo['group_id'],
- 'owner_id' => $groupInfo['owner_uid'],
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
-
- if ($de_raw['code'] != '0') {
- // Todo 任务失败原因
- // {"code": 710001, "msg": "应援失败>_<", "message": "应援失败>_<", "ttl": "1", "data": {"add_num": 0, "status": 0}}
- if ($de_raw['code'] == '710001') {
- Log::warning('在应援团{' . $groupInfo['group_name'] . '}中签到失败, 亲密度已达上限');
- } else {
-// print_r($de_raw);
- Log::warning('在应援团{' . $groupInfo['group_name'] . '}中签到失败, 原因待查');
- }
- return false;
- }
- if ($de_raw['data']['status'] == '0') {
- Log::notice('在应援团{' . $groupInfo['group_name'] . '}中签到成功,增加{' . $de_raw['data']['add_num'] . '点}亲密度');
- } else {
- Log::warning('在应援团{' . $groupInfo['group_name'] . '}中不要重复签到');
- }
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/plugin/GuardRaffle.php b/src/plugin/GuardRaffle.php
deleted file mode 100644
index a99c013..0000000
--- a/src/plugin/GuardRaffle.php
+++ /dev/null
@@ -1,152 +0,0 @@
- $room_id,
- 'raffle_id' => $guard['id'],
- 'raffle_name' => $guard['gift_name'],
- 'wait' => time() + mt_rand(5, 30)
- ];
- Statistics::addPushList($data['raffle_name']);
- self::$wait_list[] = $data;
- }
- return true;
- }
-
- /**
- * @use 创建抽奖任务
- * @param array $raffles
- * @return array
- */
- protected static function createLottery(array $raffles): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v3/guard/join';
- $tasks = [];
- foreach ($raffles as $raffle) {
- $payload = [
- 'id' => $raffle['raffle_id'],
- 'roomid' => $raffle['room_id'],
- "type" => "guard",
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- $tasks[] = [
- 'payload' => Sign::common($payload),
- 'source' => [
- 'room_id' => $raffle['room_id'],
- 'raffle_id' => $raffle['raffle_id'],
- 'raffle_name' => $raffle['raffle_name']
- ]
- ];
- }
- // print_r($results);
- return Curl::async('app', $url, $tasks);
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- foreach ($results as $result) {
- $data = $result['source'];
- $content = $result['content'];
- $de_raw = json_decode($content, true);
- // {"code":-403,"data":null,"message":"访问被拒绝","msg":"访问被拒绝"}
- if (isset($de_raw['code']) && $de_raw['code'] == 0) {
- Statistics::addSuccessList($data['raffle_name']);
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: " . $de_raw['data']['award_name'] . "x" . $de_raw['data']['award_num']);
- Statistics::addProfitList($data['raffle_name'] . '-' . $de_raw['data']['award_name'], $de_raw['data']['award_num']);
- } elseif (isset($de_raw['msg']) && $de_raw['code'] == -403 && $de_raw['msg'] == '访问被拒绝') {
- Log::debug("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: {$de_raw['msg']}");
- self::pauseLock();
- } else {
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: " . isset($de_raw['msg']) ? $de_raw['msg'] : $de_raw);
- }
- }
- return '';
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/Judge.php b/src/plugin/Judge.php
deleted file mode 100644
index e3e982e..0000000
--- a/src/plugin/Judge.php
+++ /dev/null
@@ -1,370 +0,0 @@
- 'https://www.bilibili.com',
- 'referer' => 'https://www.bilibili.com/',
- ];
-
- private static array $wait_case = [];
-
- /**
- * @use run
- */
- public static function run()
- {
- // 基础判断
- if (self::getLock() > time() || !getEnable('judgement')) {
- return;
- }
- // 资格判断 没有资格就60-120分钟后计息 不排除其他错误
- if (!self::jury()) {
- self::setLock(mt_rand(60, 120) * 60);
- }
- // 任务
- if (empty(self::$wait_case)) {
- // 获取
- $case_id = self::caseObtain();
- self::caseCheck($case_id);
- } else {
- // 执行
- $case = array_pop(self::$wait_case);
- self::vote($case['id'], $case['vote']);
- }
- // 如果没有设置时间 就设置个默认时间 可能在一秒钟内处理完 所以 <=
- if (self::getLock() <= time()) {
- self::setLock(mt_rand(15, 30) * 60);
- }
-
- }
-
-
- /**
- * @use 案件核查
- * @param $case_id
- * @return bool|void
- */
- private static function caseCheck($case_id)
- {
- if ($case_id == '') {
- return true;
- }
- $case_info = self::caseInfo($case_id);
- $case_opinion = self::caseOpinion($case_id);
- if (!$case_opinion && empty($case_opinion)) {
-// $vote_info = $case_info[array_rand($case_info)];
- $vote_info = $case_info[self::probability()];
-
- } else {
- $vote_info = $case_opinion[array_rand($case_opinion)];
- }
- $vote = $vote_info['vote'];
- $vote_text = $vote_info['vote_text'];
- Log::info("案件 $case_id 的预测投票结果:$vote($vote_text)");
- self::$wait_case[] = ["id" => $case_id, 'vote' => $vote];
- // 尝试修复25018 未测试
- self::vote($case_id, 0);
-
- self::setLock(60 + 5);
- }
-
- /**
- * @use 投票
- * @param string $case_id
- * @param int $vote
- */
- private static function vote(string $case_id, int $vote)
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/vote';
- $payload = [
- "case_id" => $case_id,
- "vote" => $vote,
- "content" => "",
- "anonymous" => 0,
- "csrf" => getCsrf(),
- ];
- $raw = Curl::post('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1}
- // {"code":25018,"message":"不能进行此操作","ttl":1}
- if (isset($de_raw['code']) && $de_raw['code']) {
- Log::warning("案件 $case_id 投票失败 $raw");
- } else {
- Log::notice("案件 $case_id 投票成功 $raw");
- }
- }
-
- /**
- * @use 申请连任
- */
- private static function juryApply()
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/apply';
- $payload = [
- "csrf" => getCsrf(),
- ];
- $raw = Curl::post('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1}
- if (isset($de_raw['code']) && $de_raw['code']) {
- Log::warning("提交連任申請失敗 $raw");
- } else {
- Log::notice("提交連任申請成功 $raw");
- Notice::push('jury_auto_apply', '提交連任申請成功');
- }
- }
-
-
- /**
- * @use 获取众议观点
- */
- private static function caseOpinion(string $case_id, int $pn = 1, int $ps = 5)
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/case/opinion';
- $payload = [
- 'case_id' => $case_id,
- 'pn' => $pn,
- 'ps' => $ps
- ];
-
- $raw = Curl::get('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"total":438,"list":[]}}
- if (isset($de_raw['code']) && $de_raw['code']) {
- return false;
- } else {
- return $de_raw['data']['list'];
- }
- }
-
-
- /**
- * @use 获取案例详情
- * @param string $case_id
- * @return mixed
- */
- private static function caseInfo(string $case_id): mixed
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/case/info';
- $payload = [
- 'case_id' => $case_id,
- ];
- $raw = Curl::get('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"case_id":"","case_type":1,"vote_items":[{"vote":1,"vote_text":"合适"},{"vote":2,"vote_text":"一般"},{"vote":3,"vote_text":"不合适"},{"vote":4,"vote_text":"无法判断"}],"default_vote":4,"status":0,"origin_start":0,"avid":,"cid":,"vote_cd":5,"case_info":{"comment":{"uname":"用户1","face":"xxxx"},"danmu_img":""}}}
- if (isset($de_raw['code']) && $de_raw['code']) {
- return false;
- } else {
- return $de_raw['data']['vote_items'];
- }
- }
-
- /**
- * @use 获取案件任务
- * @return string
- */
- private static function caseObtain(): string
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/case/next';
- $payload = [];
- $raw = Curl::get('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"case_id":"AC1xx411c7At"}}
- // {"code":25008,"message":"真给力 , 移交众裁的举报案件已经被处理完了","ttl":1}
- // {"code":25014,"message":"25014","ttl":1}
- // {"code":25005,"message":"请成为風機委員后再试","ttl":1}
- // {"code":25006,"message":"風機委員资格已过期","ttl":1}
- if (isset($de_raw['code']) && $de_raw['code']) {
- switch ($de_raw['code']) {
- case 25005:
- Log::warning($de_raw['message']);
- self::setLock(self::timing(10));
- break;
- case 25006:
- Log::warning($de_raw['message']);
- Notice::push('jury_leave_office', $de_raw['message']);
- self::setLock(self::timing(10));
- break;
- case 25008:
- Log::info("暂时没有新的案件需要审理~ $raw");
- break;
- case 25014:
- Log::info("今日案件已审满,感谢您对社区的贡献!明天再来看看吧~");
- self::setLock(self::timing(7, 0, 0, true));
- break;
- default:
- Log::info("获取案件失败~ $raw");
- }
- return '';
- } else {
- $case_id = $de_raw['data']['case_id'];
- Log::info("获取到案例ID $case_id ~");
- return $case_id;
- }
-
- }
-
- /**
- * @use 陪审团
- * @return bool
- */
- private static function jury(): bool
- {
- $url = 'https://api.bilibili.com/x/credit/v2/jury/jury';
- $payload = [];
- $raw = Curl::get('pc', $url, $payload, self::$default_headers);
- $de_raw = json_decode($raw, true);
- // {"code":25005,"message":"请成为風機委員后再试","ttl":1}
- // {"code":0,"message":"0","ttl":1,"data":{"uname":"","face":"http://i2.hdslb.com/bfs/face/.jpg","case_total":,"term_end":,"status":1}}
- if (isset($de_raw['code']) && $de_raw['code']) {
- return false;
- }
-// "status": 1 理论正常
-// "status": 2, 没有资格
-// "apply_status": -1 已经卸任但未连任
-// "apply_status": 0 申请连任后
-// "apply_status": 5 审核连任中
-// "apply_status": 3 连任成功后
-// "apply_status": 4 申请连任失败(?)
- // 理论上正常
- if ($de_raw['data']['status'] == 1) {
- Log::info('你可以參與社區衆裁,共創良好環境哦~');
- return true;
- }
- // 只是嘗試
- if ($de_raw['data']['apply_status'] == -1 && getConf('auto_apply', 'judgement')) {
- self::juryApply();
- }
-
- return false;
-
- }
-
- /**
- * @use 获取案例数据|风纪检测
- * @return bool
- */
- private static function judgementIndex(): bool
- {
- $url = 'https://api.bilibili.com/x/credit/jury/caseList';
- $headers = [
- 'Referer' => "https://www.bilibili.com/judgement/index"
- ];
- $payload = [
- 'callback' => "jQuery1720" . self::randInt() . "_" . time(),
- 'pn' => 1,
- 'ps' => 25,
- '_' => time()
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
-// print_r($de_raw);
- Log::debug($raw);
- $data = $de_raw['data'];
- if (!$data) {
- Log::info('该用户非风纪委成员');
- return false;
- }
- $today = date("Y-m-d");
- $sum_cases = 0;
- $valid_cases = 0;
- $judging_cases = 0;
- foreach ($data as $case) {
- $ts = $case['voteTime'] / 1000;
- $vote_day = date("Y-m-d", $ts);
- if ($vote_day == $today) {
- $sum_cases += 1;
- $vote = $case['vote'];
- if ($vote) {
- $valid_cases += 1;
- } else {
- $judging_cases += 1;
- }
- }
- }
- Log::info("今日投票{$sum_cases}({$valid_cases}票有效(非弃权),{$judging_cases}票还在进行中)");
- return true;
- }
-
- /**
- * @use 随机整数
- * @param int $max
- * @return string
- */
- private static function randInt(int $max = 17): string
- {
- $temp = [];
- foreach (range(1, $max) as $ignored) {
- $temp[] = mt_rand(0, 9);
- }
- return implode("", $temp);
- }
-
- /**
- * @use 概率
- * @return int
- */
- private static function probability(): int
- {
- $result = 0;
- $prize_arr = [0 => 25, 1 => 40, 2 => 25, 3 => 10];
- // 概率数组的总概率精度
- $sum = array_sum($prize_arr);
- // 概率数组循环
- foreach ($prize_arr as $key => $value) {
- if (mt_rand(1, $sum) <= $value) {
- $result = $key;
- break;
- }
- $sum -= $value;
- }
- return $result;
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/Live.php b/src/plugin/Live.php
deleted file mode 100644
index ae67ade..0000000
--- a/src/plugin/Live.php
+++ /dev/null
@@ -1,581 +0,0 @@
- "", 'room_id' => "int|mixed"])]
- public static function areaToRid($area_id): array
- {
- $url = "https://api.live.bilibili.com/room/v1/area/getRoomList";
- $payload = [
- 'platform' => 'web',
- 'parent_area_id' => $area_id,
- 'cate_id' => 0,
- 'area_id' => 0,
- 'sort_type' => 'online',
- 'page' => 1,
- 'page_size' => 30
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- // 防止异常
- if (!isset($de_raw['data']) || $de_raw['code'] || count($de_raw['data']) == 0) {
- Log::warning("获取直播分区异常: " . $de_raw['msg']);
- $area_info = [
- 'area_id' => $area_id,
- 'room_id' => 23058
- ];
- } else {
- $area_info = [
- 'area_id' => $area_id,
- 'room_id' => $de_raw['data'][0]['roomid']
- ];
- }
- return $area_info;
- }
-
- /**
- * @use 获取随机直播房间号
- * @return int
- */
- public static function getUserRecommend(): int
- {
- $url = 'https://api.live.bilibili.com/room/v1/Area/getListByAreaID';
- $payload = [
- 'areaId' => 0,
- 'sort' => 'online',
- 'pageSize' => 30,
- 'page' => 1
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
-// print_r($de_raw);
- if ($de_raw['code'] != '0') {
- return 23058;
- }
- return $de_raw['data'][mt_rand(1, 29)]['roomid'];
- }
-
- /**
- * @use 获取直播房间号
- * @param $room_id
- * @param bool $uid
- * @return mixed
- */
- public static function getRealRoomID($room_id, bool $uid = false): mixed
- {
- $room_infos = [];
- // 缓存开始 如果存在就赋值 否则默认值
- if ($temp = getCache('room_infos')) {
- $room_infos = $temp;
- }
- // 取缓存
- if (isset($room_infos[strval($room_id)])) {
- $data = $room_infos[strval($room_id)];
- } else {
- // 默认数据
- $_data = ['uid' => false, 'room_id' => false];
- // TODO 优化
- $data = self::getRoomInfoV1($room_id);
- if (!isset($data['code']) || !isset($data['data'])) {
- // 访问错误
- $data = $_data;
- } elseif ($data['code']) {
- // 访问错误
- $data = $_data;
- Log::warning($room_id . ' : ' . $data['msg']);
- } elseif ($data['data']['is_hidden']) {
- // 隐藏
- $data = $_data;
- } elseif ($data['data']['is_locked']) {
- // 锁定
- $data = $_data;
- } elseif ($data['data']['encrypted']) {
- // 加密
- $data = $_data;
- } else {
- // 有效
- $data = [
- 'uid' => $data['data']['uid'],
- 'room_id' => $data['data']['room_id'],
- ];
- }
- // 推入缓存前
- $room_infos[strval($room_id)] = $data;
- }
- // 缓存结束 需要的数据的放进缓存
- setCache('room_infos', $room_infos);
- // 如果需要UID
- if ($uid) return $data;
- // 否
- return $data['room_id'];
- }
-
- /**
- * @use 获取直播间信息
- * @param $room_id
- * @return array
- */
- public static function getRoomInfoV1($room_id): array
- {
- $url = 'https://api.live.bilibili.com/room/v1/Room/room_init';
- $payload = [
- 'id' => $room_id
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 获取直播间信息
- * @param $room_id
- * @return array
- */
- public static function getRoomInfoV2($room_id): array
- {
- $url = ' https://api.live.bilibili.com/room/v1/Room/get_info_by_id';
- $payload = [
- 'ids[]' => $room_id
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 获取弹幕配置
- * @param $room_id
- * @return array
- */
- public static function getDanMuConf($room_id): array
- {
- $url = 'https://api.live.bilibili.com/room/v1/Danmu/getConf';
- $payload = [
- 'room_id' => $room_id,
- 'platform' => 'pc',
- 'player' => 'web'
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 获取配置信息
- * @param $room_id
- * @return array
- */
- #[ArrayShape(['addr' => "mixed|string", 'token' => "mixed|string"])]
- public static function getDanMuInfo($room_id): array
- {
- $data = self::getDanMuConf($room_id);
- if (isset($data['data']['host_server_list'][0]['host'])) {
- $server = $data['data']['host_server_list'][0];
- $addr = "tcp://{$server['host']}:{$server['port']}/sub";
- } else {
- $addr = getConf('server_addr', 'zone_monitor');
- }
- return [
- 'addr' => $addr,
- 'token' => $data['data']['token'] ?? '',
- ];
- }
-
- /**
- * @use web端获取直播间信息
- * @param $room_id
- * @return array
- */
- public static function webGetRoomInfo($room_id): array
- {
- $url = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom';
- $payload = [
- 'room_id' => $room_id
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 钓鱼检测
- * @param $room_id
- * @return bool
- */
- public static function fishingDetection($room_id): bool
- {
- if (self::getRealRoomID($room_id)) {
- return false;
- }
- return true;
- }
-
- /**
- * @use 访问直播间
- * @param $room_id
- * @return bool
- */
- public static function goToRoom($room_id): bool
- {
- $url = 'https://api.live.bilibili.com/room/v1/Room/room_entry_action';
- $payload = [
- 'room_id' => $room_id,
- ];
- // Log::info('进入直播间[' . $room_id . ']抽奖!');
- Curl::post('app', $url, Sign::common($payload));
- return true;
- }
-
- /**
- * @use 发送弹幕pc
- * @param int $room_id
- * @param string $content
- * @return array
- */
- public static function sendBarragePC(int $room_id, string $content): array
- {
- $room_id = self::getRealRoomID($room_id);
- if (!$room_id) {
- return ['code' => 404, 'message' => '直播间数据异常'];
- }
- $url = 'https://api.live.bilibili.com/msg/send';
- $payload = [
- 'color' => '16777215',
- 'fontsize' => 25,
- 'mode' => 1,
- 'msg' => $content,
- 'rnd' => 0,
- 'bubble' => 0,
- 'roomid' => $room_id,
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf(),
- ];
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => "https://live.bilibili.com/$room_id"
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"data":[],"message":"","msg":""}
- return json_decode($raw, true) ?? ['code' => 404, 'msg' => '上层数据为空!'];
- }
-
- /**
- * @use 发送弹幕app
- * @param int $room_id
- * @param string $content
- * @return array
- */
- public static function sendBarrageAPP(int $room_id, string $content): array
- {
- $room_id = self::getRealRoomID($room_id);
- if (!$room_id) {
- return ['code' => 404, 'message' => '直播间数据异常'];
- }
- $url = 'https://api.live.bilibili.com/msg/send';
- $payload = [
- 'color' => '16777215',
- 'fontsize' => 25,
- 'mode' => 1,
- 'msg' => $content,
- 'rnd' => 0,
- 'roomid' => $room_id,
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf(),
- ];
- $raw = Curl::post('app', $url, Sign::common($payload));
- return json_decode($raw, true) ?? ['code' => 404, 'msg' => '上层数据为空!'];
- }
-
- /**
- * @use 获取勋章列表
- * @param int $page_size
- * @return array
- */
- public static function fetchMedalList(int $page_size = 50): array
- {
- $metal_list = [];
- for ($i = 1; $i <= 100; $i++) {
- // https://live.bilibili.com/p/html/live-app-fansmedal-manange/index.html
- $url = 'https://api.live.bilibili.com/xlive/app-ucenter/v1/fansMedal/panel';
- $payload = [
- 'page' => $i,
- 'page_size' => $page_size
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"list":[],"special_list":[],"bottom_bar":null,"page_info":{"number":0,"current_page":1,"has_more":false,"next_page":2,"next_light_status":2},"total_number":0,"has_medal":0}}
- if (isset($data['code']) && $data['code']) {
- Log::warning('获取勋章列表失败!', ['msg' => $data['message']]);
- return $metal_list;
- }
- // list special_list
- $keys = ['list', 'special_list'];
- foreach ($keys as $key) {
- if (isset($de_raw['data'][$key])) {
- foreach ($de_raw['data'][$key] as $vo) {
- // 部分主站勋章没有直播间
- if (isset($vo['room_info']['room_id'])) {
- $vo['medal']['roomid'] = $vo['room_info']['room_id'];
- } else {
- $vo['medal']['roomid'] = 0;
- }
- $metal_list[] = $vo['medal'];
- }
- }
- }
- // total_number || count == 0
- if (count($metal_list) >= $de_raw['data']['total_number'] || empty($metal_list)) {
- break;
- }
- }
- // count == 0
- if (!empty($metal_list)) {
- $num = count($metal_list);
- Log::info("勋章列表获取成功, 共获取到 $num 个!");
- }
- return $metal_list;
- }
-
- /**
- * @use 背包获取单项礼物
- * @param string $gift_name
- * @param int $gift_id
- * @return array
- */
- public static function fetchBagListByGift(string $gift_name, int $gift_id): array
- {
- $new_bag_list = [];
- $payload = [];
- $url = 'https://api.live.bilibili.com/gift/v2/gift/bag_list';
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::warning('背包查看失败!', ['msg' => $data['message']]);
- return $new_bag_list;
- }
- if (isset($data['data']['list'])) {
- $bag_list = $data['data']['list'];
- if (count($bag_list)) {
- // 按过期时间 升序
- // array_multisort(array_column($bag_list, "gift_id"), SORT_DESC, $bag_list);
- array_multisort(array_column($bag_list, "expire_at"), SORT_ASC, $bag_list);
- }
- foreach ($bag_list as $vo) {
- // 去除永久礼物
- if ($vo['corner_mark'] == '永久') continue;
- if ($vo['gift_id'] == $gift_id && $vo['gift_name'] == $gift_name) {
- $new_bag_list[] = $vo;
- }
- }
- }
- return $new_bag_list;
- }
-
- /**
- * @use 赠送礼物
- * @param array $guest // 用户信息
- * @param array $gift // 礼物信息
- * @param int $num // 数量
- */
- public static function sendGift(array $guest, array $gift, int $num)
- {
- $url = 'https://api.live.bilibili.com/gift/v2/live/bag_send';
- $payload = [
- 'uid' => getUid(), // 自己的UID
- 'gift_id' => $gift['gift_id'],
- 'ruid' => $guest['uid'], // UP的UID
- 'send_ruid' => 0,
- 'gift_num' => $num,
- 'bag_id' => $gift['bag_id'],
- 'platform' => 'pc',
- 'biz_code' => 'live',
- 'biz_id' => $guest['roomid'], // UP的直播间
- 'rnd' => time(), // 时间戳
- 'storm_beat_id' => 0,
- 'metadata' => '',
- 'price' => 0,
- 'csrf' => getCsrf(),
- 'csrf_token' => getCsrf()
- ];
- // {"code":0,"msg":"success","message":"success","data":{"tid":"1595419985112400002","uid":4133274,"uname":"沙奈之朵","face":"https://i2.hdslb.com/bfs/face/eb101ef90ebc4e9bf79f65312a22ebac84946700.jpg","guard_level":0,"ruid":893213,"rcost":30834251,"gift_id":30607,"gift_type":5,"gift_name":"小心心","gift_num":1,"gift_action":"投喂","gift_price":5000,"coin_type":"silver","total_coin":5000,"pay_coin":5000,"metadata":"","fulltext":"","rnd":"1595419967","tag_image":"","effect_block":1,"extra":{"wallet":null,"gift_bag":{"bag_id":210196588,"gift_num":20},"top_list":[],"follow":null,"medal":null,"title":null,"pk":{"pk_gift_tips":"","crit_prob":0},"fulltext":"","event":{"event_score":0,"event_redbag_num":0},"capsule":null},"blow_switch":0,"send_tips":"赠送成功","gift_effect":{"super":0,"combo_timeout":0,"super_gift_num":0,"super_batch_gift_num":0,"batch_combo_id":"","broadcast_msg_list":[],"small_tv_list":[],"beat_storm":null,"smallTVCountFlag":true},"send_master":null,"crit_prob":0,"combo_stay_time":3,"combo_total_coin":0,"demarcation":2,"magnification":1,"combo_resources_id":1,"is_special_batch":0,"send_gift_countdown":6}}
- $data = Curl::post('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::warning('送礼失败!', ['msg' => $data['message']]);
- } else {
- Log::notice("成功向 {$payload['biz_id']} 投喂了 $num 个{$gift['gift_name']}");
- }
- }
-
- /**
- * @use 获取分区直播间
- * @param int $parent_area_id
- * @param int $area_id
- * @param int $page
- * @return array
- */
- public static function getAreaRoomList(int $parent_area_id, int $area_id, int $page = 1): array
- {
- $url = 'https://api.live.bilibili.com/xlive/web-interface/v1/second/getList';
- $payload = [
- 'platform' => 'web',
- 'parent_area_id' => $parent_area_id,
- 'area_id' => $area_id,
- 'sort_type' => 'online',
- 'page' => $page
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- $room_ids = [];
-
- if ($de_raw['code'] == 0) {
- foreach ($de_raw['data']['list'] as $room) {
- $room_ids[] = $room['roomid'];
- }
- }
- return $room_ids;
- }
-
- /**
- * @use 获取用户卡片
- * @param int $mid
- * @return array
- */
- public static function getMidCard(int $mid): array
- {
- $url = 'https://api.bilibili.com/x/web-interface/card';
- $payload = [
- 'mid' => $mid,
- ];
- //{"code":0,"message":"0","ttl":1,"data":{"card":{"mid":"1","name":"bishi","approve":false,"sex":"男","rank":"10000","face":"http://i1.hdslb.com/bfs/face/34c5b30a990c7ce4a809626d8153fa7895ec7b63.gif","DisplayRank":"0","regtime":0,"spacesta":0,"birthday":"","place":"","description":"","article":0,"attentions":[],"fans":154167,"friend":5,"attention":5,"sign":"","level_info":{"current_level":4,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0,"image_enhance":"","image_enhance_frame":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""},"Official":{"role":0,"title":"","desc":"","type":-1},"official_verify":{"type":-1,"desc":""},"vip":{"type":2,"status":1,"due_date":1727625600000,"vip_pay_type":1,"theme_type":0,"label":{"path":"","text":"年度大会员","label_theme":"annual_vip","text_color":"#FFFFFF","bg_style":1,"bg_color":"#FB7299","border_color":""},"avatar_subscript":1,"nickname_color":"#FB7299","role":3,"avatar_subscript_url":"http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png","vipType":2,"vipStatus":1}},"following":false,"archive_count":2,"article_count":0,"follower":154167}}
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 获取用户状态
- * @param int $mid
- * @return array
- */
- public static function getMidStat(int $mid): array
- {
- $url = 'https://api.bilibili.com/x/relation/stat';
- $payload = [
- 'vmid' => $mid,
- ];
- // {"code":0,"message":"0","ttl":1,"data":{"mid":50329118,"following":62,"whisper":0,"black":0,"follower":7610241}}
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
- /**
- * @use 直播间抽奖检查
- * @param int $room_id
- * @return array
- */
- public static function getLotteryCheck(int $room_id): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/Check';
- $payload = [
- 'roomid' => $room_id,
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
-
- /**
- * @use 获取直播间抽奖信息
- * @param int $room_id
- * @return array
- */
- public static function getLotteryInfoWeb(int $room_id): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/getLotteryInfoWeb';
- $payload = [
- 'roomid' => $room_id,
- ];
- $raw = Curl::get('other', $url, $payload);
- return json_decode($raw, true);
- }
-
-
- /**
- * @use 获取用户关注数
- * @param int $mid
- * @return int
- */
- public static function getMidFollower(int $mid): int
- {
- $follower = 0;
- // root->data->follower
- if (mt_rand(0, 10) > 5) {
- $data = self::getMidStat($mid);
- } else {
- $data = self::getMidCard($mid);
- }
-
- if (isset($data['code']) && $data['code']) {
- Log::warning("获取用户资料卡片失败: CODE -> {$data['code']} MSG -> {$data['message']} ");
- } else {
- // root->data->follower
- $follower = $data['data']['follower'];
- }
- return $follower;
- }
-}
diff --git a/src/plugin/LiveReservation.php b/src/plugin/LiveReservation.php
deleted file mode 100644
index f9e8490..0000000
--- a/src/plugin/LiveReservation.php
+++ /dev/null
@@ -1,153 +0,0 @@
- time() || !getEnable('live_reservation')) {
- return;
- }
- if (getConf('vmids', 'live_reservation') == "") {
- return;
- }
- $vmids = explode(',', getConf('vmids', 'live_reservation'));
- // 获取目标列表->获取预约列表->执行预约列表
- foreach ($vmids as $vmid) {
- $reservation_list = self::fetchReservation($vmid);
- foreach ($reservation_list as $reservation) {
- self::reserve($reservation);
- }
- }
- // 1-3小时
- self::setLock(mt_rand(1, 3) * 60 * 60);
- }
-
- /**
- * @use 尝试预约并抽奖
- * @param array $data
- */
- private static function reserve(array $data)
- {
- $url = 'https://api.bilibili.com/x/space/reserve';
- $headers = [
- 'content-type' => 'application/x-www-form-urlencoded',
- 'origin' => 'https://space.bilibili.com',
- 'referer' => "https://space.bilibili.com/{$data['vmid']}"
- ];
- $payload = [
- 'sid' => $data['sid'],
- 'jsonp' => 'jsonp',
- 'csrf' => getCsrf()
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1}
- Log::info($data['name'] . '|' . $data['vmid'] . '|' . $data['sid']);
- Log::info($data['text']);
- Log::info($data['jump_url']);
-
- if (!$de_raw['code']) {
- Log::notice("尝试预约并抽奖成功: {$de_raw['message']}");
- } else {
- Log::warning("尝试预约并抽奖失败: $raw");
- }
-
- }
-
-
- /**
- * @use 获取预约列表
- * @param string $vmid
- * @return array
- */
- private static function fetchReservation(string $vmid): array
- {
- $reservation_list = [];
-
- $url = 'https://api.bilibili.com/x/space/reservation';
- $payload = [
- 'vmid' => $vmid,
- ];
- $headers = [
- 'origin' => 'https://space.bilibili.com',
- 'referer' => "https://space.bilibili.com/$vmid/"
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":[{"sid":253672,"name":"直播预约:创世之音-虚拟偶像演唱会","total":6382,"stime":1636716437,"etime":1637408100,"is_follow":1,"state":100,"oid":"","type":2,"up_mid":9617619,"reserve_record_ctime":1636731801,"live_plan_start_time":1637406000,"lottery_type":1,"lottery_prize_info":{"text":"预约有奖:小电视年糕抱枕、哔哩哔哩小电视樱花毛绒抱枕大号、哔哩哔哩小夜灯","lottery_icon":"https://i0.hdslb.com/bfs/activity-plat/static/ce06d65bc0a8d8aa2a463747ce2a4752/rgHplMQyiX.png","jump_url":"https://www.bilibili.com/h5/lottery/result?business_id=253672\u0026business_type=10\u0026lottery_id=76240"},"show_total":true,"subtitle":""},{"sid":246469,"name":"直播预约:创世之音-YuNi个人演唱会","total":3555,"stime":1636367836,"etime":1637494500,"is_follow":0,"state":100,"oid":"","type":2,"up_mid":9617619,"reserve_record_ctime":0,"live_plan_start_time":1637492400,"show_total":true,"subtitle":""}]}
- if (!$de_raw['code']) {
- // data == NULL
- $de_data = $de_raw['data'] ?: [];
- foreach ($de_data as $data) {
- $result = self::checkLottery($data);
- if (!$result) continue;
- $reservation_list[] = $result;
- }
- } else {
- Log::warning("获取预约列表失败: $raw");
- }
- return $reservation_list;
- }
-
-
- /**
- * @use 检测有效抽奖
- * @param array $data
- * @return bool|array
- */
- private static function checkLottery(array $data): bool|array
- {
- // 已经过了有效时间
- if ($data['etime'] <= time()) {
- return false;
- }
- // 已经预约过了
- if ($data['is_follow']) {
- return false;
- }
- // 有预约抽奖
- if (array_key_exists('lottery_prize_info', $data) && array_key_exists('lottery_type', $data)) {
- return [
- 'sid' => $data['sid'], // 246469
- 'name' => $data['name'], // "直播预约:创世之音-虚拟偶像演唱会"
- 'vmid' => $data['up_mid'], // 9617619
- 'jump_url' => $data['lottery_prize_info']['jump_url'], // "https://www.bilibili.com/h5/lottery/result?business_id=253672&business_type=10&lottery_id=76240"
- 'text' => $data['lottery_prize_info']['text'], // "预约有奖:小电视年糕抱枕、哔哩哔哩小电视樱花毛绒抱枕大号、哔哩哔哩小夜灯"
- ];
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/plugin/Login.php b/src/plugin/Login.php
deleted file mode 100644
index f092cef..0000000
--- a/src/plugin/Login.php
+++ /dev/null
@@ -1,554 +0,0 @@
- time()) {
- return true;
- }
- self::setLock(7200);
- if (!self::checkToken()) {
- Log::warning('令牌即将过期');
- Log::info('申请更换令牌中...');
- if (!self::refreshToken()) {
- Log::warning('无效令牌,正在重新申请...');
- self::accountLogin();
- }
- return false;
- }
- return true;
- }
-
- /**
- * @use 获取令牌信息
- * @return bool
- */
- private static function checkToken(): bool
- {
- $url = 'https://passport.bilibili.com/api/v2/oauth2/info';
- $payload = [
- 'access_token' => getAccessToken(),
- ];
- $data = Curl::get('app', $url, Sign::common($payload));
- // {"ts":1234,"code":0,"data":{"mid":1234,"access_token":"1234","expires_in":7759292}}
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::error('检查令牌失败', ['msg' => $data['message']]);
- return false;
- }
- Log::notice('令牌有效期: ' . date('Y-m-d H:i:s', $data['ts'] + $data['data']['expires_in']));
- return $data['data']['expires_in'] > 14400;
- }
-
- /**
- * @use 刷新Token
- * @throws \Jelix\IniFile\IniException
- */
- private static function refreshToken(): bool
- {
- $url = 'https://passport.bilibili.com/api/v2/oauth2/refresh_token';
- $payload = [
- 'access_token' => getAccessToken(),
- 'refresh_token' => getRefreshToken(),
- ];
- $raw = Curl::post('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- // {"message":"user not login","ts":1593111694,"code":-101}
- if (isset($de_raw['code']) && $de_raw['code']) {
- Log::error('重新生成令牌失败', ['msg' => $de_raw['message']]);
- return false;
- }
- self::refreshSuccess($de_raw);
- return true;
- }
-
- /**
- * @use 公钥加密
- * @param $plaintext
- * @return string
- */
- private static function publicKeyEnc($plaintext): string
- {
- Log::info('正在载入公钥');
- // $url = 'https://passport.bilibili.com/api/oauth2/getKey';
- $url = 'https://passport.bilibili.com/x/passport-login/web/key';
- $payload = [];
- $data = Curl::get('app', $url, Sign::login($payload));
- $data = json_decode($data, true);
- if (isset($data['code']) && $data['code']) {
- Log::error('公钥载入失败', ['msg' => $data['message']]);
- die();
- } else {
- Log::info('公钥载入完毕');
- }
- // print_r($data);
- $public_key = $data['data']['key'];
- $hash = $data['data']['hash'];
- openssl_public_encrypt($hash . $plaintext, $crypt, $public_key);
- return base64_encode($crypt);
- }
-
- /**
- * @use 获取验证码
- * @return array
- */
- private static function getCaptcha(): array
- {
- $url = 'https://passport.bilibili.com/web/captcha/combine';
- $payload = [
- 'plat' => 3
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- // {"code":0,"data":{"result":{"success":1,"gt":"b6e5b7fad7ecd37f465838689732e788","challenge":"88148a764f94e5923564b356a69277fc","key":"230509df5ce048ca9bf29e1ee323af30"},"type":1}}
- Log::info('正在获取验证码 ' . $de_raw['code']);
- if ($de_raw['code'] == 0 && isset($de_raw['data']['result'])) {
- return [
- 'gt' => $de_raw['data']['result']['gt'],
- 'challenge' => $de_raw['data']['result']['challenge'],
- 'key' => $de_raw['data']['result']['key'],
- ];
- }
- return [
- 'gt' => '',
- 'challenge' => '',
- 'key' => ''
- ];
- }
-
- /**
- * @use 识别验证码
- * @param array $captcha
- * @return array
- */
- #[ArrayShape(['validate' => "mixed", 'challenge' => "mixed"])]
- private static function ocrCaptcha(array $captcha): array
- {
- $url = 'https://captcha-v1.mudew.com:19951/';
- $payload = [
- 'type' => 'gt3',
- 'gt' => $captcha['gt'],
- "challenge" => $captcha['challenge'],
- "referer" => "https://passport.bilibili.com/"
- ];
- $headers = [
- 'Content-Type' => 'application/json',
- ];
- $data = Curl::post('other', $url, $payload, $headers);
- $de_raw = json_decode($data, true);
- Log::info('正在获取验证码 ' . $de_raw['code']);
- return [
- 'validate' => $de_raw['data']['validate'],
- 'challenge' => $de_raw['data']['challenge']
- ];
- }
-
- /**
- * @use 账密登录
- * @param string $validate
- * @param string $challenge
- * @param string $mode
- * @throws \Jelix\IniFile\IniException
- */
- private static function accountLogin(string $validate = '', string $challenge = '', string $mode = '账密模式')
- {
- Log::info("尝试 $mode 登录");
- // $url = 'https://passport.bilibili.com/api/v3/oauth2/login';
- $url = 'https://passport.bilibili.com/x/passport-login/oauth2/login';
- $payload = [
- 'seccode' => $validate ? "$validate|jordan" : '',
- 'validate' => $validate,
- 'challenge' => $challenge,
- 'permission' => 'ALL',
- 'username' => self::$username,
- 'password' => self::$password,
- 'captcha' => '',
- 'subid' => 1,
- 'cookies' => ''
- ];
- $raw = Curl::post('app', $url, Sign::login($payload));
- $de_raw = json_decode($raw, true);
- // {"ts":1593079322,"code":-629,"message":"账号或者密码错误"}
- // {"ts":1593082268,"code":-105,"data":{"url":"https://passport.bilibili.com/register/verification.html?success=1>=b6e5b7fad7ecd37f465838689732e788&challenge=7efb4020b22c0a9ac124aea624e11ad7&ct=1&hash=7fa8282ad93047a4d6fe6111c93b308a"},"message":"验证码错误"}
- // {"ts":1593082432,"code":0,"data":{"status":0,"token_info":{"mid":123456,"access_token":"123123","refresh_token":"123123","expires_in":2592000},"cookie_info":{"cookies":[{"name":"bili_jct","value":"123123","http_only":0,"expires":1595674432},{"name":"DedeUserID","value":"123456","http_only":0,"expires":1595674432},{"name":"DedeUserID__ckMd5","value":"123123","http_only":0,"expires":1595674432},{"name":"sid","value":"bd6aagp7","http_only":0,"expires":1595674432},{"name":"SESSDATA","value":"6d74d850%123%2Cf0e36b61","http_only":1,"expires":1595674432}],"domains":[".bilibili.com",".biligame.com",".bigfunapp.cn"]},"sso":["https://passport.bilibili.com/api/v2/sso","https://passport.biligame.com/api/v2/sso","https://passport.bigfunapp.cn/api/v2/sso"]}}
- // {"ts":1610254019,"code":0,"data":{"status":2,"url":"https://passport.bilibili.com/account/mobile/security/managephone/phone/verify?tmp_token=2bc5dd260df7158xx860565fxx0d5311&requestId=dffcfxx052fe11xxa9c8e2667739c15c&source=risk","message":"您的账号存在高危异常行为,为了您的账号安全,请验证手机号后登录帐号"}}
- // https://passport.bilibili.com/mobile/verifytel_h5.html
- self::loginAfter($mode, $de_raw['code'], $de_raw);
- }
-
- /**
- * @use 短信登录
- * @param string $mode
- * @throws \Jelix\IniFile\IniException
- */
- private static function smsLogin(string $mode = '短信模式')
- {
- Log::info("尝试 $mode 登录");
- if (getConf('phone', 'login.check')) {
- self::checkPhone(self::$username);
- }
- $captcha = self::sendSms(self::$username);
- $url = 'https://passport.bilibili.com/x/passport-login/login/sms';
- $payload = [
- 'captcha_key' => $captcha['captcha_key'],
- 'cid' => $captcha['cid'],
- 'tel' => $captcha['tel'],
- 'statistics' => $captcha['statistics'],
- 'code' => self::cliInput('请输入收到的短信验证码: '),
- ];
- $raw = Curl::post('app', $url, Sign::login($payload));
- $de_raw = json_decode($raw, true);
- self::loginAfter($mode, $de_raw['code'], $de_raw);
- }
-
- /**
- * @use 输入短信验证码
- * @param string $msg
- * @param int $max_char
- * @return string
- */
- private static function cliInput(string $msg, int $max_char = 100): string
- {
- $stdin = fopen('php://stdin', 'r');
- echo '# ' . $msg;
- $input = fread($stdin, $max_char);
- fclose($stdin);
- return str_replace(PHP_EOL, '', $input);
- }
-
- /**
- * @use 发送短信验证码
- * @param string $phone
- * @return array
- */
- #[ArrayShape(['cid' => "mixed", 'tel' => "string", 'statistics' => "string", 'captcha_key' => "mixed"])]
- private static function sendSms(string $phone): array
- {
- $url = 'https://passport.bilibili.com//x/passport-login/sms/send';
- $payload = [
- 'cid' => getConf('country_code', 'login.country'),
- 'tel' => $phone,
- 'statistics' => '{"appId":1,"platform":3,"version":"6.32.0","abtest":""}',
- ];
- $raw = Curl::post('app', $url, Sign::login($payload));
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"4e292933816755442c1568e2043b8e41","recaptcha_url":""}}
- // {"code":0,"message":"0","ttl":1,"data":{"is_new":false,"captcha_key":"","recaptcha_url":"https://www.bilibili.com/h5/project-msg-auth/verify?ct=geetest\u0026recaptcha_token=ad520c3a4a3c46e29b1974d85efd2c4b\u0026gee_gt=1c0ea7c7d47d8126dda19ee3431a5f38\u0026gee_challenge=c772673050dce482b9f63ff45b681ceb\u0026hash=ea2850a43cc6b4f1f7b925d601098e5e"}}
- if ($de_raw['code'] == 0 && isset($de_raw['data']['captcha_key']) && $de_raw['data']['recaptcha_url'] == '') {
- Log::info("短信验证码发送成功 {$de_raw['data']['captcha_key']}");
- $payload['captcha_key'] = $de_raw['data']['captcha_key'];
- return $payload;
- }
- Log::error("短信验证码发送失败 $raw");
- die();
- }
-
- /**
- * @use 登录之后
- * @param $mode
- * @param $code
- * @param $data
- * @throws \Jelix\IniFile\IniException
- */
- private static function loginAfter($mode, $code, $data)
- {
- switch ($code) {
- case 0:
- // data->data->status number
- if (array_key_exists('status', $data['data'])) {
- // 二次判断
- switch ($data['data']['status']) {
- case 0:
- // 正常登录
- self::loginSuccess($mode, $data);
- break;
- case 2:
- // 异常高危
- self::loginFail($mode, $data['data']['message']);
- case 3:
- // 需要验证手机号
- self::loginFail($mode, "需要验证手机号: {$data['data']['url']}");
- default:
- // 未知错误
- self::loginFail($mode, '未知错误: ' . json_encode($data));
- }
- } else {
- // 正常登录
- self::loginSuccess($mode, $data);
- }
- break;
- case -105:
- // 需要验证码
- self::loginFail($mode, '此次登录需要验证码或' . $data['message']);
- case -629:
- // 密码错误
- self::loginFail($mode, $data['message']);
- case -2100:
- // 验证手机号
- self::loginFail($mode, '账号启用了设备锁或异地登录需验证手机号');
- default:
- // 未知错误
- self::loginFail($mode, '未知错误: ' . $data['message']);
- }
-
- }
-
- /**
- * @use 登录成功
- * @param $mode
- * @param $data
- * @throws \Jelix\IniFile\IniException
- */
- private static function loginSuccess($mode, $data)
- {
- Log::info("$mode 登录成功");
- self::successHandle($data);
- Log::info('生成信息配置完毕');
- }
-
- /**
- * @use 刷新成功
- * @param $data
- * @throws \Jelix\IniFile\IniException
- */
- private static function refreshSuccess($data)
- {
- Log::info('重新令牌生成完毕');
- self::successHandle($data);
- Log::info('重置信息配置完毕');
- }
-
- /**
- * @use 成功处理
- * @param $data
- * @throws \Jelix\IniFile\IniException
- */
- private static function successHandle($data)
- {
- $access_token = $data['data']['token_info']['access_token'];
- $refresh_token = $data['data']['token_info']['refresh_token'];
- self::saveConfig('access_token', $access_token, 'login.auth');
- self::saveConfig('refresh_token', $refresh_token, 'login.auth');
- self::saveConfig('cookie', self::formatCookie($data), 'login.auth');
- $user = User::parseCookies();
- self::saveConfig('uid', $user['uid'], 'login.auth', false);
- self::saveConfig('csrf', $user['csrf'], 'login.auth', false);
- }
-
- /**
- * @use 登录失败
- * @param $mode
- * @param $data
- */
- #[NoReturn]
- private static function loginFail($mode, $data)
- {
- Log::error("$mode 登录失败", ['msg' => $data]);
- die();
- }
-
- /**
- * @use 检查手机号格式
- * @param string $phone
- */
- private static function checkPhone(string $phone)
- {
- // /^1[3456789]{1}\d{9}$/
- if (!preg_match("/^1[3456789]\d{9}$/", $phone)) {
- Log::error("当前用户名不是有效手机号格式");
- die();
- }
- }
-
- /**
- * @use 保存配置
- * @param string $key
- * @param string $value
- * @param string $section
- * @param bool $print
- * @param bool $hide
- * @throws \Jelix\IniFile\IniException
- */
- private static function saveConfig(string $key, string $value, string $section, bool $print = true, bool $hide = true)
- {
- setConf($key, $value, $section);
- if ($print) {
- Log::info(" > $key: " . ($hide ? Common::replaceStar($value, 6, 6) : $value));
- }
- }
-
- /**
- * @use @use 格式化COOKIE
- * @param array $data
- * @return string
- */
- private static function formatCookie(array $data): string
- {
- $c = '';
- $cookies = $data['data']['cookie_info']['cookies'];
- foreach ($cookies as $cookie) {
- $c .= $cookie['name'] . '=' . $cookie['value'] . ';';
- }
- return $c;
- }
-
- /**
- * @use 清除已有
- * @throws \Jelix\IniFile\IniException
- */
- private static function clearAccount()
- {
- $variables = ['cookie', 'access_token', 'refresh_token'];
- foreach ($variables as $variable) {
- setConf($variable, '', 'login.auth');
- }
- }
-
- /**
- * @use 刷新COOKIE
- * @return string
- */
- private static function refreshCookie(): string
- {
- $url = 'https://passport.bilibili.com/api/login/sso';
- $payload = [
- 'gourl' => 'https%3A%2F%2Faccount.bilibili.com%2Faccount%2Fhome'
- ];
- $response = Curl::headers('app', $url, Sign::common($payload));
- $headers = $response['Set-Cookie'];
- $cookies = [];
- foreach ($headers as $header) {
- preg_match_all('/^(.*);/iU', $header, $cookie);
- $cookies[] = $cookie[0][0];
- }
- return implode("", array_reverse($cookies));
- }
-
- /**
- * @use 验证码登录
- * @param string $mode
- * @throws \Jelix\IniFile\IniException
- */
- private static function captchaLogin(string $mode = '验证码模式')
- {
- $captcha_ori = self::getCaptcha();
- $captcha = self::ocrCaptcha($captcha_ori);
- self::accountLogin($captcha['validate'], $captcha['challenge'], $mode);
- }
-
-
-}
\ No newline at end of file
diff --git a/src/plugin/MainSite.php b/src/plugin/MainSite.php
deleted file mode 100644
index 1a8dc08..0000000
--- a/src/plugin/MainSite.php
+++ /dev/null
@@ -1,433 +0,0 @@
- time() || !getEnable('main_site')) {
- return;
- }
- if (self::watchAid() && self::shareAid() && self::coinAdd()) {
- self::setLock(self::timing(10));
- return;
- }
- self::setLock(3600);
- }
-
- /**
- * @use 投币
- * @param $aid
- * @return bool
- */
- private static function reward($aid): bool
- {
- $url = "https://api.bilibili.com/x/web-interface/coin/add";
- $payload = [
- "aid" => $aid,
- "multiply" => "1",
- "cross_domain" => "true",
- "csrf" => getCsrf()
- ];
- $headers = [
- 'Host' => "api.bilibili.com",
- 'Origin' => "https://www.bilibili.com",
- 'Referer' => "https://www.bilibili.com/video/av$aid",
- 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
- ];
- // {"code":34005,"message":"超过投币上限啦~","ttl":1,"data":{"like":false}}
- // {"code":0,"message":"0","ttl":1,"data":{"like":false}}
- // CODE -> 137001 MSG -> 账号封禁中,无法完成操作
- // CODE -> -650 MSG -> 用户等级太低
-
- $raw = Curl::post('app', $url, Sign::common($payload), $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice("主站任务: av$aid 投币成功 {$de_raw['code']} MSG -> {$de_raw['message']}");
- return true;
- } else {
- Log::warning("主站任务: av$aid 投币失败 CODE -> {$de_raw['code']} MSG -> {$de_raw['message']}");
- return false;
- }
- }
-
- /**
- * @use 投币日志
- * @return int
- */
- protected static function coinLog(): int
- {
- $url = "https://api.bilibili.com/x/member/web/coin/log";
- $payload = [];
- $raw = Curl::get('pc', $url, $payload);
- $de_raw = json_decode($raw, true);
-
- $logs = $de_raw['data']['list'] ?? [];
- $coins = 0;
- foreach ($logs as $log) {
- $log_ux = strtotime($log['time']);
- $log_date = date('Y-m-d', $log_ux);
- $now_date = date('Y-m-d');
- if ($log_date != $now_date) {
- break;
- }
- if (str_contains($log['reason'], "打赏")) {
- switch ($log['delta']) {
- case -1:
- $coins += 1;
- break;
- case -2:
- $coins += 2;
- break;
- default:
- break;
- }
- }
- }
- return $coins;
- }
-
- /**
- * @use 视频投币
- * @return bool
- */
- protected static function coinAdd(): bool
- {
- if (!getConf('add_coin', 'main_site')) return true;
-
- // 预计数量 失败默认0 避免损失
- $estimate_num = getConf('add_coin_num', 'main_site') ?? 0;
- // 库存数量
- $stock_num = self::getCoin();
- // 实际数量 处理硬币库存少于预计数量
- $actual_num = intval(min($estimate_num, $stock_num)) - self::coinLog();
- Log::info("当前硬币库存 $stock_num 预计投币 $estimate_num 实际投币 $actual_num");
- // 上限
- if ($actual_num <= 0) {
- Log::notice('今日投币上限已满');
- return true;
- }
- // 稿件列表
- if (getConf('add_coin_mode', 'main_site') == 'random') {
- // 随机热门稿件榜单
- $aids = self::getTopRCmdAids($actual_num);
- } else {
- // 固定获取关注UP稿件榜单, 不足会随机补全
- $aids = self::getFollowUpAids($actual_num);
- }
- Log::info("获取稿件列表: " . implode(" ", $aids));
- // 投币
- foreach ($aids as $aid) {
- self::reward($aid);
- }
- return true;
- }
-
- /**
- * @use 获取随机AID
- * @return string
- */
- private static function getRandomAid(): string
- {
- do {
- $url = "https://api.bilibili.com/x/web-interface/newlist";
- $payload = [
- 'pn' => mt_rand(1, 1000),
- 'ps' => 1,
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- // echo "getRandomAid " . count($de_raw['data']['archives']) . PHP_EOL;
- // $aid = array_rand($de_raw['data']['archives'])['aid'];
- } while (count((array)$de_raw['data']['archives']) == 0);
- $aid = $de_raw['data']['archives'][0]['aid'];
- return (string)$aid;
- }
-
- /**
- * @use 获取关注UP稿件列表
- * @param int $num
- * @return array
- */
- private static function getFollowUpAids(int $num): array
- {
- $aids = [];
- $url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new';
- $payload = [
- 'uid' => getUid(),
- 'type_list' => '8,512,4097,4098,4099,4100,4101'
- ];
- $headers = [
- 'origin' => 'https://t.bilibili.com',
- 'referer' => 'https://t.bilibili.com/pages/nav/index_new'
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- foreach ($de_raw['data']['cards'] as $index => $card) {
- if ($index >= $num) {
- break;
- }
- $aids[] = $card['desc']['rid'];
- }
- // 此处补全缺失
- if (count($aids) < $num) {
- $aids = array_merge($aids, self::getTopRCmdAids($num - count($aids)));
- }
- return $aids;
- }
-
- /**
- * @use 获取榜单稿件列表
- * @param int $num
- * @return array
- */
- private static function getDayRankingAids(int $num): array
- {
- // day: 日榜1 三榜3 周榜7 月榜30
- $aids = [];
- $rand_nums = [];
- $url = "https://api.bilibili.com/x/web-interface/ranking";
- $payload = [
- 'rid' => 0,
- 'day' => 1,
- 'type' => 1,
- 'arc_type' => 0
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- for ($i = 0; $i < $num; $i++) {
- while (true) {
- $rand_num = mt_rand(1, 99);
- if (in_array($rand_num, $rand_nums)) {
- continue;
- } else {
- $rand_nums[] = $rand_num;
- break;
- }
- }
- $aid = $de_raw['data']['list'][$rand_nums[$i]]['aid'];
- $aids[] = $aid;
- }
-
- return $aids;
- }
-
- /**
- * @use 首页推荐
- * @param int $num
- * @param int $ps
- * @return array
- */
- private static function getTopRCmdAids(int $num, int $ps = 30): array
- {
- // 动画1 国创168 音乐3 舞蹈129 游戏4 知识36 科技188 汽车223 生活160 美食211 动物圈127 鬼畜119 时尚155 资讯202 娱乐5 影视181
- $rids = [1, 168, 3, 129, 4, 36, 188, 223, 160, 211, 127, 119, 155, 202, 5, 181];
- $aids = [];
- $url = 'https://api.bilibili.com/x/web-interface/dynamic/region';
- $payload = [
- 'ps' => $ps,
- 'rid' => $rids[array_rand($rids)],
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- if ($num == 1) {
- $temps = [array_rand($de_raw['data']['archives'], $num)];
- } else {
- $temps = array_rand($de_raw['data']['archives'], $num);
- }
- foreach ($temps as $temp) {
- $aids[] = $de_raw['data']['archives'][$temp]['aid'];
- }
- return $aids;
- }
- return self::getDayRankingAids($num);
- }
-
- /**
- * @use 分享视频
- * @return bool
- */
- private static function shareAid(): bool
- {
- if (!getConf('share', 'main_site')) return true;
-
- # aid = 稿件av号
- $url = "https://api.bilibili.com/x/web-interface/share/add";
- $av_info = self::parseAid();
- $payload = [
- 'aid' => $av_info['aid'],
- 'jsonp' => "jsonp",
- 'csrf' => getCsrf(),
- ];
- $headers = [
- 'Host' => "api.bilibili.com",
- 'Origin' => "https://www.bilibili.com",
- 'Referer' => "https://www.bilibili.com/video/av{$av_info['aid']}",
- 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice("主站任务: av{$av_info['aid']} 分享成功");
- return true;
- } else {
- Log::warning("主站任务: av{$av_info['aid']} 分享失败");
- return false;
- }
- }
-
- /**
- * @use 观看视频
- * @return bool
- */
- private static function watchAid(): bool
- {
- if (!getConf('watch', 'main_site')) return true;
-
- $url = "https://api.bilibili.com/x/report/click/h5";
- $av_info = self::parseAid();
- $user_info = User::parseCookies();
- $payload = [
- 'aid' => $av_info['aid'],
- 'cid' => $av_info['cid'],
- 'part' => 1,
- 'did' => $user_info['sid'],
- 'ftime' => time(),
- 'jsonp' => "jsonp",
- 'lv' => "",
- 'mid' => getUid(),
- 'csrf' => getCsrf(),
- 'stime' => time()
- ];
-
- $headers = [
- 'Host' => "api.bilibili.com",
- 'Origin' => "https://www.bilibili.com",
- 'Referer' => "https://www.bilibili.com/video/av{$av_info['aid']}",
- 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
-
- if ($de_raw['code'] == 0) {
- $url = "https://api.bilibili.com/x/report/web/heartbeat";
- $payload = [
- "aid" => $av_info['aid'],
- "cid" => $av_info['cid'],
- "mid" => $user_info['uid'],
- "csrf" => getCsrf(),
- "jsonp" => "jsonp",
- "played_time" => "0",
- "realtime" => $av_info['duration'],
- "pause" => false,
- "dt" => "7",
- "play_type" => "1",
- 'start_ts' => time()
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
-
- if ($de_raw['code'] == 0) {
- sleep(5);
- $payload['played_time'] = $av_info['duration'] - 1;
- $payload['play_type'] = 0;
- $payload['start_ts'] = time();
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice("主站任务: av{$av_info['aid']} 观看成功");
- return true;
- }
- }
- }
- Log::warning("主站任务: av{$av_info['aid']} 观看失败");
- return false;
- }
-
- /**
- * @use 解析AID到CID
- * @return array
- */
- #[ArrayShape(['aid' => "string", 'cid' => "mixed", 'duration' => "mixed"])]
- private static function parseAid(): array
- {
- while (true) {
- $aid = self::getRandomAid();
- $url = "https://api.bilibili.com/x/web-interface/view";
- $payload = [
- 'aid' => $aid
- ];
- $raw = Curl::get('other', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] != 0) {
- continue;
- } else {
- if (!array_key_exists('cid', $de_raw['data'])) {
- continue;
- }
- }
- $cid = $de_raw['data']['cid'];
- $duration = $de_raw['data']['duration'];
- return [
- 'aid' => $aid,
- 'cid' => $cid,
- 'duration' => $duration
- ];
- }
- }
-
- /**
- * @use 获取硬币数量
- * @return int
- */
- private static function getCoin(): int
- {
- $url = 'https://account.bilibili.com/site/getCoin';
- $payload = [];
- $headers = [
- 'referer' => 'https://account.bilibili.com/account/coin',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"status":true,"data":{"money":1707.9}}
- if ($de_raw['code'] == 0 && isset($de_raw['data']['money'])) {
- return floor($de_raw['data']['money']);
- }
- return 0;
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/ManGa.php b/src/plugin/ManGa.php
deleted file mode 100644
index a5f63ab..0000000
--- a/src/plugin/ManGa.php
+++ /dev/null
@@ -1,96 +0,0 @@
- time() || !getEnable('manga')) {
- return;
- }
- if (self::sign() && self::share()) {
- self::setLock(self::timing(10));
- return;
- }
- self::setLock(3600);
- }
-
-
- /**
- * @use 漫画签到
- * @return bool
- */
- private static function sign(): bool
- {
- sleep(1);
- $url = 'https://manga.bilibili.com/twirp/activity.v1.Activity/ClockIn';
- $payload = [
- 'access_key' => getAccessToken(),
- 'ts' => time()
- ];
- $raw = Curl::post('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- # {"code":0,"msg":"","data":{}}
- # {"code":"invalid_argument","msg":"clockin clockin is duplicate","meta":{"argument":"clockin"}}
- if (!$de_raw['code']) {
- Log::notice('漫画签到: 成功');
- } else {
- Log::warning('漫画签到: 失败或者重复操作');
- }
- return true;
- }
-
-
- /**
- * @use 漫画分享
- * @return bool
- */
- private static function share(): bool
- {
- sleep(1);
- $payload = [];
- $url = "https://manga.bilibili.com/twirp/activity.v1.Activity/ShareComic";
- $raw = Curl::post('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- # {"code":0,"msg":"","data":{"point":5}}
- # {"code":1,"msg":"","data":{"point":0}}
- if (!$de_raw['code']) {
- Log::notice('漫画分享: 成功');
- } else {
- Log::warning('漫画分享: 失败或者重复操作');
- }
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/plugin/MaterialObject.php b/src/plugin/MaterialObject.php
deleted file mode 100644
index acbd839..0000000
--- a/src/plugin/MaterialObject.php
+++ /dev/null
@@ -1,246 +0,0 @@
- time() || !getEnable('live_box')) {
- return;
- }
- self::setPauseStatus();
- self::calcAid(880, 1080);
- $lottery_list = self::fetchLottery();
- self::drawLottery($lottery_list);
- self::setLock(mt_rand(6, 10) * 60);
- }
-
- /**
- * @use 过滤抽奖Title
- * @param string $title
- * @return bool
- */
- private static function filterTitleWords(string $title): bool
- {
- self::loadJsonData();
- $sensitive_words = self::$store->get("MaterialObject.sensitive");
- foreach ($sensitive_words as $word) {
- if (str_contains($title, $word)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @use 抽奖盒子状态
- * @param int $aid
- * @param string $reply
- * @return mixed
- */
- private static function boxStatus(int $aid, string $reply = 'bool'): mixed
- {
- // $url = 'https://api.live.bilibili.com/lottery/v1/box/getStatus';
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v2/Box/getStatus';
- $payload = [
- 'aid' => $aid,
- ];
- $raw = Curl::get('pc', $url, $payload);
- $de_raw = json_decode($raw, true);
- // {"code":0,"data":null,"message":"ok","msg":"ok"}
- // {"code":0,"data":{"title":"荣耀宝箱抽奖","rule":"a 抽奖时间按如下规则抽取一次,重复无效。\nb 获奖者需要再获奖名单公布后一周内反馈姓名、邮寄地址、联系方式,因获奖者逾期查看获奖名单、逾期提交个人资料或个人资料有误,将视为自动放弃获奖资格及由此产生的权利。","current_round":2,"typeB":[{"startTime":"2020-05-18 18:30:00","imgUrl":"https://i0.hdslb.com/bfs/live/f600b89f2c2550b600612feba90e39901a9f027c.jpg","join_start_time":1589796000,"join_end_time":1589797800,"status":4,"list":[{"jp_name":"荣耀路由3","jp_num":"1","jp_id":3181,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/f600b89f2c2550b600612feba90e39901a9f027c.jpg"}],"round_num":1},{"startTime":"2020-05-18 19:00:00","imgUrl":"https://i0.hdslb.com/bfs/live/f600b89f2c2550b600612feba90e39901a9f027c.jpg","join_start_time":1589797800,"join_end_time":1589799600,"status":0,"list":[{"jp_name":"荣耀路由3","jp_num":"1","jp_id":3182,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/f600b89f2c2550b600612feba90e39901a9f027c.jpg"}],"round_num":2},{"startTime":"2020-05-18 19:30:00","imgUrl":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg","join_start_time":1589799600,"join_end_time":1589801400,"status":-1,"list":[{"jp_name":"荣耀平板V6","jp_num":"1","jp_id":3183,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg"}],"round_num":3},{"startTime":"2020-05-18 20:00:00","imgUrl":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg","join_start_time":1589801400,"join_end_time":1589803200,"status":-1,"list":[{"jp_name":"荣耀平板V6","jp_num":"1","jp_id":3184,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg"}],"round_num":4},{"startTime":"2020-05-18 20:30:00","imgUrl":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg","join_start_time":1589803200,"join_end_time":1589805000,"status":-1,"list":[{"jp_name":"荣耀平板V6","jp_num":"1","jp_id":3185,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/9fcde6f26a546b9dfb5ffc7a0c4f4503a05e16f2.jpg"}],"round_num":5},{"startTime":"2020-05-18 21:00:00","imgUrl":"https://i0.hdslb.com/bfs/live/73db69bd5a9e5dedb7d2f32d72fd6248b860e238.jpg","join_start_time":1589805000,"join_end_time":1589806800,"status":-1,"list":[{"jp_name":"荣耀MagicBook Pro","jp_num":"1","jp_id":3186,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/73db69bd5a9e5dedb7d2f32d72fd6248b860e238.jpg"}],"round_num":6},{"startTime":"2020-05-18 22:00:00","imgUrl":"https://i0.hdslb.com/bfs/live/4dba1e8b58c174d5e2311de339b1e02a3ac77a98.jpg","join_start_time":1589806800,"join_end_time":1589810400,"status":-1,"list":[{"jp_name":"荣耀智慧屏新品,荣耀MagicBook Pro,荣耀平板V6,荣耀路由3","jp_num":"1","jp_id":3187,"jp_type":2,"ex_text":"","jp_pic":"https://i0.hdslb.com/bfs/live/4dba1e8b58c174d5e2311de339b1e02a3ac77a98.jpg"}],"round_num":7}],"activity_pic":"https://i0.hdslb.com/bfs/live/c3ed87683f6e87d256d1f5fdddbfb220fc4c2cdf.png","activity_id":556,"weight":20,"background":"https://i0.hdslb.com/bfs/live/84cd59bcb1e977359df618dbeb0f7828751f457c.png","title_color":"#FFFFFF","closeable":0,"jump_url":"https://live.bilibili.com/p/html/live-app-treasurebox/index.html?is_live_half_webview=1\u0026hybrid_biz=live-app-treasurebox\u0026hybrid_rotate_d=1\u0026hybrid_half_ui=1,3,100p,70p,0,0,30,100;2,2,375,100p,0,0,30,100;3,3,100p,70p,0,0,30,100;4,2,375,100p,0,0,30,100;5,3,100p,70p,0,0,30,100;6,3,100p,70p,0,0,30,100;7,3,100p,70p,0,0,30,100\u0026aid=556"},"message":"","msg":""}
- switch ($reply) {
- // 等于0是有抽奖返回false
- case 'bool':
- if (!is_null($de_raw['data'])) {
- return false;
- }
- return true;
- case 'array':
- if (!is_null($de_raw['data'])) {
- return $de_raw;
- }
- return [];
- default:
- return $de_raw;
- }
- }
-
- /**
- * @use 获取抽奖
- * @return array
- */
- private static function fetchLottery(): array
- {
- // 缓存开始 如果存在就赋值 否则默认值
- if ($temp = getCache('invalid_aids')) {
- self::$invalid_aids = $temp;
- }
-
- $lottery_list = [];
- $max_probe = 10;
- $probes = range(self::$start_aid, self::$end_aid);
- foreach ($probes as $probe_aid) {
- // 最大试探
- if ($max_probe == 0) break;
- // 无效列表
- if (in_array($probe_aid, self::$invalid_aids)) {
- continue;
- }
- // 试探
- $response = self::boxStatus($probe_aid, 'array');
- if (empty($response)) {
- $max_probe--;
- continue;
- }
- $rounds = $response['data']['typeB'];
- $last_round = end($rounds);
- // 最后抽奖轮次无效
- if ($last_round['join_end_time'] < time()) {
- self::$invalid_aids[] = $probe_aid;
- continue;
- }
- // 过滤敏感词
- $title = $response['data']['title'];
- if (self::filterTitleWords($title)) {
- self::$invalid_aids[] = $probe_aid;
- continue;
- }
- // 过滤抽奖轮次
- $round_num = self::filterRound($rounds);
- if ($round_num == 0) {
- continue;
- }
- $lottery_list[] = [
- 'aid' => $probe_aid,
- 'num' => $round_num,
- ];
- }
- // 缓存结束 需要的数据的放进缓存
- setCache('invalid_aids', self::$invalid_aids);
-
- return $lottery_list;
- }
-
- /**
- * @use 过滤轮次
- * @param array $rounds
- * @return int
- */
- private static function filterRound(array $rounds): int
- {
- foreach ($rounds as $round) {
- $join_start_time = $round['join_start_time'];
- $join_end_time = $round['join_end_time'];
- if ($join_end_time > time() && time() > $join_start_time) {
- $status = $round['status'];
- /*
- * 3 结束 1 抽过 -1 未开启 0 可参与
- */
- if ($status == 0) {
- return $round['round_num'];
- }
- }
- }
- return 0;
- }
-
- /**
- * @use 抽奖
- * @param array $lottery_list
- * @return bool
- */
- private static function drawLottery(array $lottery_list): bool
- {
- foreach ($lottery_list as $lottery) {
- $aid = $lottery['aid'];
- $num = $lottery['num'];
- Log::notice("实物抽奖 $aid 轮次 $num 可参与抽奖~");
- // $url = 'https://api.live.bilibili.com/lottery/v1/Box/draw';
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v2/Box/draw';
- $payload = [
- 'aid' => $aid,
- 'number' => $num,
- ];
- $raw = Curl::get('pc', $url, $payload);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice("实物抽奖 $aid 轮次 $num 参与抽奖成功~");
- } else {
- Log::notice("实物抽奖 $aid 轮次 $num {$de_raw['msg']}~");
- }
- }
- return true;
- }
-
- /**
- * @use 计算Aid
- * @param $min
- * @param $max
- * @return bool
- */
- private static function calcAid($min, $max): bool
- {
- // Todo 优化计算AID算法
- if (self::$end_aid != 0 && self::$start_aid != 0) {
- return false;
- }
- while (true) {
- $middle = round(($min + $max) / 2);
- if (self::boxStatus($middle)) {
- if (self::boxStatus($middle + mt_rand(0, 3))) {
- $max = $middle;
- } else {
- $min = $middle;
- }
- } else {
- $min = $middle;
- }
- if ($max - $min == 1) {
- break;
- }
- }
- self::$start_aid = $min - mt_rand(15, 30);
- self::$end_aid = $min + mt_rand(15, 30);
- Log::info("实物抽奖起始值[" . self::$start_aid . "],结束值[" . self::$end_aid . "]");
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/plugin/PkRaffle.php b/src/plugin/PkRaffle.php
deleted file mode 100644
index 9592463..0000000
--- a/src/plugin/PkRaffle.php
+++ /dev/null
@@ -1,140 +0,0 @@
- $room_id,
- 'raffle_id' => $pk['id'],
- 'raffle_name' => $pk['title'],
- 'wait' => time() + mt_rand(5, 25)
- ];
- Statistics::addPushList($data['raffle_name']);
- self::$wait_list[] = $data;
- }
- return true;
- }
-
- /**
- * @use 创建抽奖任务
- * @param array $raffles
- * @return array
- */
- protected static function createLottery(array $raffles): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/pk/join';
- $tasks = [];
- foreach ($raffles as $raffle) {
- $payload = [
- 'id' => $raffle['raffle_id'],
- 'roomid' => $raffle['room_id'],
- 'csrf_token' => getCsrf(),
- "csrf" => getCsrf(),
- ];
- $tasks[] = [
- 'payload' => Sign::common($payload),
- 'source' => [
- 'room_id' => $raffle['room_id'],
- 'raffle_id' => $raffle['raffle_id'],
- 'raffle_name' => $raffle['raffle_name']
- ]
- ];
- }
- // print_r($results);
- return Curl::async('app', $url, $tasks);
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- foreach ($results as $result) {
- $data = $result['source'];
- $content = $result['content'];
- $de_raw = json_decode($content, true);
- /*
- * {'code': 0, 'message': '0', 'ttl': 1, 'data': {'id': 343560, 'gift_type': 0, 'award_id': '1', 'award_text': '辣条X1', 'award_image': 'https://i0.hdslb.com/bfs/live/da6656add2b14a93ed9eb55de55d0fd19f0fc7f6.png', 'award_num': 0, 'title': '大乱斗获胜抽奖'}}
- * {'code': -1, 'message': '抽奖已结束', 'ttl': 1}
- * {'code': -2, 'message': '您已参加过抽奖', 'ttl': 1}
- * {"code":-403,"data":null,"message":"访问被拒绝","msg":"访问被拒绝"}
- */
- if (isset($de_raw['code']) && $de_raw['code'] == 0) {
- Statistics::addSuccessList($data['raffle_name']);
- $award_text = $de_raw['data']['award_text'];
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: $award_text");
- // 收益
- Statistics::addProfitList($data['raffle_name'] . '-' . explode('X', $award_text)[0], $de_raw['data']['award_num']);
- } elseif (isset($de_raw['msg']) && $de_raw['code'] == -403 && $de_raw['msg'] == '访问被拒绝') {
- Log::debug("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: {$de_raw['message']}");
- self::pauseLock();
- } else {
- Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} {$data['raffle_name']}: {$de_raw['message']}");
- }
- }
- return '';
- }
-}
diff --git a/src/plugin/PolishTheMedal.php b/src/plugin/PolishTheMedal.php
deleted file mode 100644
index 7782958..0000000
--- a/src/plugin/PolishTheMedal.php
+++ /dev/null
@@ -1,132 +0,0 @@
- 11000 MSG -> ''
- if (in_array($medal['roomid'], [21686237, 0])) return;
-
- Log::info("开始点亮直播间@{$medal['roomid']}的勋章");
- // 擦亮
- $response = Live::sendBarrageAPP($medal['roomid'], Generator::emoji());
- if (isset($response['code']) && $response['code'] == 0) {
- Log::notice("在直播间@{$medal['roomid']}发送点亮弹幕成功");
- } else {
- Log::warning("在直播间@{$medal['roomid']}发送点亮弹幕失败, CODE -> {$response['code']} MSG -> {$response['message']} ");
- }
- }
-
-
- /**
- * @use 获取灰色勋章列表(过滤无勋章或已满)
- * @param bool $all
- */
- private static function fetchGreyMedalList(bool $all = false)
- {
- $data = Live::fetchMedalList();
- foreach ($data as $vo) {
- // 过滤主站勋章
- if (!isset($vo['roomid']) || $vo['roomid'] == 0) continue;
- // 过滤自己勋章
- if ($vo['target_id'] == getUid()) continue;
- // 所有
- self::$fans_medals[] = [
- 'uid' => $vo['target_id'],
- 'roomid' => $vo['roomid'],
- ];
- // 如果是每天擦亮 ,就不过滤|否则过滤掉,只点亮灰色
- if ($all) {
- self::$grey_fans_medals[] = [
- 'uid' => $vo['target_id'],
- 'roomid' => $vo['roomid'],
- ];
- } else {
- // 灰色
- if ($vo['medal_color_start'] == 12632256 && $vo['medal_color_end'] == 12632256 && $vo['medal_color_border'] == 12632256) {
- self::$grey_fans_medals[] = [
- 'uid' => $vo['target_id'],
- 'roomid' => $vo['roomid'],
- ];
- }
- }
- }
- // 乱序
- shuffle(self::$grey_fans_medals);
- }
-}
\ No newline at end of file
diff --git a/src/plugin/RedPocketRaffle.php b/src/plugin/RedPocketRaffle.php
deleted file mode 100644
index 0ff7a26..0000000
--- a/src/plugin/RedPocketRaffle.php
+++ /dev/null
@@ -1,95 +0,0 @@
- $raffle['ruid'],
- 'room_id' => $raffle['room_id'],
- 'lot_id' => $raffle['lot_id'],
- 'spm_id' => '444.8.red_envelope.extract',
- 'jump_from' => '',
- 'session_id' => '',
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- // {"code":0,"message":"0","ttl":1,"data":{"join_status":1}}
- $tasks[] = [
- 'payload' => Sign::common($payload),
- 'source' => [
- 'room_id' => $raffle['room_id'],
- 'raffle_id' => $raffle['raffle_id'],
- 'raffle_name' => $raffle['raffle_name']
- ]
- ];
- }
- // print_r($results);
- return Curl::async('app', $url, $tasks);
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- return '';
- }
-}
\ No newline at end of file
diff --git a/src/plugin/Schedule.php b/src/plugin/Schedule.php
deleted file mode 100644
index 25760ee..0000000
--- a/src/plugin/Schedule.php
+++ /dev/null
@@ -1,176 +0,0 @@
- time()) {
- return;
- }
- self::isSleep();
- self::isSpecialPause();
- self::setLock(60);
- }
-
- /**
- * @use 检查休眠
- */
- private static function isSleep(): bool
- {
- if (getEnable('sleep') && self::$unlock_hour != date('H')) {
- self::$sleep_section = empty(self::$sleep_section) ? explode(',', getConf('section', 'sleep')) : self::$sleep_section;
- if (!in_array(date('H'), self::$sleep_section)) {
- return false;
- }
- self::handleBan('sleep');
- }
- return true;
- }
-
-
- /**
- * @use 特殊暂停
- */
- private static function isSpecialPause(): bool
- {
- foreach (self::$guarded_second as $classname) {
- $status = call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'getPauseStatus'));
- if ($status) {
- return true;
- }
- }
- foreach (self::$guarded_third as $classname) {
- $status = call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'getPauseStatus'));
- if (!$status) {
- return false;
- }
- }
- self::handleBan('special');
- return true;
- }
-
-
- /**
- * @use 处理禁令
- * @param $action
- * @param string $classname
- */
- private static function handleBan($action, string $classname = '')
- {
- switch ($action) {
- // 休眠
- case 'sleep':
- foreach (self::$fillable as $classname) {
- Log::info("插件 $classname 白名单,保持当前状态继续");
- }
- $unlock_time = 60 * 60;
- self::$unlock_hour = date('H');
- $classname_list = array_merge(self::$guarded_first, self::$guarded_second, self::$guarded_third);
- if (!User::isMaster()) {
- $classname_list = array_merge($classname_list, self::$guarded_fourth);
- }
- self::stopProc($classname_list, $unlock_time, true);
- Log::warning('进入自定义休眠时间范围,暂停非必要任务,自动开启!');
- break;
- // 暂停访问
- case 'pause':
- // 访问拒绝 统一时间 第二天0点
- $unlock_time = strtotime(date("Y-m-d", strtotime("+1 day", time()))) - time();
- self::stopProc([$classname], $unlock_time);
- Log::warning("$classname 任务拒绝访问,暂停任务,自动开启!");
- // 推送被ban信息
- $time = floor($unlock_time / 60 / 60);
- Notice::push('banned', "任务 $classname 暂停,$time 小时后自动恢复!");
- break;
- // 特殊类
- case 'special':
- // 访问拒绝 统一时间 第二天0点
- $unlock_time = strtotime(date("Y-m-d", strtotime("+1 day", time()))) - time();
- self::stopProc(self::$guarded_second, $unlock_time);
- Log::warning("所有抽奖任务拒绝访问,暂停监控任务,自动开启!");
- break;
- default:
- break;
- }
- }
-
-
- /**
- * @use 停止运行
- * @param array $classname_list
- * @param int $unlock_time
- * @param bool $force
- */
- private static function stopProc(array $classname_list, int $unlock_time, bool $force = false)
- {
- foreach ($classname_list as $classname) {
- Log::info("插件 $classname 黑名单,锁定状态将于" . date("Y-m-d H:i", time() + $unlock_time) . "解除");
- // 强制 无视小黑屋设定
- if ($force) {
- call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'setPauseStatus'), false);
- }
- call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'setLock'), $unlock_time + 3 * 60);
- call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'setPauseStatus'), true);
- }
- }
-
- /**
- * @use 触发封禁
- * @param string $classname
- */
- public static function triggerRefused(string $classname)
- {
- self::handleBan('pause', $classname);
- }
-}
\ No newline at end of file
diff --git a/src/plugin/Sign.php b/src/plugin/Sign.php
deleted file mode 100644
index ce1d6b4..0000000
--- a/src/plugin/Sign.php
+++ /dev/null
@@ -1,97 +0,0 @@
- getAccessToken(),
- 'actionKey' => 'appkey',
- 'appkey' => $app_key,
- 'build' => getDevice('bili_a.build'),
- 'channel' => getDevice('bili_a.channel'),
- 'device' => getDevice('bili_a.device'),
- 'mobi_app' => getDevice('bili_a.mobi_app'),
- 'platform' => getDevice('bili_a.platform'),
- 'ts' => time(),
- ];
- $payload = array_merge($payload, $default);
- return self::encryption($payload, $app_secret);
- }
-
- /**
- * @use 通用
- * @param $payload
- * @return array
- */
- public static function common($payload): array
- {
- # Android 旧
- $app_key = base64_decode(getDevice('bili_a.app_key'));
- $app_secret = base64_decode(getDevice('bili_a.secret_key'));
-
- $default = [
- 'access_key' => getAccessToken(),
- 'actionKey' => 'appkey',
- 'appkey' => $app_key,
- 'build' => getDevice('bili_a.build'),
- 'device' => getDevice('bili_a.device'),
- 'mobi_app' => getDevice('bili_a.mobi_app'),
- 'platform' => getDevice('bili_a.platform'),
- 'ts' => time(),
- ];
- $payload = array_merge($payload, $default);
- return self::encryption($payload, $app_secret);
- }
-
-
- /**
- * @use 加密
- * @param array $payload
- * @param string $app_secret
- * @return array
- */
- private static function encryption(array $payload, string $app_secret): array
- {
- if (isset($payload['sign'])) {
- unset($payload['sign']);
- }
- ksort($payload);
- $data = http_build_query($payload);
- $payload['sign'] = md5($data . $app_secret);
- return $payload;
- }
-}
\ No newline at end of file
diff --git a/src/plugin/Silver.php b/src/plugin/Silver.php
deleted file mode 100644
index 217225f..0000000
--- a/src/plugin/Silver.php
+++ /dev/null
@@ -1,133 +0,0 @@
- time()) {
- return;
- }
- self::setPauseStatus();
-
- if (empty(self::$task)) {
- self::getSilverBox();
- } else {
- self::openSilverBox();
- }
- }
-
- /**
- * @use 获取宝箱
- */
- private static function getSilverBox()
- {
- $url = 'https://api.live.bilibili.com/lottery/v1/SilverBox/getCurrentTask';
- $payload = [];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- if (isset($data['code']) && $data['code'] == -10017) {
- Log::notice($data['message']);
- if (User::isMaster()) {
- self::setLock(self::timing(6));
- } else {
- self::setLock(self::timing(10));
- }
- return;
- }
-
- if (isset($data['code']) && $data['code']) {
- Log::error("check freeSilverCurrentTask failed! Error message: {$data['message']}");
- die();
- }
-
- Log::info("获得一个宝箱,内含 {$data['data']['silver']} 个瓜子");
- Log::info("开启宝箱需等待 {$data['data']['minute']} 分钟");
-
- self::$task = [
- 'time_start' => $data['data']['time_start'],
- 'time_end' => $data['data']['time_end'],
- ];
- self::setLock($data['data']['minute'] * 60 + 5);
- }
-
-
- /**
- * @use 开启宝箱
- */
- private static function openSilverBox()
- {
- $url = 'https://api.live.bilibili.com/mobile/freeSilverAward';
- $payload = [
- 'time_end' => self::$task['time_end'],
- 'time_start' => self::$task['time_start']
- ];
- $data = Curl::get('app', $url, Sign::common($payload));
- $data = json_decode($data, true);
-
- // {"code":400,"msg":"访问被拒绝","message":"访问被拒绝","data":[]}
- if (isset($data['msg']) && $data['code'] == 400 && $data['msg'] == '访问被拒绝') {
- self::pauseLock();
- return;
- }
-
- if ($data['code'] == -800) {
- self::setLock(12 * 60 * 60);
- Log::warning("领取宝箱失败,{$data['message']}!");
- return;
- }
-
- if ($data['code'] == -903) {
- Log::warning("领取宝箱失败,{$data['message']}!");
- self::$task = [];
- self::setLock(60);
- return;
- }
-
- if (isset($data['code']) && $data['code']) {
- Log::warning("领取宝箱失败,{$data['message']}!");
- self::setLock(60);
- return;
- }
-
- Log::notice("领取宝箱成功,Silver: {$data['data']['silver']}(+{$data['data']['awardSilver']})");
-
- self::$task = [];
- self::setLock(10);
- }
-}
diff --git a/src/plugin/Silver2Coin.php b/src/plugin/Silver2Coin.php
deleted file mode 100644
index 2d59870..0000000
--- a/src/plugin/Silver2Coin.php
+++ /dev/null
@@ -1,112 +0,0 @@
- time() || !getEnable('silver2coin')) {
- return;
- }
- if (self::appSilver2coin() && self::pcSilver2coin()) {
- // 定时10点 + 1-60分钟随机
- self::setLock(self::timing(10, 0, 0, true));
- return;
- }
- self::setLock(3600);
- }
-
- /**
- * @use app兑换
- * @return bool
- */
- protected static function appSilver2coin(): bool
- {
- usleep(0.5 * APP_MICROSECOND);
- $url = 'https://api.live.bilibili.com/AppExchange/silver2coin';
- $payload = [];
- $raw = Curl::post('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
-
- return self::handle('APP', $de_raw);
-
- }
-
- /**
- * @use pc兑换
- * @return bool
- */
- protected static function pcSilver2coin(): bool
- {
- usleep(0.5 * APP_MICROSECOND);
- $payload = [
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- // $url = "https://api.live.bilibili.com/exchange/silver2coin";
- // $url = "https://api.live.bilibili.com/pay/v1/Exchange/silver2coin";
- $url = "https://api.live.bilibili.com/xlive/revenue/v1/wallet/silver2coin";
- $raw = Curl::post('pc', $url, $payload);
- $de_raw = json_decode($raw, true);
-
- return self::handle('PC', $de_raw);
- }
-
- /**
- * @use 处理结果
- * @param string $type
- * @param array $data
- * @return bool
- */
- private static function handle(string $type, array $data): bool
- {
- // {"code":403,"msg":"每天最多能兑换 1 个","message":"每天最多能兑换 1 个","data":[]}
- // {"code":403,"msg":"仅主站正式会员以上的用户可以兑换","message":"仅主站正式会员以上的用户可以兑换","data":[]}
- // {"code":0,"msg":"兑换成功","message":"兑换成功","data":{"gold":"5074","silver":"36734","tid":"727ab65376a15a6b117cf560a20a21122334","coin":1}}
- // {"code":0,"data":{"coin":1,"gold":1234,"silver":4321,"tid":"Silver2Coin21062316490299678123456"},"message":"兑换成功"}
- switch ($data['code']) {
- case 0:
- Log::notice("[$type] 银瓜子兑换硬币: {$data['message']}");
- return true;
- case 403:
- Log::warning("[$type] 银瓜子兑换硬币: {$data['message']}");
- return true;
- default:
- Log::warning("[$type] 银瓜子兑换硬币: CODE -> {$data['code']} MSG -> {$data['message']} ");
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/src/plugin/SmallHeart.php b/src/plugin/SmallHeart.php
deleted file mode 100644
index a0e96f6..0000000
--- a/src/plugin/SmallHeart.php
+++ /dev/null
@@ -1,102 +0,0 @@
-= 12000) {
- self::$total_time = 0;
- self::$metal = null;
- self::setLock(self::timing(2));
- } else {
- self::setLock(self::$interval);
- }
- }
- }
-
- /**
- * @use 心跳处理
- */
- private static function heartBeat()
- {
- if (empty(self::$fans_medals)) {
- return;
- }
- if (is_null(self::$metal)) {
- self::$metal = self::$fans_medals[array_rand(self::$fans_medals)];
- }
- $interval = self::xliveHeartBeatTask(self::$metal['roomid'], 999, 999);
- if ($interval != 0) {
- self::$total_time += $interval;
- }
- self::$interval = $interval == 0 ? 60 : $interval;
- }
-
- /**
- * @use 获取灰色勋章列表(过滤无勋章或已满)
- */
- private static function fetchMedalList()
- {
- $data = Live::fetchMedalList();
- foreach ($data as $vo) {
- // 过滤主站勋章
- if (!isset($vo['roomid']) || $vo['roomid'] == 0) continue;
- // 过滤自己勋章
- if ($vo['target_id'] == getUid()) continue;
- // 所有
- self::$fans_medals[] = [
- 'uid' => $vo['target_id'],
- 'roomid' => $vo['roomid'],
- ];
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/Statistics.php b/src/plugin/Statistics.php
deleted file mode 100644
index 4e2155e..0000000
--- a/src/plugin/Statistics.php
+++ /dev/null
@@ -1,306 +0,0 @@
- time()) {
- return;
- }
- self::outputResult();
- self::setLock(20 * 60);
- }
-
- /**
- * @use 添加推送
- * @param string $key
- * @param int $num
- * @return bool
- */
- public static function addPushList(string $key, int $num = 1): bool
- {
- self::initKeyValue(self::$push_list, $key);
- self::valIncrease(self::$push_list, $key);
- return true;
- }
-
- /**
- * @use 添加参与
- * @param string $key
- * @param int $num
- * @return bool
- */
- public static function addJoinList(string $key, int $num = 1): bool
- {
- self::initKeyValue(self::$join_list, $key);
- self::valIncrease(self::$join_list, $key);
- return true;
- }
-
- /**
- * @use 添加成功
- * @param string $key
- * @param int $num
- * @return bool
- */
- public static function addSuccessList(string $key, int $num = 1): bool
- {
- self::initKeyValue(self::$success_list, $key);
- self::valIncrease(self::$success_list, $key);
- return true;
- }
-
- /**
- * @use 添加收益
- * @param string $title
- * @param int $num
- * @param int $updated_time
- * @return bool
- */
- public static function addProfitList(string $title, int $num, int $updated_time = 0): bool
- {
- self::initKeyValue(self::$profit_list, $title, 0, 'num');
- self::initKeyValue(self::$profit_list, $title, time(), 'updated_time');
- self::valIncrease(self::$profit_list, $title, $num, 'num');
- self::valReplace(self::$profit_list, $title, time(), 'updated_time');
- return true;
- }
-
- /**
- * @use 转换时间
- * @param int $the_time
- * @return string
- */
- private static function timeTran(int $the_time): string
- {
- $the_time = $the_time < 1577808000 ? time() : $the_time;
- $t = time() - $the_time + 3;//现在时间-发布时间 获取时间差
- $f = [
- '31536000' => '年',
- '2592000' => '个月',
- '604800' => '星期',
- '86400' => '天',
- '3600' => '小时',
- '60' => '分钟',
- '1' => '秒',
- ];
- foreach ($f as $k => $v) {
- if (0 != $c = floor($t / (int)$k)) {
- return $c . $v . '前';
- }
- }
- return '';
- }
-
- /**
- * @use 初始化键值
- * @param array $target
- * @param string $key
- * @param int $value
- * @param null $second_key
- * @return bool
- */
- private static function initKeyValue(array &$target, string $key, int $value = 0, $second_key = null): bool
- {
- if (!array_key_exists(self::getTodayKey(), $target)) {
- $target[self::getTodayKey()] = [];
- }
- if (!array_key_exists($key, $target[self::getTodayKey()])) {
- $target[self::getTodayKey()][$key] = is_null($second_key) ? $value : [];
- }
- if (!is_null($second_key) && !array_key_exists($second_key, $target[self::getTodayKey()][$key])) {
- $target[self::getTodayKey()][$key][$second_key] = $value;
- }
- return true;
- }
-
- /**
- * @use 获取结果
- * @param array $target
- * @param string $key
- * @param null $second_key
- * @return mixed
- */
- private static function getResult(array &$target, string $key, $second_key = null): mixed
- {
- is_null($second_key) ? self::initKeyValue($target, $key) : self::initKeyValue($target, $key, 0, $second_key);
- return is_null($second_key) ? $target[self::getTodayKey()][$key] : $target[self::getTodayKey()][$key][$second_key];
- }
-
- /**
- * @use 获取所有结果
- * @param array $target
- * @param string $key
- * @param string|null $second_key
- * @return int
- */
- private static function getResults(array &$target, string $key, string $second_key = null): int
- {
- $results = 0;
- is_null($second_key) ? self::initKeyValue($target, $key) : self::initKeyValue($target, $key, 0, $second_key);
- foreach ($target as $item) {
- $skip = is_null($second_key) ? isset($item[$key]) : isset($item[$key][$second_key]);
- if ($skip) {
- is_null($second_key) ? $results += intval($item[$key]) : $results += intval($item[$key][$second_key]);
- }
- }
- return $results;
- }
-
- /**
- * @use 变量增加
- * @param array $target
- * @param string $key
- * @param int $num
- * @param null $second_key
- * @return bool
- */
- private static function valIncrease(array &$target, string $key, int $num = 1, $second_key = null): bool
- {
- is_null($second_key) ? $target[self::getTodayKey()][$key] += $num : $target[self::getTodayKey()][$key][$second_key] += $num;
- return true;
- }
-
- /**
- * @use 变量替换
- * @param array $target
- * @param string $key
- * @param null $data
- * @param string $second_key
- * @return bool
- */
- private static function valReplace(array &$target, string $key, $data = null, string $second_key = ''): bool
- {
- is_null($second_key) ? $target[self::getTodayKey()][$key] = $data : $target[self::getTodayKey()][$key][$second_key] = $data;
- return true;
- }
-
- /**
- * @use 获取 table -> tr -> td
- * @return array
- */
- private static function getTrList(): array
- {
- $tr_list_count = [];
- $tr_list_profit = [];
- // 统计数量
- foreach (self::$push_list as $index => $item) {
- foreach ($item as $key => $val) {
- $td = [
- '名称 (总计/今日)' => $key,
- '推送' => self::getResults(self::$push_list, $key) . '/' . self::getResult(self::$push_list, $key),
- '参与' => self::getResults(self::$join_list, $key) . '/' . self::getResult(self::$join_list, $key),
- '成功' => self::getResults(self::$success_list, $key) . '/' . self::getResult(self::$success_list, $key),
- ];
- $tr_list_count[] = $td;
- }
- }
- // 收益数量
- foreach (self::$profit_list as $index => $item) {
- foreach ($item as $key => $val) {
- $td = [
- '名称 (总计/今日)' => explode('-', $key)[0],
- '数量' => self::getResults(self::$profit_list, $key, 'num') . '/' . self::getResult(self::$profit_list, $key, 'num'),
- '奖品' => explode('-', $key)[1],
- '更新时间' => self::timeTran(self::getResult(self::$profit_list, $key, 'updated_time')),
- ];
- $tr_list_profit[] = $td;
- }
- }
- return [self::unique_arr($tr_list_count), self::unique_arr($tr_list_profit)];
- }
-
- /**
- * @use 二维数组去重
- * @param array $result
- * @return array
- */
- private static function unique_arr(array $result): array
- {
- return array_unique($result, SORT_REGULAR);
- }
-
- /**
- * @use 数据数组转表格数组
- * @param array $data
- * @return array
- */
- private static function arrayToTable(array $data): array
- {
- $th_list = [];
- if ($data) {
- $renderer = new ArrayToTextTable($data);
- foreach (explode("\n", $renderer->getTable()) as $value) {
- if ($value) {
- $th_list[] = $value;
- }
- }
- }
- return $th_list;
- }
-
- /**
- * @use 打印表格
- */
- private static function outputResult()
- {
- $arr_tr_list = self::getTrList();
- foreach ($arr_tr_list as $tr_list) {
- $th_list = self::arrayToTable($tr_list);
- foreach ($th_list as $th) {
- Log::notice($th);
- }
- }
- }
-
- /**
- * @use 获取今日KEY
- * @return string
- */
- private static function getTodayKey(): string
- {
- $ts = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
- // 1592668800 -> 5bb4085b4cc25bc0
- return md5($ts);
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/StormRaffle.php b/src/plugin/StormRaffle.php
deleted file mode 100644
index cdd381a..0000000
--- a/src/plugin/StormRaffle.php
+++ /dev/null
@@ -1,180 +0,0 @@
- $room_id,
- 'raffle_id' => $de_raw['id'],
- 'raffle_name' => '节奏风暴',
- 'wait' => time()
- ];
- Statistics::addPushList($data['raffle_name']);
- self::$wait_list[] = $data;
- return true;
- }
-
- /**
- * 格式化日志输出
- * @param $id
- * @param $num
- * @param $info
- * @return string
- */
- private static function formatInfo($id, $num, $info): string
- {
- return "节奏风暴 $id 请求 $num 状态 $info";
- }
-
- /**
- * @use 创建抽奖任务
- * @param array $raffles
- * @return array
- */
- protected static function createLottery(array $raffles): array
- {
- $url = 'https://api.live.bilibili.com/lottery/v1/Storm/join';
- foreach ($raffles as $raffle) {
- self::$attempt = empty($attempt = getConf('attempt', 'live_storm')) ? [5, 10] : explode(',', $attempt);
- $num = mt_rand((int)self::$attempt[0], (int)self::$attempt[1]);
- $payload = [
- 'id' => $raffle['raffle_id'],
- 'roomid' => $raffle['room_id'],
- "color" => "16772431",
- "captcha_token" => "",
- "captcha_phrase" => "",
- "token" => getCsrf(),
- "csrf_token" => getCsrf(),
- "visit_id" => ""
- ];
- for ($i = 1; $i < $num; $i++) {
- $raw = Curl::post('app', $url, Sign::common($payload));
- if (str_contains((string)$raw, 'html')) {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, '触发哔哩哔哩安全风控策略(412)'));
- break;
- }
- $de_raw = json_decode($raw, true);
- // {"code":-412,"message":"请求被拦截","ttl":1,"data":null}
- if ($de_raw['code'] == -412) {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, '触发哔哩哔哩安全风控策略(-412)'));
- break;
- }
- if ($de_raw['code'] == 429 || $de_raw['code'] == -429) {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, '节奏风暴未实名或异常验证码'));
- break;
- }
- if ($de_raw['code'] == 0) {
- $data = $de_raw['data'];
- Statistics::addSuccessList($raffle['raffle_name']);
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, $data['mobile_content']));
- Statistics::addProfitList($data['title'] . '-' . $data['gift_name'], $data['gift_num']);
- break;
- }
- if (!isset($de_raw['msg'])) {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, $de_raw));
- break;
- }
- if ($de_raw['msg'] == '节奏风暴不存在' || $de_raw['msg'] == '节奏风暴抽奖过期' || $de_raw['msg'] == '没抢到') {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, '节奏风暴已经结束'));
- break;
- }
- if ($de_raw['msg'] == '已经领取奖励') {
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, '节奏风暴已经领取'));
- break;
- }
- if (isset($de_raw['data']) && empty($de_raw['data'])) {
- Log::debug(self::formatInfo($raffle['raffle_id'], $num, '节奏风暴在小黑屋'));
- self::pauseLock();
- break;
- }
- if ($de_raw['msg'] == '你错过了奖励,下次要更快一点哦~') {
- continue;
- }
- Log::notice(self::formatInfo($raffle['raffle_id'], $num, $de_raw['msg']));
- }
- }
- return [];
- }
-
- /**
- * @use 解析抽奖信息
- * @param array $results
- * @return mixed
- */
- protected static function parseLottery(array $results): mixed
- {
- foreach ($results as $result) {
- $data = $result['source'];
- $content = $result['content'];
- echo '';
- }
- return '';
- }
-}
diff --git a/src/plugin/User.php b/src/plugin/User.php
deleted file mode 100644
index e0b9a2f..0000000
--- a/src/plugin/User.php
+++ /dev/null
@@ -1,356 +0,0 @@
- Common::getUnixTimestamp(),
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- return json_decode($raw, true);
- }
-
-
- /**
- * @use Web User
- * @param null $room_id
- * @return mixed
- */
- public static function webGetUserInfo($room_id = null): mixed
- {
- $url = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByUser';
- $payload = [
- 'room_id' => $room_id ?? getConf('room_id', 'global_room')
- ];
- $raw = Curl::get('pc', $url, $payload);
- return json_decode($raw, true);
- }
-
-
- /**
- * @use App User
- * @param null $room_id
- * @return mixed
- */
- public static function appGetUserInfo($room_id = null): mixed
- {
- $url = 'https://api.live.bilibili.com/xlive/app-room/v1/index/getInfoByUser';
- $payload = [
- 'room_id' => $room_id ?? getConf('room_id', 'global_room')
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- return json_decode($raw, true);
- }
-
- /**
- * @use 转换信息
- * @return array
- */
- #[ArrayShape(['csrf' => "mixed|string", 'uid' => "mixed|string", 'sid' => "mixed|string"])]
- public static function parseCookies(): array
- {
- $cookies = getCookie();
- preg_match('/bili_jct=(.{32})/', $cookies, $token);
- preg_match('/DedeUserID=(\d+)/', $cookies, $uid);
- preg_match('/DedeUserID__ckMd5=(.{16})/', $cookies, $sid);
- return [
- 'csrf' => $token[1] ?? '',
- 'uid' => $uid[1] ?? '',
- 'sid' => $sid[1] ?? '',
- ];
- }
-
- /**
- * @use 获取全部关注列表
- * @return array
- */
- public static function fetchAllFollowings(): array
- {
- $uid = getUid();
- $followings = [];
- for ($i = 1; $i < 100; $i++) {
- $url = "https://api.bilibili.com/x/relation/followings";
- $payload = [
- 'vmid' => $uid,
- 'pn' => $i,
- 'ps' => 50,
- ];
- $headers = [
- 'referer' => "https://space.bilibili.com/$uid/fans/follow?tagid=-1",
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data']['list'])) {
- foreach ($de_raw['data']['list'] as $user) {
- $followings[] = $user['mid'];
- }
- if (count($followings) == $de_raw['data']['total']) {
- break;
- }
- continue;
- }
- break;
- }
- return $followings;
- }
-
- /**
- * @use 获取分组关注列表
- * @param int $tag_id
- * @param int $page_num
- * @param int $page_size
- * @return array
- */
- public static function fetchTagFollowings(int $tag_id = 0, int $page_num = 100, int $page_size = 50): array
- {
- $uid = getUid();
- $followings = [];
- for ($i = 1; $i < $page_num; $i++) {
- $url = "https://api.bilibili.com/x/relation/tag";
- $payload = [
- 'mid' => $uid,
- 'tagid' => $tag_id,
- 'pn' => $i,
- 'ps' => $page_size,
- ];
- $headers = [
- 'referer' => "https://space.bilibili.com/$uid/fans/follow?tagid=$tag_id",
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data'])) {
- foreach ($de_raw['data'] as $user) {
- $followings[] = $user['mid'];
- }
- if (count($de_raw['data']) != $page_size || empty($de_raw['data'])) {
- break;
- }
- continue;
- }
- break;
- }
- return $followings;
- }
-
- /**
- * @use 设置用户关注
- * @param int $follow_uid
- * @param bool $un_follow
- * @return bool
- */
- public static function setUserFollow(int $follow_uid, bool $un_follow = false): bool
- {
- $url = 'https://api.live.bilibili.com/relation/v1/Feed/SetUserFollow';
- $payload = [
- 'uid' => getUid(),
- 'type' => $un_follow ? 0 : 1,
- 'follow' => $follow_uid,
- 're_src' => 18,
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- $headers = [
- 'origin' => 'https://live.bilibili.com',
- 'referer' => 'https://live.bilibili.com/',
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"msg":"success","message":"success","data":[]}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- return true;
- }
- return false;
- }
-
- /**
- * @use 创建关注分组
- * @param string $tag_name
- * @return int
- */
- public static function createRelationTag(string $tag_name): int
- {
- $url = 'https://api.bilibili.com/x/relation/tag/create?cross_domain=true';
- $payload = [
- 'tag' => $tag_name,
- 'csrf' => getCsrf(),
- ];
- $headers = [
- 'content-type' => 'application/x-www-form-urlencoded; charset=UTF-8',
- 'origin' => 'https://live.bilibili.com',
- 'referer' => 'https://link.bilibili.com/iframe/blfe-link-followIframe'
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1,"data":{"tagid":244413}}
- if ($de_raw['code'] == 0 && isset($de_raw['data']['tagid'])) {
- return $de_raw['data']['tagid'];
- }
- return 0;
- }
-
- /**
- * @use 添加用户到分组
- * @param int $fid
- * @param int $tid
- * @return bool
- */
- public static function tagAddUsers(int $fid, int $tid): bool
- {
- $url = 'https://api.bilibili.com/x/relation/tags/addUsers?cross_domain=true';
- $payload = [
- 'fids' => $fid,
- 'tagids' => $tid,
- 'csrf' => getCsrf(),
- ];
- $headers = [
- 'content-type' => 'application/x-www-form-urlencoded; charset=UTF-8',
- 'origin' => 'https://live.bilibili.com',
- 'referer' => 'https://link.bilibili.com/iframe/blfe-link-followIframe'
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- // {"code":0,"message":"0","ttl":1}
- if ($de_raw['code'] == 0) {
- return true;
- }
- return false;
- }
-
- /**
- * @use 获取分组列表
- * @return array
- */
- public static function fetchTags(): array
- {
- $tags = [];
- $url = 'https://api.bilibili.com/x/relation/tags';
- $payload = [];
- $headers = [
- 'referer' => 'https://space.bilibili.com/' . getUid() . '/fans/follow',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data'])) {
- foreach ($de_raw['data'] as $tag) {
- $tags[$tag['tagid']] = $tag['name'];
- }
- }
- return $tags;
- }
-
- /**
- * @use 是否为有效年度大会员
- * @return bool
- */
- public static function isYearVip(): bool
- {
- $url = 'https://api.bilibili.com/x/vip/web/user/info';
- $headers = [
- 'origin' => 'https://account.bilibili.com',
- 'referer' => 'https://account.bilibili.com/account/home'
- ];
- $payload = [];
- $raw = Curl::get('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1,"data":{"mid":1234,"vip_type":2,"vip_status":1,"vip_due_date":1667750400000,"vip_pay_type":0,"theme_type":0,"label":{"text":"年度大会员","label_theme":"annual_vip","text_color":"#FFFFFF","bg_style":1,"bg_color":"#FB7299","border_color":""},"avatar_subscript":1,"avatar_subscript_url":"http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png","nickname_color":"#FB7299","is_new_user":false}}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- if ($de_raw['data']['vip_type'] == 2 && $de_raw['data']['vip_due_date'] > Common::getUnixTimestamp()) {
- Log::debug("获取会员成功 有效年度大会员");
- return true;
- }
- Log::debug("获取会员成功 不是年度大会员或已过期");
- } else {
- Log::debug("获取会员信息失败 $raw");
- }
- return false;
- }
-
- /**
- * @use 我的钱包
- */
- public static function myWallet()
- {
- $url = 'https://api.live.bilibili.com/pay/v2/Pay/myWallet';
- $headers = [
- 'origin' => 'https://link.bilibili.com',
- 'referer' => 'https://link.bilibili.com/p/center/index'
- ];
- $payload = [
- 'need_bp' => 1,
- 'need_metal' => 1,
- 'platform' => 'pc',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- // {"code":0,"msg":"succ","message":"succ","data":{"gold":5074,"silver":37434,"bp":"0","metal":1904}}
- $de_raw = json_decode($raw, true);
- }
-
-}
\ No newline at end of file
diff --git a/src/plugin/VipPrivilege.php b/src/plugin/VipPrivilege.php
deleted file mode 100644
index 40828b2..0000000
--- a/src/plugin/VipPrivilege.php
+++ /dev/null
@@ -1,113 +0,0 @@
- '未知奖励',
- 1 => 'B币劵',
- 2 => '会员购优惠券'
- ];
-
- /**
- * @use run
- */
- public static function run()
- {
- if (self::getLock() > time() || !getEnable('vip_privilege')) {
- return;
- }
- // 如果为年度大会员
- if (User::isYearVip()) {
- $privilege_list = self::myVipPrivilege();
- foreach ($privilege_list as $privilege) {
- // 是否领取状态
- if ($privilege['state'] != 0) {
- continue;
- }
- // 领取奖励
- self::myVipPrivilegeReceive($privilege['type']);
- }
- }
- // 定时11点 + 随机120分钟
- self::setLock(self::timing(11) + mt_rand(1, 120) * 60);
- }
-
- /**
- * @use 领取我的大会员权益
- * @param int $type
- */
- private static function myVipPrivilegeReceive(int $type)
- {
- $url = 'https://api.bilibili.com/x/vip/privilege/receive';
- $headers = [
- 'origin' => 'https://account.bilibili.com',
- 'referer' => 'https://account.bilibili.com/account/big/myPackage',
- ];
- $payload = [
- 'type' => $type,
- 'csrf' => getCsrf(),
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0) {
- Log::notice('大会员权益 ' . self::$privilege[$type] . ' 领取成功');
- } else {
- Log::warning('大会员权益 ' . self::$privilege[$type] . " 领取失败, $raw");
- }
- }
-
- /**
- * @use 获取我的大会员权益列表
- * @return array
- */
- private static function myVipPrivilege(): array
- {
- $url = 'https://api.bilibili.com/x/vip/privilege/my';
- $headers = [
- 'origin' => 'https://account.bilibili.com',
- 'referer' => 'https://account.bilibili.com/account/big/myPackage',
- ];
- $payload = [];
- $raw = Curl::get('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1,"data":{"list":[{"type":1,"state":0,"expire_time":1622476799},{"type":2,"state":0,"expire_time":1622476799}]}}
- $de_raw = json_decode($raw, true);
- if ($de_raw['code'] == 0 && isset($de_raw['data']['list'])) {
- Log::info('获取大会员权益列表成功');
- return $de_raw['data']['list'];
- } else {
- Log::warning("获取大会员权益列表失败 $raw");
- return [];
- }
- }
-}
\ No newline at end of file
diff --git a/src/plugin/ZoneTcpClient.php b/src/plugin/ZoneTcpClient.php
deleted file mode 100644
index f4f7b49..0000000
--- a/src/plugin/ZoneTcpClient.php
+++ /dev/null
@@ -1,698 +0,0 @@
- time() || !getEnable('zone_monitor')) {
- return;
- }
- self::setPauseStatus();
- self::init();
- self::updateConnection();
- self::heartBeat();
- self::receive();
- self::pushHandle();
- }
-
- /**
- * @use 初始化
- */
- private static function init()
- {
- if (empty(getConf('server_addr', 'zone_monitor'))) {
- exit('推送服务器信息不完整, 请检查配置文件!');
- }
- if (!self::$client) {
- self::initConnect();
- }
- }
-
- /**
- * @use 初始化连接
- */
- private static function initConnect()
- {
- $areas = Live::fetchLiveAreas();
- foreach ($areas as $area_id) {
- self::$client_maps["server$area_id"] = ["area_id" => null, "room_id" => null, "client" => null, "heart_beat" => 0, "status" => false];
- self::triggerReConnect([
- 'area_id' => $area_id,
- 'wait_time' => time()
- ], 'Initialization');
- }
- }
-
- /**
- * @use 触发重连
- * @param array $area_data
- * @param string $reason
- */
- private static function triggerReConnect(array $area_data, string $reason)
- {
- Log::debug("Reconnect Reason: {$area_data['area_id']} -> $reason");
- self::$client_maps["server" . $area_data['area_id']]['status'] = false;
- self::$trigger_restart[] = $area_data;
- }
-
- /**
- * @use 更新连接
- */
- private static function updateConnection()
- {
- $num = count(self::$trigger_restart);
- for ($i = 0; $i < $num; $i++) {
- $area_data = array_shift(self::$trigger_restart);
- if (is_null($area_data)) {
- break;
- }
- if (time() < $area_data['wait_time']) {
- self::$trigger_restart[] = $area_data;
- continue;
- }
- Log::notice("update_connections triggered, info: {$area_data['area_id']}");
- $area_info = Live::areaToRid($area_data['area_id']);
-// $area_room_info = [
-// 'area_id' => $area_data['area_id'],
-// 'room_id' => $room_id
-// ];
- self::update($area_info);
- }
- }
-
- /**
- * @use 更新操作
- * @param array $area
- */
- private static function update(array $area)
- {
- self::$area_id = $area['area_id'];
- self::$room_id = $area['room_id'];
- try {
- self::$server = Live::getDanMuInfo(self::$room_id);
- self::$client = (new Factory())->createClient(self::$server['addr'], 40);
- self::$client->setBlocking(false);
- self::sendHandShake();
- self::$client_maps["server" . self::$area_id] = [
- 'client' => self::$client,
- 'area_id' => self::$area_id,
- 'room_id' => self::$room_id,
- 'status' => true,
- 'heart_beat' => time() + 25,
- ];
- // self::$client->getPeerName()
- Log::info("连接到 @分区 {$area['area_id']} @房间 {$area['room_id']} @状态 √ @信息 Successful!");
- } catch (Exception $e) {
- Log::error("连接到 @分区 {$area['area_id']} @房间 {$area['room_id']} @状态 × @信息 {$e->getMessage()}");
- self::triggerReConnect([
- 'area_id' => self::$area_id,
- 'wait_time' => time() + 60
- ], self::formatErr($e));
- }
- }
-
- /**
- * 判断字符串是否为 Json 格式
- * @param string $data Json 字符串
- * @param bool $assoc 是否返回对象or关联数组,默认返回关联数组
- * @return array|bool|object 成功返回转换后的对象或数组,失败返回 false
- */
- private static function analyJson(string $data = '', bool $assoc = true): object|bool|array
- {
- if (is_array($data)) {
- return $data;
- }
- $data = json_decode($data, $assoc);
- if (($data && is_object($data)) || (is_array($data) && !empty($data))) {
- return $data;
- }
- return false;
- }
-
- /**
- * @use 响应数据
- * @param $msg
- * @param $type
- * @return bool
- */
- private static function onMessage($msg, $type): bool
- {
- // 心跳后回复人气
- if ($type == 3) {
- // $num = unpack('N', $msg)[1];
- // Log::info("当前直播间现有 {$num} 人聚众搞基!");
- return false;
- }
- $de_raw = self::analyJson($msg);
- // 进入房间返回
- if (isset($de_raw['code']) && !$de_raw['code']) {
- return false;
- }
- // 部分cmd抽风
- if (!$de_raw || !isset($de_raw['cmd'])) {
- Log::warning("解析错误: $msg");
- return false;
- }
- $data = [];
- $update_room = false;
- switch ($de_raw['cmd']) {
- case 'POPULARITY_RED_POCKET_WINNER_LIST':
- // data -> lot_id|winner_info[] ->uid|name|user_type|award_name
- Log::debug(json_encode($de_raw, true));
- break;
- case 'POPULARITY_RED_POCKET_START':
- // data [0]?-> * sender_uid|sender_name|join_requirement|danmu|lot_status|lot_id|start_time|end_time|awards
- // awards[] -> gift_id|num
- Log::debug(json_encode($de_raw, true));
- // 关联
- if (count(array_filter(array_keys($de_raw['data']), 'is_string')) > 0) {
- // 关联数组
- $temp = $de_raw['data'];
- } else {
- // 索引数组
- $temp = $de_raw['data'][0];
- }
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $temp['lot_id'],
- 'raffle_title' => '利是包',
- 'raffle_type' => 'red_pocket',
- 'source' => $msg
- ];
- break;
- case 'TV_START':
- // 小电视飞船(1)
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => $de_raw['data']['title'],
- 'raffle_type' => 'small_tv',
- 'source' => $msg
- ];
- break;
- case 'SPECIAL_GIFT':
- // 节奏风暴(1)
- if (array_key_exists('39', $de_raw['data'])) {
- if ($de_raw['data']['39']['action'] == 'start') {
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $de_raw['data']['39']['id'],
- 'raffle_title' => '节奏风暴(1)',
- 'raffle_type' => 'storm',
- 'source' => $msg
- ];
- }
- }
- break;
- case 'GUARD_LOTTERY_START':
- // 舰长(1)
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => '总督舰长(1)',
- 'raffle_type' => 'guard',
- 'source' => $msg
- ];
- break;
- case 'GUARD_MSG':
- // 舰长(2)
- // {"buy_type":3,"cmd":"GUARD_MSG","msg":":?淩白夜:? 在本房间开通了舰长"}
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => self::$raffle_id++,
- 'raffle_title' => '总督舰长(2)',
- 'raffle_type' => 'guard',
- 'source' => $msg
- ];
- break;
- case 'LOTTERY_START':
- // 舰长(3)
- $data = [
- 'room_id' => $de_raw['data']['roomid'],
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => '总督舰长(3)',
- 'raffle_type' => 'guard',
- 'source' => $msg
- ];
- break;
- case 'ANCHOR_LOT_START':
- // 天选时刻(1)
- $data = [
- 'room_id' => $de_raw['data']['room_id'],
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => '天选时刻(1)',
- 'raffle_type' => 'anchor',
- 'source' => $msg
- ];
- break;
- case 'PK_LOTTERY_START':
- // PK大乱斗(1)
- $data = [
- 'room_id' => $de_raw['data']['room_id'],
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => 'PK大乱斗(1)',
- 'raffle_type' => 'pk',
- 'source' => $msg
- ];
- break;
- case 'PK_BATTLE_END':
- // PK大乱斗(2)
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $de_raw['pk_id'],
- 'raffle_title' => 'PK大乱斗(2)',
- 'raffle_type' => 'pk',
- 'source' => $msg
- ];
- break;
- case 'RAFFLE_START':
- // 高能抽奖(1)
- $data = [
- 'room_id' => self::$room_id,
- 'raffle_id' => $de_raw['data']['id'],
- 'raffle_title' => $de_raw['data']['title'], // 高能抽奖(1)
- 'raffle_type' => 'raffle',
- 'source' => $msg
- ];
- break;
- case 'NOTICE_MSG':
- $msg_type = $de_raw['msg_type'];
- $msg_self = $de_raw['msg_self'];
- $msg_common = str_replace(' ', '', $de_raw['msg_common']);
- $real_room_id = $de_raw['real_roomid'];
- if (in_array($msg_type, [2, 8])) {
- $data = [
- 'room_id' => $real_room_id,
- 'raffle_id' => self::$raffle_id++,
- 'raffle_title' => $msg_self,
- 'raffle_type' => 'raffle',
- 'source' => $msg
- ];
- // echo self::$room_id . '--' . $real_room_id . PHP_EOL;
- }
- if ($msg_type == 6 && str_contains($msg_common, '节奏风暴')) {
- $data = [
- 'room_id' => $real_room_id,
- 'raffle_id' => self::$raffle_id++,
- 'raffle_title' => '节奏风暴',
- 'raffle_type' => 'raffle',
- 'source' => $msg
- ];
-
- }
- break;
- case 'DANMU_GIFT_LOTTERY_START':
- // 弹幕抽奖(1)
-// $data = [
-// 'room_id' => $de_raw['data']['room_id'],
-// 'raffle_id' => $de_raw['data']['id'],
-// 'raffle_title' => $de_raw['data']['title'],
-// 'raffle_type' => 'raffle',
-// 'source' => $msg
-// ];
- break;
- case 'PREPARING':
- // 房间内下播消息。
- $update_room = true;
- break;
- case 'CUT_OFF':
- // 房间内被下播消息。
- $update_room = true;
- break;
- case 'WARNING':
- // 房间内管理员警告消息。
- $update_room = true;
- break;
- default:
- $data = [];
- break;
- }
- // 下播处理
- if ($update_room) {
- self::triggerReConnect([
- 'area_id' => self::$area_id,
- 'wait_time' => time()
- ], 'Interrupt live broadcast');
- }
- // 处理数据
- if (!empty($data)) {
- unset($data['source']);
- if (!isset(self::$raffle_list[$data['raffle_type']])) {
- self::$raffle_list[$data['raffle_type']] = [];
- }
- $data['area_id'] = self::$area_id;
- self::$raffle_list[$data['raffle_type']][] = $data;
- Log::info("监测到 @分区 {$data['area_id']} @房间 {$data['room_id']} @抽奖 {$data['raffle_title']}");
- // print_r($data);
- }
- return true;
- }
-
- /**
- * @推送到上游处理
- */
- private static function pushHandle()
- {
- foreach (self::$raffle_list as $type => $data) {
- $temp_room_id = 0;
- foreach (self::$raffle_list[$type] as $raffle) {
- if ($temp_room_id != $raffle['room_id']) {
- DataTreating::distribute($raffle);
- $temp_room_id = $raffle['room_id'];
- }
- }
- }
- self::$raffle_list = [];
- }
-
- /**
- * @use 响应关闭
- * @param $client
- */
- private static function onClosed($client)
- {
- }
-
- /**
- * @use 发送握手包
- * @return bool
- */
- private static function sendHandShake(): bool
- {
- return self::writer(self::genHandshakePkg(self::$room_id));
- }
-
- /**
- * @use 心跳包
- * @return string
- */
- #[Pure]
- private static function genHeartBeatPkg(): string
- {
- return self::packMsg('', 0x0002);
- }
-
- /**
- * @use 握手包
- * @param $room_id
- * @return string
- */
- private static function genHandshakePkg($room_id): string
- {
- // {"uid":123,"roomid":123,"protover":3,"platform":"web","type":2,"key":""}
- return self::packMsg(json_encode([
- "uid" => 0,
- "roomid" => intval($room_id),
- "platform" => "web",
- "clientver" => "1.10.6",
- "protover" => 2,
- "type" => 2,
- "key" => self::$server['token'],
- ]), 0x0007);
- }
-
- /**
- * @use 打包数据
- * @param $value
- * @param $option
- * @return string
- */
- private static function packMsg($value, $option): string
- {
- $head = pack('NnnNN', 0x10 + strlen($value), 0x10, 0x01, $option, 0x0001);
- return $head . $value;
- }
-
- /**
- * @use 解包数据
- * @param $value
- * @return array|false
- */
- private static function unPackMsg($value): bool|array
- {
- if (strlen($value) < 4) {
- Log::warning("unPackMsg: 包头异常 " . strlen($value));
- return [];
- }
- // Log::info(json_encode($head, true));
- return unpack('Npacklen/nheadlen/nver/Nop/Nseq', $value);
- }
-
- /**
- * @use 心跳
- */
- private static function heartBeat()
- {
- foreach (self::$client_maps as $key => $client_info) {
- // 如果重连状态 跳过
- if (!$client_info['status']) {
- continue;
- }
- if ($client_info['heart_beat'] > time()) {
- continue;
- }
- self::$client = $client_info['client'];
- self::$area_id = $client_info['area_id'];
- self::$room_id = $client_info['room_id'];
- self::writer(self::genHeartBeatPkg());
- self::$client_maps[$key]['heart_beat'] = time() + 20;
- }
- }
-
- /**
- * @use 读数据
- * @param $length
- * @param bool $is_header
- * @return bool|array|string
- */
- private static function reader($length, bool $is_header = false): bool|array|string
- {
- $data = false;
- try {
- if (self::$client->selectRead(self::$socket_timeout)) {
- $ret = 0;
- $socket = self::$client->getResource();
- while ($length) {
- if ($length < 1 || $length > 65535) {
- Log::warning("Socket error: [$ret] [$length]" . PHP_EOL);
- throw new Exception("Socket error: [$ret] [$length]");
- }
- $cnt = 0;
- $w = NULL;
- $e = NULL;
- while ($cnt++ < 60) {
- // reset read fdset when timeout
- $r = array($socket);
- $ret = socket_select($r, $w, $e, 1);
- if ($ret === false)
- throw new Exception("Socket error: ret == false");
- if ($ret)
- break;
- Log::debug("Socket debug: select timeout" . PHP_EOL);
- }
- // Todo unable to read from socket[104]: Connection reset by peer
- $ret = socket_recv($socket, $buffer, $length, 0);
- if ($ret < 1) {
- Log::warning("Socket error: [$ret] [$length]" . PHP_EOL);
- throw new Exception("Socket error: [$ret] [$length]");
- }
- $data .= $buffer;
- unset($buffer);
- $length -= $ret;
- }
- if ($is_header) $data = self::unPackMsg($data);
- }
- } catch (Exception $e) {
- self::triggerReConnect([
- 'area_id' => self::$area_id,
- 'wait_time' => time() + 60
- ], self::formatErr($e));
- $data = false;
- }
- return $data;
- }
-
- /**
- * @use 写数据
- * @param $data
- * @return bool
- */
- private static function writer($data): bool
- {
- $status = false;
- try {
- while (self::$client->selectWrite(self::$socket_timeout)) {
- $status = self::$client->write($data);
- break;
- }
- } catch (Exception $e) {
- self::triggerReConnect([
- 'area_id' => self::$area_id,
- 'wait_time' => time() + 60
- ], self::formatErr($e));
- }
- return $status;
- }
-
- /**
- * @use 读取数据
- */
- private static function receive()
- {
- foreach (self::$client_maps as $client_info) {
- // 如果重连状态 就跳过
- if (!$client_info['status']) {
- continue;
- }
- self::$client = $client_info['client'];
- self::$area_id = $client_info['area_id'];
- self::$room_id = $client_info['room_id'];
- $head = self::reader(16, true);
- if (!$head) {
- // 长度为0 ,空信息
- continue;
- }
- $length = $head['packlen'] ?? 16;
- $type = $head['op'] ?? 0x0000;
- $len_body = $length - 16;
- Log::debug("(AreaId={$client_info['area_id']} -> RoomId={$client_info['room_id']} -> Len=$len_body)");
- if (!$len_body)
- continue;
- $body = self::reader($len_body);
- if ($body) {
- if ($head['ver'] == 2) {
- $data_list = self::v2_split($body, $len_body);
- foreach ($data_list as $body) {
- self::onMessage($body, $type);
- }
- } else {
- self::onMessage($body, $type);
- }
- }
- }
- }
-
- /**
- * @use V2切割
- * @param $bin
- * @param $total
- * @return array
- */
- private static function v2_split($bin, $total): array
- {
- $list = [];
- $step = 0;
- $data = gzuncompress($bin);
- $total = strlen($data);
- while (true) {
- if ($step > 165535) {
- Log::warning("v2_split: 数据step异常 $step");
- break;
- }
- if ($step == $total) break;
- $bin = substr($data, $step, 16);
- $head = self::unPackMsg($bin);
- $length = $head['packlen'] ?? 16;
- $body = substr($data, $step + 16, $length - 16);
- $step += $length;
- $list[] = $body;
- }
- return $list;
- }
-
- /**
- * @param $object
- * @return string
- */
- private static function getClass($object): string
- {
- $class = get_class($object);
- return 'c' === $class[0] && str_starts_with($class, "class@anonymous\0") ? get_parent_class($class) . '@anonymous' : $class;
- }
-
- /**
- * @param $e
- * @return string
- */
- private static function formatErr($e): string
- {
- return sprintf('Uncaught Exception %s: "%s" at %s line %s', self::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine());
- }
-
- /*
- * @use replace delay by select
- */
- public static function Delayed(): Delayed
- {
- $r = [];
- $w = NULL;
- $e = NULL;
- $delay = 0;
- if (self::getLock() > time())
- return new Delayed(1000);
- try {
- foreach (self::$client_maps as $client_info) {
- if ($client_info['client'])
- $r[] = $client_info['client']->getResource();
- }
- if (count($r) !== 0)
- socket_select($r, $w, $e, 1);
- else
- $delay = 50;
- } catch (Exception $exception) {
- $delay = 1000;
- }
- return new Delayed($delay);
- }
-}
diff --git a/src/plugins/BasePlugin.php b/src/plugins/BasePlugin.php
deleted file mode 100644
index bcb3d85..0000000
--- a/src/plugins/BasePlugin.php
+++ /dev/null
@@ -1,120 +0,0 @@
-_view;
- }
-
- /**
- * @use 视图
- * @param mixed $view
- */
- public function setView(mixed $view): void
- {
- $this->_view = $view;
- }
-
- /**
- * @use 渲染视图
- * @param $view
- * @param array $params
- */
- public function render($view, array $params = [])
- {
-
- }
-
- /**
- * @use 渲染视图文件
- * @param $file
- * @param array $params
- */
- public function renderFile($file, array $params = [])
- {
- }
-
- /**
- * @use 视图路径
- */
- public function getViewPath()
- {
- }
-
- /**
- * @use 安装
- * @return bool
- */
- public function install(): bool
- {
- return true;
- }
-
- /**
- * @use 卸载
- * @return bool
- */
- public function uninstall(): bool
- {
- return true;
- }
-
- /**
- * @use 开启
- * @return bool
- */
- public function open(): bool
- {
- return true;
- }
-
- /**
- * @use 关闭
- * @return bool
- */
- public function close(): bool
- {
- return true;
- }
-
- /**
- * @use 更新
- * @return bool
- */
- public function upgrade(): bool
- {
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/plugins/Plugins.php b/src/plugins/Plugins.php
deleted file mode 100644
index 9dfd498..0000000
--- a/src/plugins/Plugins.php
+++ /dev/null
@@ -1,169 +0,0 @@
-detector();
- }
-
- /**
- * @use 防止使用clone克隆对象
- */
- private function __clone()
- {
- }
-
- /**
- * @use Singleton
- * @param mixed ...$args
- * @return \BiliHelper\Plugins\Plugins
- */
- public static function getInstance(...$args): Plugins
- {
- // 判断$instance是否是Singleton的对象,不是则创建
- if (!self::$instance instanceof self) {
- self::$instance = new self(...$args);
- }
- return self::$instance;
- }
-
- /**
- * @use 初始化所有插件类
- * @access public
- * @return void
- */
- public function detector()
- {
- //主要功能为将插件需要执行功能放入 $_staff
- $plugins = $this->get_active_plugins();
-
- if ($plugins) {
- foreach ($plugins as $plugin) {
- // 这里将所有插件践行初始化
- // 路径请自己注意
- if (@file_exists($plugin['path'])) {
- include_once($plugin['path']);
- // 此时设定 文件夹名称 文件名称 类名 是统一的 如果想设定不统一请自己在get_active_plugins()内进行实现
- $class = $plugin['name'];
- if (class_exists($class)) {
- // 初始化所有插件类
- new $class($this);
- }
- }
- }
- }
- }
-
- /**
- * 这里是在插件中使用的方法 用来注册插件
- *
- * @param string $hook
- * @param object $class_name
- * @param string $method
- */
- public function register(string $hook, object &$class_name, string $method)
- {
- // 获取类名和方法名链接起来做下标
- $func_class = get_class($class_name) . '->' . $method;
- // 将类和方法放入监听数组中 以$func_class做下标
- $this->_staff[$hook][$func_class] = array(&$class_name, $method);
-
- }
-
- /**
- * 这个是全局使用的触发钩子动作方法
- *
- * @param string $hook
- * @param string $data
- * @return string
- */
- public function trigger(string $hook, string $data = ''): string
- {
- // 首先需要判断一下$hook 存不存在
-
- if (isset($this->_staff[$hook]) && is_array($this->_staff[$hook]) && count($this->_staff[$hook]) > 0) {
- $plugin_func_result = '';
- // 如果存在定义 $plugin_func_result
- foreach ($this->_staff[$hook] as $staff) {
- // 如果只是记录 请不要返回
- $plugin_func_result = '';
- $class = &$staff[0]; // 引用过来的类
- $method = $staff[1]; // 类下面的方法
- if (method_exists($class, $method)) {
- $func_result = $class->$method($data);
- if (is_numeric($func_result)) {
- // 这里判断返回值是不是字符串,如果不是将不进行返回到页面上
- $plugin_func_result .= $func_result;
- }
-
- }
- }
- }
-
- return $plugin_func_result ?? '';
- }
-
- /**
- * 获取插件信息
- */
- public function get_active_plugins(): array
- {
- // 既假定了插件在根目录的/plugin
- // 我们再次假定插件的入口和插件文件夹的名字是一样的
- // 既假定了插件在根目录的/plugin
- // 注意:这个执行文件我放在了根目录 以下路径请根据实际情况获取
-
- $plugin_dir_path = '.' . DIRECTORY_SEPARATOR . 'plugin' . DIRECTORY_SEPARATOR;
-
- $plugin_dir_name_arr = scandir($plugin_dir_path);
-
- $plugins = array();
- foreach ($plugin_dir_name_arr as $k => $v) {
- if ($v == "." || $v == "..") {
- continue;
- }
- if (is_dir($plugin_dir_path . $v)) {
- $path = $plugin_dir_path . $v . DIRECTORY_SEPARATOR . $v . '.php';
- $plugins[] = ['name' => $v, 'path' => $path];
- }
-
- }
- return $plugins;
- }
-
-
-
-
-
-}
\ No newline at end of file
diff --git a/src/script/BaseTask.php b/src/script/BaseTask.php
deleted file mode 100644
index 69bda25..0000000
--- a/src/script/BaseTask.php
+++ /dev/null
@@ -1,78 +0,0 @@
-choice('Select', $options, $default, true);
- static::interactor()->greenBold("You selected: $options[$option]", true);
- // return $options[$option];
- return $option;
- }
-
- /**
- * @use 确认
- * @param string $msg
- * @param string $default
- * @return bool
- */
- public static function confirm(string $msg, string $default = 'n'): bool
- {
- $confirm = static::interactor()->confirm($msg, $default); // Default: n (no)
- if (!$confirm) die();
-// if ($confirm) { // is a boolean
-// static::interactor()->greenBold('是 :)', true); // Output green bold text
-// } else {
-// static::interactor()->redBold('否 :(', true); // Output red bold text
-// die();
-// }
- return true;
- }
-
- /**
- * @return \Ahc\Cli\IO\Interactor
- */
- public static function interactor(): Interactor
- {
- return static::$interactor ?? static::$interactor = new Interactor;
- }
-}
\ No newline at end of file
diff --git a/src/script/UnFollow.php b/src/script/UnFollow.php
deleted file mode 100644
index 1a1facd..0000000
--- a/src/script/UnFollow.php
+++ /dev/null
@@ -1,162 +0,0 @@
- $up_uname) {
- $payload = [
- 'fid' => $up_uid,
- 'act' => 2,
- 're_src' => 11,
- 'csrf' => getCsrf(),
- ];
- $headers = [
- 'origin' => 'https://space.bilibili.com',
- 'referer' => 'https://space.bilibili.com/' . getUid() . '/fans/follow?tagid=' . $tag_id,
- ];
- $raw = Curl::post('pc', $url, $payload, $headers);
- // {"code":0,"message":"0","ttl":1}
- $data = json_decode($raw, true);
- if ($data['code'] == 0) {
- Log::notice("UP.$up_uid - $up_uname 取关成功");
- } else {
- Log::error("UP.$up_uid - $up_uname 取关失败 CODE -> {$data['code']} MSG -> {$data['message']} ");
- break;
- }
- sleep(random_int(5, 10));
- }
- }
-
-
- /**
- * @use 获取分组关注
- * @param $tag_id
- * @param int $max_pn
- * @param int $max_ps
- * @return array
- * @throws \Exception
- */
- private static function relationTag($tag_id, int $max_pn = 60, int $max_ps = 20): array
- {
- $following = [];
- $url = 'https://api.bilibili.com/x/relation/tag';
- for ($pn = 1; $pn <= $max_pn; $pn++) {
- $payload = [
- 'mid' => getUid(),
- 'tagid' => $tag_id,
- 'pn' => $pn,
- 'ps' => $max_ps,
- ];
- $headers = [
- 'referer' => 'https://space.bilibili.com/' . getUid() . '/fans/follow',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $data = json_decode($raw, true);
- if ($data['code'] == 0 && isset($data['data'])) {
- // 循环添加到 following
- foreach ($data['data'] as $up) {
- $following[$up['mid']] = $up['uname'];
- }
- // 打印和延迟
- Log::info("已获取分组 $tag_id 页码 $pn");
- sleep(random_int(4, 8));
- // 如果页面不等于 max_ps 跳出
- if (count($data['data']) != $max_ps) {
- break;
- }
- } else {
- Log::error("获取分组关注失败 CODE -> {$data['code']} MSG -> {$data['message']} ");
- break;
- }
- }
- $following_num = count($following);
- Log::notice("已获取分组 $tag_id 有效关注数 $following_num");
- return $following;
- }
-
-
- /**
- * @use 获取分组
- * @return mixed
- */
- private static function relationTags(): mixed
- {
- $url = 'https://api.bilibili.com/x/relation/tags';
- $payload = [];
- $headers = [
- 'referer' => 'https://space.bilibili.com/',
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- $data = json_decode($raw, true);
- if ($data['code'] == 0 && isset($data['data'])) {
- $options = [];
- foreach ($data['data'] as $tag) {
- $options[$tag['tagid']] = "分组:{$tag['name']} - 关注数:{$tag['count']}";
- }
- $option = self::choice($options);
- Log::notice("已获取分组 $option - $options[$option]");
- return $option;
- } else {
- Log::error("获取关注分组失败 CODE -> {$data['code']} MSG -> {$data['message']} ");
- die();
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/script/User.php b/src/script/User.php
deleted file mode 100644
index 7b3613a..0000000
--- a/src/script/User.php
+++ /dev/null
@@ -1,72 +0,0 @@
- {$data['code']} MSG -> {$data['message']} ");
- die();
- }
- return true;
- }
-
- /**
- * @use 用户
- * @return mixed
- */
- public static function userInfo(): mixed
- {
- $url = 'https://api.bilibili.com/x/web-interface/nav';
- $payload = [];
- $headers = [
- 'origin' => 'https://space.bilibili.com',
- 'referer' => 'https://space.bilibili.com/' . getUid(),
- ];
- $raw = Curl::get('pc', $url, $payload, $headers);
- return json_decode($raw, true);
- }
-
-
-}
\ No newline at end of file
diff --git a/src/tool/ArrayToTextTable.php b/src/tool/ArrayToTextTable.php
deleted file mode 100644
index e15a542..0000000
--- a/src/tool/ArrayToTextTable.php
+++ /dev/null
@@ -1,222 +0,0 @@
-setData($data)
- ->setIndentation('')
- ->setKeysAlignment(self::AlignCenter)
- ->setValuesAlignment(self::AlignLeft)
- ->setFormatter(null);
- }
-
- public function __toString()
- {
- return $this->getTable();
- }
-
- public function getTable($data = null): string
- {
- if (!is_null($data))
- $this->setData($data);
-
- $data = $this->prepare();
-
- $i = $this->indentation;
-
- $table = $i . $this->line('┌', '─', '┬', '┐') . PHP_EOL;
-
- if ($this->displayHeader) {
- //绘制table header
- $headerRows = array_combine($this->keys, $this->keys);
- $table .= $i . $this->row($headerRows, $this->keysAlignment) . PHP_EOL;
- $table .= $i . $this->line('├', '─', '┼', '┤') . PHP_EOL;
- }
-
- foreach ($data as $row) {
- $table .= $i . $this->row($row, $this->valuesAlignment) . PHP_EOL;
- }
- $table .= $i . $this->line('└', '─', '┴', '┘') . PHP_EOL;
-
- return $table;
- }
-
- public function setIndentation($indentation): static
- {
- $this->indentation = $indentation;
- return $this;
- }
-
- public function isDisplayHeader(bool $displayHeader): static
- {
- $this->displayHeader = $displayHeader;
- return $this;
- }
-
- public function setKeysAlignment($keysAlignment): static
- {
- $this->keysAlignment = $keysAlignment;
- return $this;
- }
-
- public function setValuesAlignment($valuesAlignment): static
- {
- $this->valuesAlignment = $valuesAlignment;
- return $this;
- }
-
- public function setFormatter($formatter): static
- {
- $this->formatter = $formatter;
- return $this;
- }
-
- private function line($left, $horizontal, $link, $right): string
- {
- $line = $left;
- foreach ($this->keys as $key) {
- $line .= str_repeat($horizontal, $this->widths[$key] + 2) . $link;
- }
-
- if (mb_strlen($line) > mb_strlen($left)) {
- $line = mb_substr($line, 0, -mb_strlen($horizontal));
- }
- return $line . $right;
- }
-
- private function row($row, $alignment): string
- {
- $line = '│';
- foreach ($this->keys as $key) {
- $value = $row[$key] ?? '';
- $line .= ' ' . static::mb_str_pad($value, $this->widths[$key], ' ', $alignment) . ' ' . '│';
- }
- if (empty($row)) {
- $line .= '│';
- }
- return $line;
- }
-
- private function prepare(): array
- {
- $this->keys = [];
- $this->widths = [];
- $data = $this->data;
-
- //合并全部数组的key
- foreach ($data as $row) {
- $this->keys = array_merge($this->keys, array_keys($row));
- }
- $this->keys = array_unique($this->keys);
-
- //补充缺陷数组
- foreach ($data as $index => $row) {
- foreach ($this->keys as $key) {
- if (!array_key_exists($key, $row)) {
- $data[$index][$key] = null;
- }
- }
- }
-
- //执行formatter
- if ($this->formatter instanceof \Closure) {
- foreach ($data as &$row) {
- array_walk($row, $this->formatter);
- }
- unset($row);
- }
-
- foreach ($this->keys as $key) {
- $this->setWidth($key, $key);
- }
- foreach ($data as $row) {
- foreach ($row as $columnKey => $columnValue) {
- $this->setWidth($columnKey, $columnValue);
- }
- }
- return $data;
- }
-
- private function setWidth($key, $value)
- {
- if (!isset($this->widths[$key])) {
- $this->widths[$key] = 0;
- }
- // Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated
- // Deprecated: mb_strlen(): Passing null to parameter #1 ($string) of type string is deprecated
- $value = $value ?: '';
- $width = (strlen($value) + mb_strlen($value, 'UTF8')) / 2;
- if ($width > $this->widths[$key]) {
- $this->widths[$key] = $width;
- }
- }
-
- private static function countCJK($string): bool|int|null
- {
- return preg_match_all('/[\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}]/u', $string);
- }
-
- private function mb_str_pad($input, $pad_length, $pad_string = ' ', $pad_type = STR_PAD_RIGHT, $encoding = null): string
- {
- // $encoding = $encoding === null ? mb_internal_encoding() : $encoding;
- // $diff = strlen($input) - (strlen($input) + mb_strlen($input, $encoding)) / 2;
- // return str_pad($input, $pad_length + $diff, $pad_string, $pad_type);
-
- // https://github.com/viossat/arraytotexttable/blob/6b1af924478cb9c3a903269e304fff006fe0dbf4/src/ArrayToTextTable.php#L255
- $encoding = $encoding === null ? mb_internal_encoding() : $encoding;
- $pad_before = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_LEFT;
- $pad_after = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_RIGHT;
- $pad_length -= mb_strlen($input, $encoding) + static::countCJK($input);
- $target_length = $pad_before && $pad_after ? $pad_length / 2 : $pad_length;
-
- $repeat_times = ceil($target_length / mb_strlen($pad_string, $encoding));
- $repeated_string = str_repeat($pad_string, max(0, $repeat_times));
- $before = $pad_before ? mb_substr($repeated_string, 0, floor($target_length), $encoding) : '';
- $after = $pad_after ? mb_substr($repeated_string, 0, ceil($target_length), $encoding) : '';
-
- return $before . $input . $after;
-
- }
-
- private function setData($data): static
- {
- if (!is_array($data)) {
- $data = [];
- }
- $arrayData = [];
- foreach ($data as $row) {
- if (is_array($row)) {
- $arrayData[] = $row;
- } else if (is_object($row)) {
- $arrayData[] = get_object_vars($row);
- }
- }
- $this->data = $arrayData;
- return $this;
- }
-}
\ No newline at end of file
diff --git a/src/tool/BvToAv.php b/src/tool/BvToAv.php
deleted file mode 100644
index b596e58..0000000
--- a/src/tool/BvToAv.php
+++ /dev/null
@@ -1,53 +0,0 @@
-tr));
- for ($i = 0; $i < 6; $i++) {
- $r += $tr[$bv[$this->s[$i]]] * (pow(58, $i));
- }
- return ($r - $this->add) ^ $this->xor;
- }
-
-
- /**
- * @use AV 转 BV
- * @param $av
- * @return string
- */
- public function enc($av): string
- {
- $tr = str_split($this->tr);
- $bv = 'BV1 4 1 7 ';
- $av = ($av ^ $this->xor) + $this->add;
- for ($i = 0; $i < 6; $i++) {
- $bv[$this->s[$i]] = $tr[floor($av / pow(58, $i) % 58)];
- }
- return $bv;
- }
-}
\ No newline at end of file
diff --git a/src/tool/DumpMemory.php b/src/tool/DumpMemory.php
deleted file mode 100644
index b10157e..0000000
--- a/src/tool/DumpMemory.php
+++ /dev/null
@@ -1,25 +0,0 @@
- basename($filename),
- "文件类型" => filetype($filename),
- "文件大小" => static::transByte(filesize($filename)),
- "创建时间" => date('Y-m-d H:i:s', filectime($filename)),
- "修改时间" => date('Y-m-d H:i:s', filemtime($filename)),
- "上一次访问时间" => date('Y-m-d H:i:s', fileatime($filename)),
- ];
- }
-
-
- /**
- * @use 转换字节大小
- * @param int $byte 字节大小
- * @param int $precision 小数点保留位数
- * @return string 转换后的单位
- */
- public static function transByte(int $byte, int $precision = 2): string
- {
- $kb = 1024;
- $mb = 1024 * $kb;
- $gb = 1024 * $mb;
- $tb = 1024 * $gb;
-
- if ($byte < $kb) {
- return $byte . 'B';
- }
-
- if ($byte < $mb) {
- // 默认四舍五入, 保留两位小数
- return round($byte / $kb, $precision) . ' KB';
- }
-
- if ($byte < $gb) {
- return round($byte / $mb, $precision) . ' MB';
- }
-
- if ($byte < $tb) {
- return round($byte / $tb, $precision) . ' GB';
- }
- return '';
- }
-
-
- /**
- * @use 以字符串形式读取内容
- * @param string $filename
- * @return false|string
- */
- public static function readString(string $filename): bool|string
- {
- if (is_file($filename) && is_readable($filename)) {
- return file_get_contents($filename);
- }
- return false;
- }
-
-
- /**
- * @use 以数组形式读取内容
- * @param string $filename
- * @param bool $skip_empty_lines
- * @return array|false
- */
- public static function readArray(string $filename, bool $skip_empty_lines = false): bool|array
- {
- if (is_file($filename) && is_readable($filename)) {
- if ($skip_empty_lines) {
- // 忽略空行读取
- return file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- } else {
- // 以数组形式直接读取, 不忽略空行
- return file($filename);
- }
- }
- return false;
- }
-
-
- /**
- * @use 增加文件内容升级版
- * @param string $filename 路径名称
- * @param mixed $data 需要写入的数据
- * @param boolean $clear_content 是否清空原始内容再写入
- * @return bool true|false
- */
- public static function write(string $filename, mixed $data, bool $clear_content = false): bool
- {
- $srcData = '';
- $dirname = dirname($filename);
- // 检测目标路径是否存在
- if (!file_exists($dirname)) {
- mkdir($dirname, 0777, true);
- }
- // 文件存在并且不清空原始文件
- if (is_file($filename) && !$clear_content) {
- $srcData = file_get_contents($filename);
- }
-
- // 检测数据是否为数组或者对象
- if (is_array($data) || is_object($data)) {
- // 序列化数据
- $data = serialize($data);
- }
- // 拼装数据
- $data = $srcData . $data;
- // 写入数据
- if (file_put_contents($filename, $data) !== false) {
- return true;
- }
- return false;
- }
-
- /**
- * @use 截断文本
- * @param string $filename 文件名称
- * @param int $length 截断文本长度
- * @return boolean true|false
- */
- public static function truncate(string $filename, int $length): bool
- {
- // 判断文件是否存在并且是可写的
- if (is_file($filename) && is_writeable($filename)) {
- // 创建文件句柄, 以读写方式打开
- $handler = fopen($filename, 'rb+');
- $length = max($length, 0);
- ftruncate($handler, $length);
- fclose($handler);
- }
- return false;
- }
-
-}
diff --git a/src/tool/Path.php b/src/tool/Path.php
deleted file mode 100644
index be77e3a..0000000
--- a/src/tool/Path.php
+++ /dev/null
@@ -1,37 +0,0 @@
- [
- 'GT-I9:number2-5:00 Build/JDQ39',
- 'Nokia 3:number1-3:[10|15] Build/IMM76D',
- '[SAMSUNG |]SM-G3:number1-5:0[R5|I|V|A|T|S] Build/JLS36C',
- 'Ascend G3:number0-3:0 Build/JLS36I',
- '[SAMSUNG |]SM-G3:number3-6::number1-8::number0-9:[V|A|T|S|I|R5] Build/JLS36C',
- 'HUAWEI G6-L:number10-11: Build/HuaweiG6-L:number10-11:',
- '[SAMSUNG |]SM-[G|N]:number7-9:1:number0-8:[S|A|V|T] Build/[JLS36C|JSS15J]',
- '[SAMSUNG |]SGH-N0:number6-9:5[T|V|A|S] Build/JSS15J',
- 'Samsung Galaxy S[4|IV] Mega GT-I:number89-95:00 Build/JDQ39',
- 'SAMSUNG SM-T:number24-28:5[s|a|t|v] Build/[JLS36C|JSS15J]',
- 'HP :number63-73:5 Notebook PC Build/[JLS36C|JSS15J]',
- 'HP Compaq 2:number1-3:10b Build/[JLS36C|JSS15J]',
- 'HTC One 801[s|e] Build/[JLS36C|JSS15J]',
- 'HTC One max Build/[JLS36C|JSS15J]',
- 'HTC Xplorer A:number28-34:0[e|s] Build/GRJ90'
- ],
- '4.4' => [
- 'XT10:number5-8:0 Build/SU6-7.3',
- 'XT10:number12-52: Build/[KXB20.9|KXC21.5]',
- 'Nokia :number30-34:10 Build/IMM76D',
- 'E:number:20-23::number0-3::number0-4: Build/24.0.[A|B].1.34',
- '[SAMSUNG |]SM-E500[F|L] Build/KTU84P',
- 'LG Optimus G Build/KRT16M',
- 'LG-E98:number7-9: Build/KOT49I',
- 'Elephone P:number2-6:000 Build/KTU84P',
- 'IQ450:number0-4: Quad Build/KOT49H',
- 'LG-F:number2-5:00[K|S|L] Build/KOT49[I|H]',
- 'LG-V:number3-7::number0-1:0 Build/KOT49I',
- '[SAMSUNG |]SM-J:number1-2::number0-1:0[G|F] Build/KTU84P',
- '[SAMSUNG |]SM-N80:number0-1:0 Build/[KVT49L|JZO54K]',
- '[SAMSUNG |]SM-N900:number5-8: Build/KOT49H',
- '[SAMSUNG-|]SGH-I337[|M] Build/[JSS15J|KOT49H]',
- '[SAMSUNG |]SM-G900[W8|9D|FD|H|V|FG|A|T] Build/KOT49H',
- '[SAMSUNG |]SM-T5:number30-35: Build/[KOT49H|KTU84P]',
- '[Google |]Nexus :number5-7: Build/KOT49H',
- 'LG-H2:number0-2:0 Build/KOT49[I|H]',
- 'HTC One[_M8|_M9|0P6B|801e|809d|0P8B2|mini 2|S][ dual sim|] Build/[KOT49H|KTU84L]',
- '[SAMSUNG |]GT-I9:number3-5:0:number0-6:[V|I|T|N] Build/KOT49H',
- 'Lenovo P7:number7-8::number1-6: Build/[Lenovo|JRO03C]',
- 'LG-D95:number1-8: Build/KOT49[I|H]',
- 'LG-D:number1-8::number0-8:0 Build/KOT49[I|H]',
- 'Nexus5 V:number6-7:.1 Build/KOT49H',
- 'Nexus[_|] :number4-10: Build/[KOT49H|KTU84P]',
- 'Nexus[_S_| S ][4G |]Build/GRJ22',
- '[HM NOTE|NOTE-III|NOTE2 1LTE[TD|W|T]',
- 'ALCATEL ONE[| ]TOUCH 70:number2-4::number0-9:[X|D|E|A] Build/KOT49H',
- 'MOTOROLA [MOTOG|MSM8960|RAZR] Build/KVT49L'
- ],
- '5.0' => [
- 'Nokia :number10-11:00 [wifi|4G|LTE] Build/GRK39F',
- 'HTC 80:number1-2[s|w|e|t] Build/[LRX22G|JSS15J]',
- 'Lenovo A7000-a Build/LRX21M;',
- 'HTC Butterfly S [901|919][s|d|] Build/LRX22G',
- 'HTC [M8|M9|M8 Pro Build/LRX22G',
- 'LG-D3:number25-37: Build/LRX22G',
- 'LG-D72:number0-9: Build/LRX22G',
- '[SAMSUNG |]SM-G4:number0-9:0 Build/LRX22[G|C]',
- '[|SAMSUNG ]SM-G9[00|25|20][FD|8|F|F-ORANGE|FG|FQ|H|I|L|M|S|T] Build/[LRX21T|KTU84F|KOT49H]',
- '[SAMSUNG |]SM-A:number7-8:00[F|I|T|H|] Build/[LRX22G|LMY47X]',
- '[SAMSUNG-|]SM-N91[0|5][A|V|F|G|FY] Build/LRX22C',
- '[SAMSUNG |]SM-[T|P][350|550|555|355|805|800|710|810|815] Build/LRX22G',
- 'LG-D7:number0-2::number0-9: Build/LRX22G',
- '[LG|SM]-[D|G]:number8-9::number0-5::number0-9:[|P|K|T|I|F|T1] Build/[LRX22G|KOT49I|KVT49L|LMY47X]'
- ],
- '5.1' => [
- 'Nexus :number5-9: Build/[LMY48B|LRX22C]',
- '[|SAMSUNG ]SM-G9[28|25|20][X|FD|8|F|F-ORANGE|FG|FQ|H|I|L|M|S|T] Build/[LRX22G|LMY47X]',
- '[|SAMSUNG ]SM-G9[35|350][X|FD|8|F|F-ORANGE|FG|FQ|H|I|L|M|S|T] Build/[MMB29M|LMY47X]',
- '[MOTOROLA |][MOTO G|MOTO G XT1068|XT1021|MOTO E XT1021|MOTO XT1580|MOTO X FORCE XT1580|MOTO X PLAY XT1562|MOTO XT1562|MOTO XT1575|MOTO X PURE XT1575|MOTO XT1570 MOTO X STYLE] Build/[LXB22|LMY47Z|LPC23|LPK23|LPD23|LPH223]'
- ],
- '6.0' => [
- '[SAMSUNG |]SM-[G|D][920|925|928|9350][V|F|I|L|M|S|8|I] Build/[MMB29K|MMB29V|MDB08I|MDB08L]',
- 'Nexus :number5-7:[P|X|] Build/[MMB29K|MMB29V|MDB08I|MDB08L]',
- 'HTC One[_| ][M9|M8|M8 Pro] Build/MRA58K',
- 'HTC One[_M8|_M9|0P6B|801e|809d|0P8B2|mini 2|S][ dual sim|] Build/MRA58K'
- ],
- '7.0' => [
- 'Pixel [XL|C] Build/[NRD90M|NME91E]',
- 'Nexus :number5-9:[X|P|] Build/[NPD90G|NME91E]',
- '[SAMSUNG |]GT-I:number91-98:00 Build/KTU84P',
- 'Xperia [V |]Build/NDE63X',
- 'LG-H:number90-93:0 Build/NRD90[C|M]'
- ],
- '7.1' => [
- 'Pixel [XL|C] Build/[NRD90M|NME91E]',
- 'Nexus :number5-9:[X|P|] Build/[NPD90G|NME91E]',
- '[SAMSUNG |]GT-I:number91-98:00 Build/KTU84P',
- 'Xperia [V |]Build/NDE63X',
- 'LG-H:number90-93:0 Build/NRD90[C|M]'
- ]
- ];
- public string $locale = 'en-US';
- /**
- * List of "OS" strings used for android
- * @var array $android_os
- */
- public array $android_os = [
- 'Linux; Android :androidVersion:; :androidDevice:',
- //Todo: Add a $windowsDevices variable that does the same as androidDevice
- //'Windows Phone 10.0; Android :androidVersion:; :windowsDevice:',
- 'Linux; U; Android :androidVersion:; :androidDevice:',
- 'Android; Android :androidVersion:; :androidDevice:'
- ];
- /**
- * List of "OS" strings used for iOS
- * @var array $mobile_ios
- */
- public array $mobile_ios = [
- 'iphone' => 'iPhone; CPU iPhone OS :number7-11:_:number0-9:_:number0-9:; like Mac OS X;',
- 'ipad' => 'iPad; CPU iPad OS :number7-11:_:number0-9:_:number0-9: like Mac OS X;',
- 'ipod' => 'iPod; CPU iPod OS :number7-11:_:number0-9:_:number0-9:; like Mac OS X;'
- ];
-
- /**
- * Get a random operating system
- * @param string|null $os
- * @return array|string|null *
- * @throws \Exception
- */
- public function getOS(string $os = NULL): array|string|null
- {
- $_os = [];
- if ($os === NULL || in_array($os, ['chrome', 'firefox', 'explorer'])) {
- $_os = $os === 'explorer' ? $this->windows_os : array_merge($this->windows_os, $this->linux_os, $this->mac_os);
- } else {
- $_os += $this->{$os . '_os'};
- }
- // randomly select on operating system
- $selected_os = rtrim($_os[random_int(0, count($_os) - 1)], ';');
-
- // check for spin syntax
- if (str_contains($selected_os, '[')) {
- $selected_os = self::processSpinSyntax($selected_os);
- }
-
- // check for random number syntax
- if (str_contains($selected_os, ':number')) {
- $selected_os = self::processRandomNumbers($selected_os);
- }
-
- if (random_int(1, 100) > 50) {
- $selected_os .= '; ' . $this->locale;
- }
- return $selected_os;
- }
-
- /**
- * Get Mobile OS
- * @param string|null $os Can specifiy android, iphone, ipad, ipod, or null/blank for random
- * @return array|string|null *
- * @throws \Exception
- */
- public function getMobileOS(string $os = NULL): array|string|null
- {
- $os = strtolower($os);
- $_os = [];
- switch ($os) {
- case'android':
- $_os += $this->android_os;
- break;
- case 'iphone':
- case 'ipad':
- case 'ipod':
- $_os[] = $this->mobile_ios[$os];
- break;
- default:
- $_os = array_merge($this->android_os, array_values($this->mobile_ios));
- }
- // select random mobile os
- $selected_os = rtrim($_os[random_int(0, count($_os) - 1)], ';');
- if (str_contains($selected_os, ':androidVersion:')) {
- $selected_os = $this->processAndroidVersion($selected_os);
- }
- if (str_contains($selected_os, ':androidDevice:')) {
- $selected_os = $this->addAndroidDevice($selected_os);
- }
- if (str_contains($selected_os, ':number')) {
- $selected_os = self::processRandomNumbers($selected_os);
- }
- return $selected_os;
- }
-
- /**
- * static::processRandomNumbers
- * @param $selected_os
- * @return null|string|string[] *
- * @throws \Exception
- */
- public static function processRandomNumbers($selected_os): array|string|null
- {
- return preg_replace_callback('/:number(\d+)-(\d+):/i', function ($matches) {
- return random_int($matches[1], $matches[2]);
- }, $selected_os);
- }
-
- /**
- * static::processSpinSyntax
- * @param $selected_os
- * @return null|string|string[] *
- */
- public static function processSpinSyntax($selected_os): array|string|null
- {
- return preg_replace_callback('/\[([\w\-\s|;]*?)\]/i', function ($matches) {
- $shuffle = explode('|', $matches[1]);
- return $shuffle[array_rand($shuffle)];
- }, $selected_os);
- }
-
- /**
- * processAndroidVersion
- * @param $selected_os
- * @return null|string|string[] *
- */
- public function processAndroidVersion($selected_os): array|string|null
- {
- $this->androidVersion = $version = $this->androidVersions[array_rand($this->androidVersions)];
- return preg_replace_callback('/:androidVersion:/i', function ($matches) use ($version) {
- return $version;
- }, $selected_os);
- }
-
- /**
- * addAndroidDevice
- * @param $selected_os
- * @return null|string|string[] *
- */
- public function addAndroidDevice($selected_os): array|string|null
- {
- $devices = $this->androidDevices[substr($this->androidVersion, 0, 3)];
- $device = $devices[array_rand($devices)];
-
- $device = self::processSpinSyntax($device);
- return preg_replace_callback('/:androidDevice:/i', function ($matches) use ($device) {
- return $device;
- }, $selected_os);
- }
-
- /**
- * static::chromeVersion
- * @param $version
- * @return string *
- * @throws \Exception
- */
- public static function chromeVersion($version): string
- {
- return random_int($version['min'], $version['max']) . '.0.' . random_int(1000, 4000)
- . '.' . random_int(100, 400);
- }
-
- /**
- * static::firefoxVersion
- * @param $version
- * @return string *
- * @throws \Exception
- */
- public static function firefoxVersion($version): string
- {
- return random_int($version['min'], $version['max']) . '.' . random_int(0, 9);
- }
-
- /**
- * static::windows
- * @param $version
- * @return string *
- * @throws \Exception
- */
- public static function windows($version): string
- {
- return random_int($version['min'], $version['max']) . '.' . random_int(0, 9);
- }
-
- /**
- * generate
- * @param null $userAgent
- * @return string *
- * @throws \Exception
- */
- public function generate($userAgent = NULL, $locale = null): string
- {
-
- if (!is_null($locale))
- $this->locale = $locale;
-
- if ($userAgent === NULL) {
- $r = random_int(0, 100);
- if ($r >= 44) {
- $userAgent = array_rand(['firefox' => 1, 'chrome' => 1, 'explorer' => 1]);
- } else {
- $userAgent = array_rand(['iphone' => 1, 'android' => 1, 'mobile' => 1]);
- }
- } elseif ($userAgent == 'windows' || $userAgent == 'mac' || $userAgent == 'linux') {
- $agents = ['firefox' => 1, 'chrome' => 1];
- if ($userAgent == 'windows') {
- $agents['explorer'] = 1;
- }
- $userAgent = array_rand($agents);
- }
- $_SESSION['agent'] = $userAgent;
- if ($userAgent == 'chrome') {
- return 'Mozilla/5.0 (' . $this->getOS($userAgent) . ') AppleWebKit/' .
- (random_int(1, 100) > 50 ? random_int(533, 537) : random_int(600, 603))
- . '.' . random_int(1, 50) . ' (KHTML, like Gecko) Chrome/' .
- self::chromeVersion(['min' => 47, 'max' => 55]) . ' Safari/'
- . (random_int(1, 100) > 50 ? random_int(533, 537) : random_int(600, 603));
- } elseif ($userAgent == 'firefox') {
-
- return 'Mozilla/5.0 (' . $this->getOS($userAgent) . ') Gecko/'
- . (random_int(1, 100) > 30 ? '20100101' : '20130401') . ' Firefox/'
- . self::firefoxVersion(['min' => 45, 'max' => 74]);
- } elseif ($userAgent == 'explorer') {
-
- return 'Mozilla / 5.0 (compatible; MSIE ' . ($int = random_int(7, 11))
- . '.0; ' . $this->getOS('windows') . ' Trident / '
- . ($int == 7 || $int == 8 ? '4' : ($int == 9 ? '5' : ($int == 10 ? '6' : '7')))
- . '.0)';
- } elseif ($userAgent == 'mobile'
- || $userAgent == 'android'
- || $userAgent == 'iphone'
- || $userAgent == 'ipad'
- || $userAgent == 'ipod') {
-
- return 'Mozilla/5.0 (' . $this->getMobileOS($userAgent) . ') AppleWebKit/'
- . (random_int(1, 100) > 50 ? random_int(533, 537) : random_int(600, 603))
- . '.' . random_int(1, 50) . ' (KHTML, like Gecko) Chrome/'
- . self::chromeVersion(['min' => 47, 'max' => 55]) . ' Mobile Safari/'
- . (random_int(1, 100) > 50 ? random_int(533, 537) : random_int(600, 603))
- . '.' . random_int(0, 9);
- } else {
- throw new Exception('Unable to determine user agent to generate');
- }
- }
-}
diff --git a/src/util/AllotTasks.php b/src/util/AllotTasks.php
deleted file mode 100644
index 5039fe5..0000000
--- a/src/util/AllotTasks.php
+++ /dev/null
@@ -1,110 +0,0 @@
- null,
- 'estimated_time' => null,
- 'work_completed' => null,
- ];
-
- /**
- * @use 加载json数据
- */
- protected static function loadJsonData(): ConfigBox
- {
- return ConfigBox::newFromFile(static::$repository);
- }
-
- /**
- * @use 提交任务
- * @param string $operation
- * @param \stdClass $act
- * @param bool $time
- * @return bool
- */
- protected static function pushTask(string $operation, stdClass $act, bool $time = false): bool
- {
- $task = [
- 'operation' => $operation,
- 'act' => $act,
- 'time' => $time
- ];
- static::$tasks[] = $task;
- return true;
- }
-
- /**
- * @use 拉取任务
- * @return mixed
- */
- protected static function pullTask(): mixed
- {
- // 任务列表为空
- if (empty(static::$tasks)) {
- return false;
- }
- // 先进先出 弹出一个任务
- $task = array_shift(static::$tasks);
- // 如果需要时间限制
- if ($task['time']) {
- // 如果预计时间为空 或 时间未到 推回队列
- if (is_null(static::$work_status['estimated_time']) || time() < intval(static::$work_status['estimated_time'])) {
- array_unshift(static::$tasks, $task);
- } else {
- // 不再需要推回时 需要制空 不影响下一个任务操作
- static::$work_status['estimated_time'] = null;
- }
- } else {
- // 切换任务 制空
- static::$work_status['estimated_time'] = null;
- }
- return $task;
- }
-
-
-
-// /**
-// * @use 执行任务
-// * @return bool
-// */
-// abstract protected function workTask(): bool;
-//
-//
-// /**
-// * @use 分配任务
-// * @return bool
-// */
-// abstract protected function allotTasks(): bool;
-}
diff --git a/src/util/BaseRaffle.php b/src/util/BaseRaffle.php
deleted file mode 100644
index 5cf1732..0000000
--- a/src/util/BaseRaffle.php
+++ /dev/null
@@ -1,233 +0,0 @@
- time()) {
- return;
- }
- static::setPauseStatus();
- static::loadJsonData();
- static::startLottery();
- }
-
- /**
- * @use 抽奖逻辑
- * @return bool
- */
- protected static function startLottery(): bool
- {
- if (count(static::$wait_list) == 0) {
- return false;
- }
- $raffle_list = [];
- $room_list = [];
- static::$wait_list = static::arrKeySort(static::$wait_list, 'wait');
- $max_num = count(static::$wait_list);
- for ($i = 0; $i <= $max_num; $i++) {
- $raffle = array_shift(static::$wait_list);
- if (is_null($raffle)) {
- break;
- }
- if ($raffle['wait'] > time()) {
- static::$wait_list[] = $raffle;
- break;
- }
- if (count($raffle_list) > 200) {
- break;
- }
- $room_list[] = $raffle['room_id'];
- $raffle_list[] = $raffle;
- // 有备注要单独处理
- if (isset($raffle['remarks'])) {
- Statistics::addJoinList($raffle['remarks']);
- } else {
- Statistics::addJoinList($raffle['raffle_name']);
- }
- }
- if (count($raffle_list) && count($room_list)) {
- $room_list = array_unique($room_list);
- foreach ($room_list as $room_id) {
- Live::goToRoom($room_id);
- }
- $results = static::createLottery($raffle_list);
- static::parseLottery($results);
- }
- return true;
- }
-
- /**
- * @use 返回抽奖数据
- * @param int $room_id
- * @return array
- */
- protected static function check(int $room_id): array
- {
- $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/getLotteryInfo';
- $payload = [
- 'roomid' => $room_id
- ];
- $raw = Curl::get('app', $url, Sign::common($payload));
- $de_raw = json_decode($raw, true);
- if (!isset($de_raw['data']) || $de_raw['code']) {
- // Todo 请求被拦截 412
- Log::error("获取抽奖数据错误,{$de_raw['message']}");
- return [];
- }
- return $de_raw;
- }
-
- /**
- * @use 解析抽奖信息
- * @param int $room_id
- * @param array $data
- * @return bool
- */
- abstract protected static function parseLotteryInfo(int $room_id, array $data): bool;
-
- /**
- * @use 创建抽奖
- * @param array $raffles
- * @return array
- */
- abstract protected static function createLottery(array $raffles): array;
-
- /**
- * @use 解析抽奖返回
- * @param array $results
- * @return mixed
- */
- abstract protected static function parseLottery(array $results): mixed;
-
- /**
- * @use 二维数组按key排序
- * @param $arr
- * @param $key
- * @param string $type
- * @return array
- */
- protected static function arrKeySort($arr, $key, string $type = 'asc'): array
- {
- switch ($type) {
- case 'desc':
- array_multisort(array_column($arr, $key), SORT_DESC, $arr);
- return $arr;
- case 'asc':
- array_multisort(array_column($arr, $key), SORT_ASC, $arr);
- return $arr;
- default:
- return $arr;
- }
- }
-
- /**
- * @use 去重检测
- * @param $lid
- * @param bool $filter
- * @return bool
- */
- protected static function toRepeatLid($lid, bool $filter = true): bool
- {
- $lid = (int)$lid;
- if (in_array($lid, static::$all_list)) {
- return true;
- }
- if (count(static::$all_list) > 4000) {
- static::$all_list = array_values(array_splice(static::$all_list, 2000, 2000));
- }
- if ($filter) {
- static::$all_list[] = $lid;
- }
- return false;
- }
-
- /**
- * @use 数据推入队列
- * @param array $data
- * @return bool
- */
- public static function pushToQueue(array $data): bool
- {
- // 开关
- if (!getEnable(static::ACTIVE_SWITCH)) {
- return false;
- }
- // 黑屋
- if (static::getPauseStatus()) {
- return false;
- }
- $current_rid = (int)$data['rid'];
- // 去重
- if (static::toRepeatLid($current_rid, false)) {
- return false;
- }
- // 拒绝钓鱼 防止重复请求
- if (in_array($current_rid, static::$banned_rids)) {
- return false;
- }
- $banned_status = Live::fishingDetection($current_rid);
- if ($banned_status) {
- static::$banned_rids[] = $current_rid;
- return false;
- }
- // 实际检测
- $raffles_info = static::check($current_rid);
- if (!empty($raffles_info)) {
- static::parseLotteryInfo($current_rid, $raffles_info);
- }
- $wait_num = count(static::$wait_list);
- if ($wait_num > 10 && ($wait_num % 2)) {
- Log::info("当前队列中共有 $wait_num 个" . static::ACTIVE_TITLE . "待抽奖");
- }
- return true;
- }
-}
diff --git a/src/util/FilterWords.php b/src/util/FilterWords.php
deleted file mode 100644
index ac081c4..0000000
--- a/src/util/FilterWords.php
+++ /dev/null
@@ -1,53 +0,0 @@
-
- * @package Smysloff\Traits
- */
-trait Singleton
-{
- /**
- * Singleton instance
- */
- private static $instance = null;
-
- /**
- * Creates an instance of Singleton
- * and always returns same instance
- *
- * @return Singleton
- */
- public static function getInstance(): self
- {
- if (!self::$instance) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
- /**
- * Initializes the singleton
- */
- protected function init(): void
- {
- }
-
- /**
- * Singleton constructor.
- * Singleton constructor needs to be private
- * 不允许从外部调用以防止创建多个实例
- * 要使用单例,必须通过 Singleton::getInstance() 方法获取实例
- */
- final public function __construct()
- {
- $this->init();
- }
-
-
- /**
- * Singleton can't be cloned
- * 防止实例被克隆(这会创建实例的副本)
- */
- final public function __clone()
- {
- throw new SingletonException("Singleton can't be cloned");
- }
-
- /**
- * Singleton can't be serialized
- */
- final public function __sleep()
- {
- throw new SingletonException("Singleton can't be serialized");
- }
-
- /**
- * Singleton can't be deserialized
- * 防止反序列化(这将创建它的副本)
- */
- final public function __wakeup()
- {
- throw new SingletonException("Singleton can't be deserialized");
- }
-
- /**
- * 其他方法自动调用
- * @param $method
- * @param $args
- * @return mixed
- */
- public function __call($method, $args)
- {
- return call_user_func_array([static::$instance, $method], $args);
- }
-
- /**
- * 静态调用
- * @param $method
- * @param $args
- * @return mixed
- */
- public static function __callStatic($method, $args)
- {
- return call_user_func_array([static::$instance, $method], $args);
- }
-
-
-}
-
diff --git a/src/util/TimeLock.php b/src/util/TimeLock.php
deleted file mode 100644
index d094071..0000000
--- a/src/util/TimeLock.php
+++ /dev/null
@@ -1,153 +0,0 @@
-_setLock(static::getBaseClass(), time() + $lock);
- }
- }
-
- /**
- * @use 获取时间
- * @return int
- */
- public static function getLock(): int
- {
- return Task::getInstance()->_getLock(static::getBaseClass());
- }
-
- /**
- * @use 获取基础CLASS NAME
- * @return string
- */
- public static function getBaseClass(): string
- {
- return basename(str_replace('\\', '/', __CLASS__));
- }
-
- /**
- * @use used in Amp loop Delayed
- * @return delayed
- */
- public static function Delayed(): Delayed
- {
- return new Delayed(1000);
- }
-
- /**
- * @use 定时
- * @param int $hour 时
- * @param int $minute 分
- * @param int $seconds 秒
- * @param bool $random 随机一个小时内
- * @return int
- */
- public static function timing(int $hour, int $minute = 0, int $seconds = 0, bool $random = false): int
- {
- $time = strtotime('today') + ($hour * 60 * 60) + ($minute * 60) + ($seconds);
- if ($time > time()) {
- $timing = strtotime('today') + ($hour * 60 * 60) + ($minute * 60) + ($seconds) - time();
- } else {
- $timing = strtotime('tomorrow') + ($hour * 60 * 60) + ($minute * 60) + ($seconds) - time();
- }
- return $random ? $timing + mt_rand(1, 60) * 60 : $timing;
- }
-
-
- /**
- * @use 判断是否在时间内
- * @param string $first_time
- * @param string $second_time
- * @return bool
- */
- public static function inTime(string $first_time, string $second_time): bool
- {
- #判断当前时间是否在时间段内,如果是,则执行
- $Day = date('Y-m-d ', time());
- $timeBegin = strtotime($Day . $first_time);
- $timeEnd = strtotime($Day . $second_time);
- $curr_time = time();
- if ($curr_time >= $timeBegin && $curr_time <= $timeEnd) {
- return true;
- }
- return false;
- }
-
- /**
- * @use 暂停
- */
- public static function pauseLock()
- {
- // 备份几种获取方式 get_called_class()
- // basename(str_replace('\\', '/', $class));
- // substr(strrchr($class, "\\"), 1);
- // substr($class, strrpos($class, '\\') + 1);
- // array_pop(explode('\\', $class));
- Schedule::triggerRefused((new ReflectionClass(static::class))->getShortName());
- }
-
- /**
- * @use 取消暂停
- */
- public static function cancelPause()
- {
- static::$pause_status = false;
- }
-
- /**
- * @use 暂停状态
- * @return bool
- */
- public static function getPauseStatus(): bool
- {
- return static::$pause_status;
- }
-
- /**
- * @use 设置状态
- * @param bool $status
- */
- public static function setPauseStatus(bool $status = false)
- {
- self::$pause_status = $status;
- }
-
-}
diff --git a/src/util/XliveHeartBeat.php b/src/util/XliveHeartBeat.php
deleted file mode 100644
index 6acb7e1..0000000
--- a/src/util/XliveHeartBeat.php
+++ /dev/null
@@ -1,324 +0,0 @@
- []]; // data [ets, benchmark, time, secret_rule, id] data->id [parent_area_id, area_id, 0, room_id]
- protected static array $_secret_rule = []; // secret_rule [2, 3, 1, 5]
- protected static array $_room_info = []; // 心跳房间信息
-
- protected static int $_retry = 3; // 重试次数
- protected static int $_count_num = 0; // 计数
- protected static int $_count_time = 0; // 计时间
-
- protected static int $_current_room_id = 0; // 当前运行的ROOM_ID
- protected static string|null $_enc_server = null; // 加密服务器 依赖配置文件
-
- protected static int $_default = 0; // 默认值
-
- // 请求配置
- protected static string $_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36';
- protected static array $_headers = [
- 'content-type' => 'application/x-www-form-urlencoded',
- 'origin' => 'https://live.bilibili.com',
- 'referer' => 'https://live.bilibili.com/',
- 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'
- ];
-
- /**
- * @use 任务接口
- * @param int $room_id
- * @param int $max_time
- * @param int $max_num
- * @return mixed
- */
- protected static function xliveHeartBeatTask(int $room_id, int $max_time, int $max_num): mixed
- {
- // 加载依赖
- if (!static::depend()) {
- return static::$_default;
- }
- // 对比当前运行
- if (static::$_current_room_id != $room_id) {
- static::resetVar(true);
- static::$_current_room_id = $room_id;
- }
- // 获取房间信息
- if (empty(static::$_room_info)) {
- $r_data = Live::webGetRoomInfo($room_id);
- if ($r_data['code'] != 0) {
- Log::warning('直播间信息获取失败');
- return static::$_default;
- }
- static::$_room_info = $r_data;
- $rdata = $r_data['data'];
- $parent_area_id = $rdata['room_info']['parent_area_id'];
- $area_id = $rdata['room_info']['area_id'];
- # 短位转长位
- $room_id = $rdata['room_info']['room_id'];
- static::$_data['id'] = [$parent_area_id, $area_id, 0, $room_id];
- }
- // 执行心跳
- $r_data = static::heartBeatIterator();
- $index = static::$_data['id'][2];
- if ($r_data['code'] != 0) {
- Log::warning("心跳失败-$index {$r_data['message']}");
- // 失败心跳
- if (static::$_retry) {
- // 重试次数 > 1 , 不全部清除
- static::resetVar(true);
- static::$_retry -= 1;
- } else {
- // 重试次数 < 1 , 全部清除
- static::resetVar(true);
- }
- return static::$_default;
- } else {
- // 成功心跳
- static::$_count_num += 1;
- static::$_count_time += $r_data['heartbeat_interval'];
-
- // 最大次数限制
- if ($max_num <= static::$_count_num) {
- // 成功在id为{room_id}的直播间发送完{ii}次心跳,退出直播心跳(达到最大心跳次数)
- }
- // 最大时间限制
- if ($max_time <= static::$_count_time) {
- //成功在id为{room_id}的直播间发送第{ii}次心跳
- }
- $minute = round(static::$_count_time / 60) - 1;
- Log::notice("已在直播间 $room_id 连续观看了 $minute 分钟");
- return $r_data['heartbeat_interval'];
- }
- }
-
- /**
- * @use 心跳迭代
- * @return array
- */
- protected static function heartBeatIterator(): array
- {
-// print_r(static::$_data);
- $rdata = [];
- # 第1次执行 eHeartBeat
- if (static::$_data['id'][2] == 0) {
- $r_data = static::eHeartBeat(static::$_data['id']);
- } else {
- # 第1次之后执行 xHeartBeat
- static::$_data['ts'] = time() * 1000;
- static::$_data['s'] = static::encParamS(static::$_data, static::$_secret_rule);
- if (!static::$_data['s']) {
- return [
- 'code' => 404,
- 'message' => '心跳加密错误',
- 'heartbeat_interval' => static::$_default
- ];
- }
- $r_data = static::xHeartBeat(static::$_data['id']);
- }
- if ($r_data['code'] == 0) {
- $rdata = $r_data['data'];
- static::$_data['ets'] = $rdata['timestamp'];
- static::$_data['benchmark'] = $rdata['secret_key'];
- static::$_data['time'] = $rdata['heartbeat_interval'];
- static::$_secret_rule = $rdata['secret_rule'];
- static::$_data['id'][2] += 1;
- }
- Log::debug(json_encode(static::$_data['id'], true));
- return [
- 'code' => $r_data['code'],
- 'message' => $r_data['message'],
- 'heartbeat_interval' => array_key_exists('heartbeat_interval', $rdata) ? $rdata['heartbeat_interval'] : static::$_default
- ];
- }
-
- /**
- * @use E心跳
- * @param array $id
- * @return array
- */
- protected static function eHeartBeat(array $id): array
- {
- $url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E';
- $payload = [
- 'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
- 'device' => json_encode([Generator::hash(), Generator::uuid4()], true),
- 'ts' => time() * 1000,
- 'is_patch' => 0,
- 'heart_beat' => [],
- 'ua' => static::$_user_agent,
- 'csrf_token' => getCsrf(),
- 'csrf' => getCsrf(),
- 'visit_id' => ''
- ];
- // print_r($payload);
- Log::debug(json_encode($payload, true));
- $raw = Curl::post('pc', $url, $payload, static::$_headers);
- // {'code':0,'message':'0','ttl':1,'data':{'timestamp':1595342828,'heartbeat_interval':300,'secret_key':'seacasdgyijfhofiuxoannn','secret_rule':[2,5,1,4],'patch_status':2}}
-
- unset($payload['id']);
- static::$_data = array_merge_recursive(static::$_data, $payload);
-
- return json_decode($raw, true);
- }
-
- /**
- * @use X心跳
- * @param array $id
- * @return array
- */
- protected static function xHeartBeat(array $id): array
- {
- $url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X';
- $payload = [
- 's' => static::$_data['s'],
- 'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
- 'device' => static::$_data['device'],
- 'ets' => static::$_data['ets'],
- 'benchmark' => static::$_data['benchmark'],
- 'time' => static::$_data['time'],
- 'ts' => static::$_data['ts'],
- 'ua' => static::$_data['ua'],
- 'csrf_token' => static::$_data['csrf_token'],
- 'csrf' => static::$_data['csrf'],
- 'visit_id' => ''
- ];
-// print_r($payload);
- Log::debug(json_encode($payload, true));
- $raw = Curl::post('pc', $url, $payload, static::$_headers);
- # {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":60,"timestamp":1619419450,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}}
- # {'code':0,'message':'0','ttl':1,'data':{'heartbeat_interval':300,'timestamp':1595346846,'secret_rule':[2,5,1,4],'secret_key':'seacasdgyijfhofiuxoannn'}}
- return json_decode($raw, true);
- }
-
- /**
- * @use 加密参数S
- * @param array $t
- * @param array $r
- * @return string|false
- */
- protected static function encParamS(array $t, array $r): bool|string
- {
- $headers = [
- 'Content-Type' => 'application/json',
- ];
- // 加密部分
- $payload = [
- 't' => static::formatT($t),
- 'r' => static::formatR($r)
- ];
-// print_r($payload);
- $data = Curl::put('other', static::$_enc_server, $payload, $headers);
- $de_raw = json_decode($data, true);
- if ($de_raw['code'] == 0) {
- if (array_key_exists('s', $de_raw)) {
- // Log::info("S加密成功 {$de_raw['s']}");
- return $de_raw['s'];
- }
- Log::warning("参数S加密失败: 加密服务器暂时错误,请检查更换");
- } else {
- Log::warning("参数S加密失败: {$de_raw['message']}");
- }
- return false;
- }
-
- /**
- * @use 格式T
- * @param array $t
- * @return array
- */
- #[ArrayShape(['id' => "mixed", 'device' => "mixed", 'ets' => "mixed", 'benchmark' => "mixed", 'time' => "mixed", 'ts' => "mixed", 'ua' => "mixed"])]
- protected static function formatT(array $t): array
- {
-// print_r($t);
- return [
- 'id' => $t['id'],
- 'device' => $t['device'],
- 'ets' => $t['ets'],
- 'benchmark' => $t['benchmark'],
- 'time' => $t['time'],
- 'ts' => $t['ts'],
- 'ua' => $t['ua'],
- ];
- }
-
- /**
- * @use 格式R
- * @param array $r
- * @return array
- */
- protected static function formatR(array $r): array
- {
- return $r;
- }
-
- /**
- * @use 重置变量
- * @param false $force
- */
- protected static function resetVar(bool $force = false)
- {
- if ($force) {
- static::$_room_info = [];
- static::$_current_room_id = 0;
-
- static::$_retry = 3;
- static::$_count_num = 0;
- static::$_count_time = 0;
- }
- static::$_data = null;
- static::$_data = ['id' => []];
- $data = [
- 'id' => static::$_data['id'],
- ];
- $data["id"][2] = 0;
- static::$_data = $data;
- }
-
- /**
- * @use 检查依赖
- * @return bool
- */
- protected static function depend(): bool
- {
- if (getConf('server', 'heartbeat_enc') == '') {
- return false;
- }
- // 加载加密服务器
- if (is_null(static::$_enc_server)) {
- static::$_enc_server = getConf('server', 'heartbeat_enc');
- }
- return true;
- }
-
-}
\ No newline at end of file
diff --git a/tests/ArrayToTextTableTest.php b/tests/ArrayToTextTableTest.php
deleted file mode 100644
index 4f964ab..0000000
--- a/tests/ArrayToTextTableTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
- 'Mo 啊大苏打allie',
- 'surname' => 'Alv萨达速度asarez',
- 'email' => 'molliealvarez@example.com',
- ],
- [
- 'firstname' => 'Dianna',
- 'surname' => 'Mcbride',
- 'age' => 1111,
- 'email' => 'diannamcbride@example.com',
- ],
- [
- 'firstname' => '撒旦撒旦asra',
- 'surname' => 'Muel大大是打算的ler',
- 'age' => 50,
- 'email' => 'elviramueller@example.com',
- ],
- [
- 'firstname' => 'Corine',
- 'surname' => 'Morton',
- 'age' => 0,
- ],
- [
- 'firstname' => 'James',
- 'surname' => 'Allison',
- ],
- [
- 'firstname' => 'Bowen这是哥',
- 'surname' => 'Kelley',
- 'age' => 50,
- 'email' => 'bowenkelley@example.com',
- ],
- ];
- $renderer = new ArrayToTextTable($data);
- echo $renderer->getTable();
- }
-}
diff --git a/tool/ConfigGenerator.php b/tool/ConfigGenerator.php
deleted file mode 100644
index 69f9090..0000000
--- a/tool/ConfigGenerator.php
+++ /dev/null
@@ -1,73 +0,0 @@
-cliInput('请注意生成程序只会填写基础配置,Enter继续: ');
- }
-
- /**
- * @param string $key
- * @param string $value
- * @param string $content
- * @return string|string[]|null
- */
- private function envReplace(string $key, string $value, string $content): array|string|null
- {
- return preg_replace(
- '/^' . $key . '=.*' . '/m',
- $key . '=' . $value,
- $content
- );
- }
-
- /**
- * @param string $msg
- * @param int $max_char
- * @return string
- */
- private function cliInput(string $msg, int $max_char = 100): string
- {
- $stdin = fopen('php://stdin', 'r');
- echo '# ' . $msg;
- $input = fread($stdin, $max_char);
- fclose($stdin);
- return str_replace(PHP_EOL, '', $input);
- }
-
- /**
- * @use Generator
- */
- public function generate()
- {
- $this->filename = $this->cliInput('请输入配置文件名: ');
- $this->template = file_get_contents($this->default_filename);
- foreach ($this->options as $index => $option) {
- $value = $this->cliInput("请输入$option: ");
- $this->template = $this->envReplace($option, $value, $this->template);
- }
- file_put_contents(__DIR__ . "\\$this->filename.ini", $this->template);
- echo "生成配置文件 $this->filename.ini 成功~";
- }
-
-}
-
-(new ConfigGenerator())->generate();
\ No newline at end of file