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://img.shields.io/badge/Author-Lkeme-blueviolet "作者")](https://github.com/lkeme/ ) -![](https://img.shields.io/badge/dynamic/json?label=GitHub%20Followers&query=%24.data.totalSubs&url=https%3A%2F%2Fapi.spencerwoo.com%2Fsubstats%2F%3Fsource%3Dgithub%26queryKey%3Dlkeme&labelColor=282c34&color=181717&logo=github&longCache=true "关注数量") -![](https://img.shields.io/github/stars/lkeme/BiliHelper-personal.svg?style=plastic&logo=appveyor "Star数量") -![](https://img.shields.io/github/forks/lkeme/BiliHelper-personal.svg?style=plastic&logo=stackshare "Fork数量") -![](https://img.shields.io/github/contributors/lkeme/BiliHelper-personal "贡献者") - -
- -## 环境依赖 - -通常使用 `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://img.shields.io/badge/Author-Lkeme-blueviolet "作者")](https://github.com/lkeme/ ) -![](https://img.shields.io/badge/dynamic/json?label=GitHub%20Followers&query=%24.data.totalSubs&url=https%3A%2F%2Fapi.spencerwoo.com%2Fsubstats%2F%3Fsource%3Dgithub%26queryKey%3Dlkeme&labelColor=282c34&color=181717&logo=github&longCache=true "关注数量") -![](https://img.shields.io/github/stars/lkeme/BiliHelper-personal.svg?style=plastic&logo=appveyor "Star数量") -![](https://img.shields.io/github/forks/lkeme/BiliHelper-personal.svg?style=plastic&logo=stackshare "Fork数量") -![](https://img.shields.io/github/contributors/lkeme/BiliHelper-personal "贡献者") -![](https://img.shields.io/github/repo-size/lkeme/BiliHelper-personal?style=flat-square&label=files&color=cf8ef4&labelColor=373e4dl "文件大小") -[![Docker Pulls](https://img.shields.io/docker/pulls/lkeme/bilihelper-personal?style=flat-square)](https://hub.docker.com/r/lkeme/bilihelper-personal) - -
- -

- - - - - - -

- -## 📌公告通知 - -代码开源,本地化99.9%,项目不收集或使用任何敏感信息,兴趣所致,一切只为学习。 - -```notice ----- 免费的东西总是得不到人的珍惜。 ----- 只有花大价钱去买到的东西,才会令人信任。 ----- 本项目仅供学习交流使用,请勿用于非法用途!* 3 -``` - -## 👤游客访问 - -

- -

- -## 🎁打赏支持 - -如果觉得本项目好用,对你有所帮助,欢迎打赏支持本项目哦。 - -

- -[comment]: <> (![Image](https://i.loli.net/2019/07/13/5d2963e5cc1eb22973.png)) - -## 🖨️相关文档 - -有疑问一定要先看看文档或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 | 待整理 | 直播银瓜子自动开启宝箱 | - -
- - -## 🖥️星图 - -[![Stargazers over time](https://starchart.cc/lkeme/BiliHelper-personal.svg)](https://starchart.cc/lkeme/BiliHelper-personal) -[![Stargazers over time](https://starchart.cc/lkeme/BiliHelper.svg)](https://starchart.cc/lkeme/BiliHelper) - -## 运行效果 - -效果图不代表当前版本,请以当前最新版本运行结果为准。 - -

- -[comment]: <> (![Image](https://i.loli.net/2019/07/13/5d296961a4bae41364.png)) - -## 项目相关 - -* [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