[refactor] all

This commit is contained in:
Lkeme 2022-06-03 23:34:44 +08:00
parent 8a9148726b
commit 95e4f9293e
160 changed files with 8740 additions and 15844 deletions

12
.github/FUNDING.yml vendored
View File

@ -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' ]

View File

@ -1,47 +0,0 @@
# Issue: BiliHepler 程序问题
<!-- 上面是标题 下面才是正文 -->
### 版本、安装方式、系统
1. 你在使用什么版本的 BiliHelper
2. 你通过什么方式安装 BiliHepler
3. 你所使用的操作系统
---
### 描述问题
<!-- 在下方简要描述问题 -->
---
### 复现问题的步骤
<!-- 在下方描述如何复现问题 -->
---
### BiliHelper 运行日志:
<!-- 如果条件允许请附日志 -->
```shell
# 此处贴入运行日志
```
---
### 截图
<!-- 如果条件允许请附图 -->

View File

@ -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
<!-- Describe your problem below -->
---
### Steps to reproduce
<!-- Describe how to reproduce problem below -->
---
### BiliHelper Logs
<!-- Paste log if possible -->
```shell
# Paste running logs here
```
---
### Screenshot
<!-- Paste screenshot if possible -->

View File

@ -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 }}

31
.gitignore vendored
View File

@ -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

View File

@ -1 +1,2 @@
哔哩哔哩BilibiliB 站主站助手、直播助手、直播抽奖、挂机升级、贴心小棉袄脚本、Lv6 离你仅有一步之遥~ - PHP 版Personal
哔哩哔哩BilibiliB 站主站助手、直播助手、直播抽奖、挂机升级、贴心小棉袄脚本、Lv6 离你仅有一步之遥~ - PHP 版Personal

View File

@ -1,843 +0,0 @@
# Release Notes
# 本项目Log
[comment]: <> (<details>)
[comment]: <> ( <summary><strong>更新历史</strong><kbd>latest</kbd></summary>)
[comment]: <> (</details>)
## 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)

338
DOC.md
View File

@ -1,338 +0,0 @@
<p align="center"><img width="300px" src="https://user-images.githubusercontent.com/19500576/118621710-36428180-b7f9-11eb-891d-3f5697347cef.png"></p>
[comment]: <> (<p align="center"><img width="300px" src="https://i.loli.net/2018/04/20/5ad97bd395912.jpeg"></p>)
<div align="center">
[![](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 "贡献者")
</div>
## 环境依赖
通常使用 `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
```
<details>
<summary>其余镜像 展开查看</summary>
<pre><code>
+ 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
```
</code></pre>
</details>
## 使用指南
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 &#40;-o&#41;)
[comment]: <> (composer dumpautoload &#40;-o&#41;)
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`配置文件
<p align="center"><img width="680px" src="https://user-images.githubusercontent.com/19500576/118621472-f8455d80-b7f8-11eb-9fec-500148a566b4.png"></p>
[comment]: <> (<p align="center"><img width="680px" src="https://i.loli.net/2018/04/21/5adb497dc3ece.png"></p>)
## 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酱
# 自行替换 <SCKEY>
APP_CALLBACK="https://sc.ftqq.com/<SCKEY>.send?text={message}"
# TelegramBot
# 自行替换 <TOKEN> <CHAR_ID>
APP_CALLBACK="https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAR_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位直播间也为短号
该脚本在每次启动会自动修正部分功能,特殊标注的请留意。

132
NOTIFY.md
View File

@ -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 酱老版本 keySCU 开头的
```ini
; Sever酱(原版)|令牌Key|依赖USE_NOTIFY
[notify.sc]
sckey = SCU566cc69da782ec****
```
**Server酱(Turbo版)**
> 文档: https://sct.ftqq.com/
> 说明: Server 酱 Turbo 版本 keySCT 开头的
```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
改成与线上不同的版本即可,检查新版本就会推送一次。

153
README.md
View File

@ -1,153 +0,0 @@
<p align="center"><img width="300px" src="https://user-images.githubusercontent.com/19500576/118621710-36428180-b7f9-11eb-891d-3f5697347cef.png"></p>
[comment]: <> (<p align="center"><img width="300px" src="https://i.loli.net/2018/04/20/5ad97bd395912.jpeg"></p>)
<div align="center">
[![](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)
</div>
<p align="center">
<img src="https://img.shields.io/badge/Version-1.1.0.220218-orange.svg?longCache=true&style=for-the-badge">
<img src="https://img.shields.io/badge/PHP-8.0+-green.svg?longCache=true&style=for-the-badge">
<img src="https://img.shields.io/badge/Composer-latest-blueviolet.svg?longCache=true&style=for-the-badge">
<img src="https://img.shields.io/badge/License-mit-blue.svg?longCache=true&style=for-the-badge">
</p>
## 📌公告通知
代码开源本地化99.9%,项目不收集或使用任何敏感信息,兴趣所致,一切只为学习。
```notice
---- 免费的东西总是得不到人的珍惜。
---- 只有花大价钱去买到的东西,才会令人信任。
---- 本项目仅供学习交流使用,请勿用于非法用途!* 3
```
## 👤游客访问
<p align="center">
<img alingn="center" src="https://profile-counter.glitch.me/BiliHelper-personal/count.svg" />
</p>
## 🎁打赏支持
如果觉得本项目好用,对你有所帮助,欢迎打赏支持本项目哦。
<p align="center"><img width="680px" src="https://user-images.githubusercontent.com/19500576/118621834-55d9aa00-b7f9-11eb-9de2-6cfd5e8f20e6.png"></p>
[comment]: <> (![Image]&#40;https://i.loli.net/2019/07/13/5d2963e5cc1eb22973.png&#41;)
## 🖨️相关文档
有疑问一定要先看看文档或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`为暂停使用或抛弃。
<details open>
<summary>点击展开</summary>
| 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 | 待整理 | 直播银瓜子自动开启宝箱 |
</details>
## 🖥️星图
[![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)
## 运行效果
效果图不代表当前版本,请以当前最新版本运行结果为准。
<p align="center"><img width="680px" src="https://user-images.githubusercontent.com/19500576/118621918-6853e380-b7f9-11eb-8c73-e041c402a56b.png"></p>
[comment]: <> (![Image]&#40;https://i.loli.net/2019/07/13/5d296961a4bae41364.png&#41;)
## 项目相关
* [BilibiliHelper](https://github.com/metowolf/BilibiliHelper)
* [BiliHelper](https://github.com/lkeme/BiliHelper)
* [Github](https://github.com/)
## 致谢
感谢 `JetBrains` 提供优秀的IDE。
<a href="https://www.jetbrains.com/?from=BiliHelper-personal" target="_blank">
<img src="https://tva1.sinaimg.cn/large/008eGmZEly1gov9g3tzrnj30u00wj0tn.jpg" width="150"/>
</a>
## License 许可证
BiliHelper is under the MIT license.
本项目基于 MIT 协议发布,并增加了 SATA 协议。
当你使用了使用 SATA 的开源软件或文档的时候,在遵守基础许可证的前提下,你必须马不停蹄地给你所使用的开源项目 “点赞” ,比如在 GitHub 上
star然后你必须感谢这个帮助了你的开源项目的作者作者信息可以在许可证头部的版权声明部分找到。
本项目的所有代码文件、配置项,除另有说明外,均基于上述介绍的协议发布,具体请看分支下的 LICENSE。
此处的文字仅用于说明,条款以 LICENSE 文件中的内容为准。

View File

@ -1,31 +1,24 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
require 'vendor/autoload.php';
$app = new BiliHelper\Core\App(__DIR__);
$app->load($argv)
->inspect()
->start();
$app = Bhp\Bootstrap\Bootstrap::getInstance(__DIR__,$argv);
$app->run();

View File

@ -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"
]
}
}

View File

@ -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="

View File

@ -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}"

View File

@ -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

View File

@ -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"
}
]
}

View File

@ -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": "S11LOL英雄联盟全球总决赛",
"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"
}
]
}

View File

@ -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"
}

View File

@ -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]"
]
}
}

View File

@ -1,46 +0,0 @@
FROM php:alpine
#MAINTAINER zsnmwy <szlszl35622@gmail.com>
LABEL AUTHOR = "Lkeme <Useri@live.cn>"
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"]

View File

@ -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

16
phpunit.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false"
bootstrap="test/bootstrap.php" colors="false" convertErrorsToExceptions="true"
convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Php Library Test Suite">
<directory>test/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,140 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
use Bhp\Cache\Cache;
use Bhp\Log\Log;
use Bhp\Notice\Notice;
use Bhp\Plugin\BasePluginRW;
use Bhp\Plugin\Plugin;
use Bhp\Request\Request;
use Bhp\TimeLock\TimeLock;
use Bhp\Util\Resource\Resource;
class CheckUpdate extends BasePluginRW
{
/**
* 插件信息
* @var array|string[]
*/
protected ?array $info = [
'hook' => __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);
}
}

604
plugin/Login/Login.php Normal file
View File

@ -0,0 +1,604 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
use Bhp\Api\Passport\ApiCaptcha;
use Bhp\Api\Passport\ApiOauth2;
use Bhp\Api\PassportTv\ApiQrcode;
use Bhp\Cache\Cache;
use Bhp\Log\Log;
use Bhp\Plugin\BasePlugin;
use Bhp\Plugin\Plugin;
use Bhp\TimeLock\TimeLock;
use Bhp\User\User;
use Bhp\Util\Common\Common;
use Bhp\Util\Qrcode\Qrcode;
use JetBrains\PhpStorm\NoReturn;
class Login extends BasePlugin
{
/**
* 插件信息
* @var array|string[]
*/
protected ?array $info = [
'hook' => __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&gt=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));
}
}

View File

@ -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

View File

@ -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}"

View File

@ -1,4 +1,5 @@
{
"code": 200,
"MaterialObject": {
"sensitive": [
"测试",

14
resources/version.json Normal file
View File

@ -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日"
}

View File

@ -0,0 +1,60 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Api\Passport;
use Bhp\Request\Request;
use Bhp\Sign\Sign;
class ApiCaptcha
{
/**
* @use 获取验证码
* @param int $plat
* @return mixed
*/
public static function combine(int $plat = 3): array
{
$url = 'https://passport.bilibili.com/web/captcha/combine';
$payload = [
'plat' => $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);
}
}

View File

@ -0,0 +1,91 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Api\Passport;
use Bhp\Request\Request;
use Bhp\Sign\Sign;
class ApiLogin
{
/**
* @use 密码登录
* @param string $username
* @param string $password
* @param string $validate
* @param string $challenge
* @return array
*/
public static function passwordLogin(string $username, string $password, string $validate = '', string $challenge = ''): array
{
// $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' => $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));
}
}

View File

@ -0,0 +1,118 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Api\Passport;
use Bhp\Request\Request;
use Bhp\Sign\Sign;
class ApiOauth2
{
/**
* 获取令牌信息
* @param string $token
* @return array
*/
public static function tokenInfo(string $token): array
{
$url = 'https://passport.bilibili.com/api/v2/oauth2/info';
$payload = [
'access_token' => $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));
}
}

View File

@ -0,0 +1,61 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Api\PassportTv;
use Bhp\Request\Request;
use Bhp\Sign\Sign;
class ApiQrcode
{
/**
* @use 获取authCode
* @return array
*/
public static function authCode(): array
{
$url = 'https://passport.bilibili.com/x/passport-tv-login/qrcode/auth_code';
$payload = [];
$headers = [
'content-type' => '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);
}
}

103
src/Bootstrap/Bootstrap.php Normal file
View File

@ -0,0 +1,103 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Bootstrap;
use Bhp\BiliInfo\BiliInfo;
use Bhp\Cache\Cache;
use Bhp\Console\Console;
use Bhp\Core\Core;
use Bhp\Device\Device;
use Bhp\FilterWords\FilterWords;
use Bhp\Log\Log;
use Bhp\Notice\Notice;
use Bhp\Plugin\Plugin;
use Bhp\Request\Request;
use Bhp\Sign\Sign;
use Bhp\Task\Task;
use Bhp\TimeLock\TimeLock;
use Bhp\User\User;
use Bhp\Util\DesignPattern\SingleTon;
use Bhp\Env\Env;
use Bhp\Config\Config;
class Bootstrap extends SingleTon
{
/**
* @var string
*/
protected string $global_path;
/**
* @var string
*/
protected string $profile_name;
/**
* @param string $global_path
* @param array $argv
* @return void
*/
public function init(string $global_path, array $argv): void
{
$this->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();
}
}

160
src/Cache/Cache.php Normal file
View File

@ -0,0 +1,160 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Cache;
use Bhp\Util\DesignPattern\SingleTon;
use Flintstone\Flintstone;
use Flintstone\Formatter\JsonFormatter;
use Overtrue\Pinyin\Pinyin;
class Cache extends SingleTon
{
// 文档
//matomo-org/component-cache
// https://www.xeweb.net/flintstone/documentation/
/**
* @var array|null
*/
protected array $caches = [];
/**
* @var Flintstone
*/
protected Flintstone $cache;
/**
* @return void
*/
public function init(): void
{
}
/**
* @param string|null $classname
* @return void
*/
public static function initCache(?string $classname = null): void
{
$class_name = $classname ?? self::getInstance()->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'];
}
}

42
src/Config/Config.php Normal file
View File

@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Config;
use Bhp\Util\Resource\BaseResource;
class Config extends BaseResource
{
/**
* @param string $filename
* @return void
*/
public function init(string $filename = 'user.ini'): void
{
$this->loadResource($filename, 'ini');
}
/**
* @use 重写获取路径
* @param string $filename
* @return string
*/
protected function getFilePath(string $filename): string
{
return str_replace("\\", "/", PROFILE_CONFIG_PATH . $filename);
}
}

View File

@ -0,0 +1,64 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Console\Command;
use Ahc\Cli\Input\Command;
use Ahc\Cli\IO\Interactor;
use Bhp\Log\Log;
use Bhp\Plugin\Plugin;
use Bhp\Task\Task;
class AppCommand extends Command
{
/**
* @var string
*/
protected string $desc = '[主要模式] 默认功能';
/**
*
*/
public function __construct()
{
parent::__construct('mode:app', $this->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();
}
}

View File

@ -0,0 +1,57 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Console\Command;
use Ahc\Cli\Input\Command;
use Ahc\Cli\IO\Interactor;
use Bhp\Log\Log;
final class RestoreCommand extends Command
{
/**
* @var string
*/
protected string $desc = '[复位模式] 复位一些缓存以及设置';
/**
*
*/
public function __construct()
{
parent::__construct('mode:restore', $this->desc);
}
/**
* @param Interactor $io
* @return void
*/
public function interact(Interactor $io): void
{
}
/**
* @return void
*/
public function execute(): void
{
Log::info("执行 $this->desc");
// 清理排程文件
// 清理缓存文件
}
}

View File

@ -0,0 +1,54 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Console\Command;
use Ahc\Cli\Input\Command;
use Ahc\Cli\IO\Interactor;
use Bhp\Log\Log;
final class ScriptCommand extends Command
{
/**
* @var string
*/
protected string $desc = '[脚本模式] 使用一些额外功能脚本';
/**
*
*/
public function __construct()
{
parent::__construct('mode:script', $this->desc);
}
/**
* @param Interactor $io
* @return void
*/
public function interact(Interactor $io): void
{
}
/**
* @return void
*/
public function execute(): void
{
Log::info("执行 $this->desc");
}
}

102
src/Console/Console.php Normal file
View File

@ -0,0 +1,102 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Console;
use Ahc\Cli\Application;
use Bhp\Console\Command\AppCommand;
use Bhp\Console\Command\RestoreCommand;
use Bhp\Console\Command\ScriptCommand;
use Bhp\Util\DesignPattern\SingleTon;
use Exception;
class Console extends SingleTon
{
/**
* @var string
*/
protected string $logo = <<<LOGO
________ ___ ___ ___ ___ ___ _______ ___ ________ _______ ________
|\ __ \|\ \|\ \ |\ \|\ \|\ \|\ ___ \ |\ \ |\ __ \|\ ___ \ |\ __ \
\ \ \|\ /\ \ \ \ \ \ \ \ \ \\\ \ \ __/|\ \ \ \ \ \|\ \ \ __/|\ \ \|\ \
\ \ __ \ \ \ \ \ \ \ \ \ __ \ \ \_|/_\ \ \ \ \ ____\ \ \_|/_\ \ _ _\
\ \ \|\ \ \ \ \ \____\ \ \ \ \ \ \ \ \_|\ \ \ \____\ \ \___|\ \ \_|\ \ \ \\ \|
\ \_______\ \__\ \_______\ \__\ \__\ \__\ \_______\ \_______\ \__\ \ \_______\ \__\\ _\
\|_______|\|__|\|_______|\|__|\|__|\|__|\|_______|\|_______|\|__| \|_______|\|__|\|__|
LOGO;
/**
* @var array
*/
protected array $argv;
/**
* @var Application
*/
protected Application $app;
/**
* @return void
*/
public function init(): void
{
}
/**
* @use 解析参数
* @param array $argv
* @param string $default
* @return string
*/
public static function parse(array $argv, string $default = 'user'): string
{
try {
// backup
// $argv = $argv ?? $_SERVER['argv'];
// $filename = str_contains($argv[1] ?? '', ':') ? $default : $args[1] ?? $default;
$argv = $argv ?? $_SERVER['argv'];
self::getInstance()->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);
}
}

64
src/Device/Device.php Normal file
View File

@ -0,0 +1,64 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Device;
use Bhp\Log\Log;
use Bhp\Util\Resource\BaseResource;
class Device extends BaseResource
{
/**
* @param string $filename
* @param string $default_filename
* @return void
*/
public function init(string $filename = 'device.yaml'): void
{
$this->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;
// }
// }
}

136
src/Env/Env.php Normal file
View File

@ -0,0 +1,136 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Env;
use Bhp\Log\Log;
use Bhp\Util\Resource\BaseResource;
class Env extends BaseResource
{
/**
* @var string
*/
public string $app_name;
/**
* @var string
*/
public string $app_version;
/**
* @var string
*/
public string $app_branch;
/**
* @var string
*/
public string $app_source;
/**
* @param string $filename
* @return void
*/
public function init(string $filename = 'version.json'): void
{
set_time_limit(0);
// header("Content-Type:text/html; charset=utf-8");
// ini_set('date.timezone', 'Asia/Shanghai');
date_default_timezone_set('Asia/Shanghai');
ini_set('display_errors', 'on');
error_reporting(E_ALL);
//
$this->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;
}
}

View File

@ -0,0 +1,43 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\FilterWords;
use Bhp\Util\Resource\BaseResource;
class FilterWords extends BaseResource
{
/**
* @param string $filename
* @return void
*/
public function init(string $filename = 'filter_library.json'): void
{
$this->loadResource($filename, 'json');
}
/**
* @use 重写真实路径
* @param string $filename
* @return string
*/
protected function getFilePath(string $filename): string
{
return str_replace("\\", "/", APP_RESOURCES_PATH . $filename);
}
}

181
src/Helpers.php Normal file
View File

@ -0,0 +1,181 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
use Bhp\Cache\Cache;
use Bhp\Config\Config;
use Bhp\Device\Device;
use Bhp\Env\Env;
use Bhp\Log\Log;
use JetBrains\PhpStorm\NoReturn;
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
/**
* @use 用户配置读取
* @param string $key
* @param mixed|null $default
* @param string $type
* @return mixed
*/
function getConf(string $key, mixed $default = null, string $type = 'default'): mixed
{
return Config::getInstance()->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);
//}

222
src/Log/Log.php Normal file
View File

@ -0,0 +1,222 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Log;
use Bhp\Request\Request;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Bramus\Monolog\Formatter\ColoredLineFormatter;
use Bhp\Util\DesignPattern\SingleTon;
class Log extends SingleTon
{
/**
* @var Logger|null
*/
protected ?Logger $_logger = null;
/**
* @return void
*/
public function init(): void
{
}
/**
* @use 初始化日志服务
* @return Logger
*/
protected function getLogger(): Logger
{
if (!$this->_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));
}
}
}

View File

@ -1,75 +1,117 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace BiliHelper\Plugin;
namespace Bhp\Notice;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\FilterWords;
use Bhp\FilterWords\FilterWords;
use Bhp\Log\Log;
use Bhp\Request\Request;
use Bhp\Util\DesignPattern\SingleTon;
class Notice
class Notice extends SingleTon
{
use FilterWords;
/**
* @var array|string[]
*/
private static array $json_headers = [
protected array $headers = [
'Content-Type' => '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
}
}
}
}

View File

@ -1,33 +1,25 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
declare(strict_types=1);
namespace BiliHelper\Exceptions;
namespace Bhp\Plugin;
use Exception;
class TaskException extends Exception
abstract class BasePlugin
{
}
use BasePluginInfo;
}

View File

@ -0,0 +1,141 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Plugin;
trait BasePluginInfo
{
/**
* 插件信息
* @var array|string[]
*/
protected array $info_template = [
'hook' => __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;
}
}

View File

@ -1,33 +1,27 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
declare(strict_types=1);
namespace BiliHelper\Exceptions;
namespace Bhp\Plugin;
use Exception;
use Bhp\Util\Resource\BaseResourcePoly;
class SingletonException extends Exception
abstract class BasePluginRW extends BaseResourcePoly
{
}
use BasePluginInfo;
}

224
src/Plugin/Plugin.php Normal file
View File

@ -0,0 +1,224 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Plugin;
use Bhp\Util\DesignPattern\SingleTon;
class Plugin extends SingleTon
{
/**
* @use 监听插件的启用/关闭|UUID下标
* @access private
* @var array
*/
protected array $_staff = [];
/**
* @use 保存所有插件信息
* @var array
*/
protected array $_plugins = [];
/**
* @use 保存插件优先级信息
* @var array
*/
protected array $_priority = [];
/**
* @return void
*/
public function init(): void
{
$this->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);
}
}

View File

@ -0,0 +1,164 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Request;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request as PRequest;
class MultiRequest
{
/**
* @var Client
*/
protected Client $client;
protected array $headers = [];
protected array $options = [];
protected Closure $successCallback;
protected Closure $errorCallback;
protected array$urls = [];
protected string$method;
protected int $concurrency = 10;
/**
* @param Client $client
*/
public function __construct(Client $client)
{
$this->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();
}
}

565
src/Request/Request.php Normal file
View File

@ -0,0 +1,565 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Request;
use Bhp\Cache\Cache;
use Bhp\Log\Log;
use Bhp\Util\ArrayR\ArrayR;
use Bhp\Util\DesignPattern\SingleTon;
use Bhp\Util\Exceptions\MethodNotFoundException;
use Bhp\Util\Exceptions\ResponseEmptyException;
use Bhp\Util\Fake\Fake;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
/**
* @method static get(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static getResponse(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static getJson(?bool $associative, string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static post(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static postResponse(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static postJson(?bool $associative, string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static put(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static putResponse(string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
* @method static putJson(?bool $associative, string $os, string $url, array $params = [], array $headers = [], float $timeout = 30.0)
*/
class Request extends SingleTon
{
/**
* @var array
*/
protected array $caches;
/**
* @var Client|null
*/
protected ?Client $client = null;
/**
* @var array
*/
protected array $retry_times;
/**
* @var float
*/
protected float $timeout;
/**
* @var string|null
*/
protected ?string $buvid = null;
/**
* @param int $min_rt
* @param int $max_rt
* @param float $timeout
* @return void
*/
public function init(int $min_rt = 0, int $max_rt = 40, float $timeout = 30.0): void
{
Cache::initCache();
$this->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();
}
}

125
src/Sign/Sign.php Normal file
View File

@ -0,0 +1,125 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Sign;
use Bhp\Util\DesignPattern\SingleTon;
class Sign extends SingleTon
{
/**
* @return void
*/
public function init(): void
{
}
/**
* @use 登录签名
* @param array $payload
* @return array
*/
public static function login(array $payload): array
{
# Android 新
$app_key = base64_decode(getDevice('app.bili_a.app_key_n'));
$app_secret = base64_decode(getDevice('app.bili_a.secret_key_n'));
//
$default = [
'access_key' => 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;
}
}

69
src/Task/Task.php Normal file
View File

@ -0,0 +1,69 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Task;
use Amp\Loop;
use Bhp\Log\Log;
use Bhp\Plugin\Plugin;
use Bhp\TimeLock\TimeLock;
use Bhp\Util\DesignPattern\SingleTon;
use Throwable;
use function Amp\asyncCall;
class Task extends SingleTon
{
/**
* @return void
*/
public function init(): void
{
}
/**
* @param string $hook
* @param mixed $data
* @return void
*/
public static function addTask(string $hook, mixed ...$data): void
{
asyncCall(function () use ($hook, $data) {
while (true) {
try {
Plugin::getInstance()->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();
}
}

195
src/TimeLock/TimeLock.php Normal file
View File

@ -0,0 +1,195 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\TimeLock;
use Amp\Delayed;
use Bhp\Util\DesignPattern\SingleTon;
class TimeLock extends SingleTon
{
/**
* @var array|null
*/
protected ?array $locks = [];
/**
* @return void
*/
public function init(): void
{
}
/**
* @use 初始化时间锁
* @param int $times
* @param bool $pause
* @return void
*/
public static function initTimeLock(int $times = 0, bool $status = false): void
{
$class_name = self::getInstance()->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());
// }
}

52
src/User/User.php Normal file
View File

@ -0,0 +1,52 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\User;
use Bhp\Util\DesignPattern\SingleTon;
use JetBrains\PhpStorm\ArrayShape;
class User extends SingleTon
{
/**
* @return void
*/
public function init(): void
{
}
/**
* @use 转换信息
* @return array
*/
#[ArrayShape(['csrf' => "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] ?? '',
];
}
}

View File

@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\ArrayR;
class ArrayR
{
/**
* @use 关联数组转字符串
* @param array $arr
* @param string $sep1
* @param string $sep2
* @return string
*/
public static function toStr(array $arr, string $sep1 = ':', string $sep2 = '\r\n'): string
{
$tmp = '';
foreach ($arr as $key => $value) {
$tmp .= "$key$sep1$value$sep2";
}
return $tmp;
}
}

View File

@ -1,28 +1,21 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace BiliHelper\Tool;
namespace Bhp\Util\Common;
class Common
{
@ -85,7 +78,17 @@ class Common
return $newStr;
}
}
/**
* @use 检查手机号格式
* @param string $phone
* @return bool
*/
public static function checkPhone(string $phone): bool
{
// /^1[3456789]{1}\d{9}$/
if (!preg_match("/^1[3456789]\d{9}$/", $phone)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,89 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\DesignPattern;
class SingleTon
{
/**
* @use 创建静态私有的变量保存该类对象
* @var array|null
*/
private static ?array $_instances = [];
/**
* @use SingleTon constructor. 私有的构造方法|防止使用new直接创建对象
*/
private function __construct()
{
}
/**
* @use 创建__clone方法防止对象被复制克隆
* @return void
*/
private function __clone(): void
{
}
/**
* @use 放置反序列化
* @return void
*/
public function __wakeup(): void
{
}
/**
* @param string $className
* @param bool $overwrite
* @return void
*/
protected static function addInstance(string $className, bool $overwrite = false): void
{
if (isset(self::$_instances[$className]) && !$overwrite) {
throw new \InvalidArgumentException($className);
}
if (!class_exists($className)) {
throw new \InvalidArgumentException($className);
}
$instance = new $className();
self::$_instances[$className] = $instance;
}
/**
* @param mixed ...$params
* @return static
*/
public static function getInstance(mixed...$params): self
{
$className = static::class;
if (!isset(self::$_instances[$className])) {
self::addInstance($className);
// test
if (is_callable([self::$_instances[$className], 'init'])) {
self::$_instances[$className]->init(...$params);
}
}
return self::$_instances[$className];
}
}

View File

@ -1,40 +1,34 @@
<?php
<?php declare(strict_types=1);
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace BiliHelper\Script;
namespace Bhp\Util\Exceptions;
use BiliHelper\Core\Log;
use Exception;
use Throwable;
class DelDynamic extends BaseTask
class GetConfigException extends Exception
{
public static string $description = '批量删除动态默认单次最大清理300个动态.';
/**
* @use run
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public static function run()
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
Log::error('当前功能未完成');
parent::__construct($message, $code, $previous);
}
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Exceptions;
use Exception;
use Throwable;
class HttpClientInitException extends Exception
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Exceptions;
use Exception;
use Throwable;
class MethodNotFoundException extends Exception
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View File

@ -1,43 +1,34 @@
<?php
<?php declare(strict_types=1);
/**
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace BiliHelper\Plugins\Example;
namespace Bhp\Util\Exceptions;
use BiliHelper\Plugins\Plugins;
use Exception;
use Throwable;
class Example
class ResponseEmptyException extends Exception
{
public function __construct()
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
$Manager = Plugins::getInstance();
$Manager->register('Example', $this, 'example');
parent::__construct($message, $code, $previous);
}
public function example($example): string
{
return "插件" . $example;
}
}

View File

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
@ -6,13 +6,42 @@
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
* Source: https://github.com/anhao/bv2av/
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace BiliHelper\Tool;
namespace Bhp\Util\Fake;
class Generator
class Fake
{
/**
* @use 生成UUID
* @return string
*/
public static function uuid(): string
{
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
/**
* @use 生成uuid4
* @return string
@ -24,7 +53,7 @@ class Generator
// var e = 16 * Math.random() | 0;
// return ("x" === t ? e : 3 & e | 8).toString(16)
// });
$chars = md5(uniqid(mt_rand(), true));
$chars = md5(uniqid((string)mt_rand(), true));
$chars = substr_replace($chars, "4", 12, 1);
$chars = substr_replace($chars, "a", 16, 1);
return substr($chars, 0, 8) . '-'
@ -46,7 +75,6 @@ class Generator
return md5($content); // sha1
}
/**
* @use 生成BUVID
* @return string
@ -88,4 +116,4 @@ class Generator
return $emoji_list[array_rand($emoji_list)];
}
}
}

View File

@ -5,11 +5,11 @@
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
* Updated: 2021 ~ 2022
* Source: https://github.com/fzaninotto/Faker/
*/
namespace BiliHelper\Tool;
namespace Bhp\Util\Fake;
class Faker
{

59
src/Util/Os/Path.php Normal file
View File

@ -0,0 +1,59 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Os;
class Path
{
/**
* @use Folder Permissions
* @param string $path
* @param int $permissions
* @return void
* 0600 所有者可读写,其他人没有任何权限
* 0644 所有者可读写,其他人可读
* 0755 所有者有所有权限,其他所有人可读和执行
* 0740 所有者有所有权限,所有者所在的组可读
* 0777 所有权限
*/
public static function SetFolderPermissions(string $path, int $permissions = 0777): void
{
if (!file_exists($path)) {
chmod($path, $permissions);
}
}
/**
* @use Create Folder
* @param string $path
* @param int $permissions
* @return void
* 0600 所有者可读写,其他人没有任何权限
* 0644 所有者可读写,其他人可读
* 0755 所有者有所有权限,其他所有人可读和执行
* 0740 所有者有所有权限,所有者所在的组可读
* 0777 所有权限
*/
public static function CreateFolder(string $path, int $permissions = 0777): void
{
if (!file_exists($path)) {
mkdir($path, $permissions);
}
}
}

View File

@ -0,0 +1,59 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class Constants
{
const QR_CACHEABLE = false;
const QR_CACHE_DIR = ''; //dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
const QR_FIND_BEST_MASK = true;
const QR_FIND_FROM_RANDOM = false;
const QR_DEFAULT_MASK = 2;
// Encoding modes
const QR_MODE_NUL = -1;
const QR_MODE_NUM = 0;
const QR_MODE_AN = 1;
const QR_MODE_8 = 2;
const QR_MODE_KANJI = 3;
const QR_MODE_STRUCTURE = 4;
// Levels of error correction.
const QR_ECLEVEL_L = 0;
const QR_ECLEVEL_M = 1;
const QR_ECLEVEL_Q = 2;
const QR_ECLEVEL_H = 3;
// Supported output formats
const STRUCTURE_HEADER_BITS = 20;
const MAX_STRUCTURED_SYMBOLS = 16;
// Maks
const N1 = 3;
const N2 = 3;
const N3 = 40;
const N4 = 10;
const QRSPEC_VERSION_MAX = 40;
const QRSPEC_WIDTH_MAX = 177;
const QRCAP_WIDTH = 0;
const QRCAP_WORDS = 1;
const QRCAP_REMINDER = 2;
const QRCAP_EC = 3;
}

View File

@ -0,0 +1,117 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class FrameFiller
{
public int $width;
public array $frame;
public int $x;
public int $y;
public int $dir;
public int $bit;
/**
* @param $width
* @param $frame
*/
public function __construct($width, &$frame)
{
$this->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);
}
}

View File

@ -0,0 +1,200 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrBitStream
{
public array $data = [];
/**
* @return int
*/
public function size(): int
{
return count($this->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;
}
}

View File

@ -0,0 +1,164 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrCode
{
public int $version;
public int $width;
public array $data;
//----------------------------------------------------------------------
public function encodeMask(QrInput $input, $mask)
{
if ($input->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);
}
}

View File

@ -0,0 +1,76 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrEncode
{
public bool $case_sensitive = true;
public bool $eight_bit = false;
public int $version = 0;
public int $size = 3;
public int $margin = 4;
public int $structured = 0; // not supported yet
public int $level = Constants::QR_ECLEVEL_L;
public int $hint = Constants::QR_MODE_8;
/**
* @param int $level
* @param int $size
* @param int $margin
* @return QrEncode
*/
public static function factory(int $level = Constants::QR_ECLEVEL_L, int $size = 3, int $margin = 4): QrEncode
{
$enc = new QrEncode();
$enc->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);
}
}

View File

@ -0,0 +1,89 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrImage
{
//----------------------------------------------------------------------
public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4, $saveandprint = FALSE)
{
$image = self::image($frame, $pixelPerPoint, $outerFrame);
if ($filename === false) {
Header("Content-type: image/png");
ImagePng($image);
} else {
if ($saveandprint === TRUE) {
ImagePng($image, $filename);
header("Content-type: image/png");
ImagePng($image);
} else {
ImagePng($image, $filename);
}
}
ImageDestroy($image);
}
//----------------------------------------------------------------------
public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
{
$image = self::image($frame, $pixelPerPoint, $outerFrame);
if ($filename === false) {
Header("Content-type: image/jpeg");
ImageJpeg($image, null, $q);
} else {
ImageJpeg($image, $filename, $q);
}
ImageDestroy($image);
}
//----------------------------------------------------------------------
public static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
{
$h = count($frame);
$w = strlen($frame[0]);
$imgW = $w + 2 * $outerFrame;
$imgH = $h + 2 * $outerFrame;
$base_image = ImageCreate($imgW, $imgH);
$col[0] = ImageColorAllocate($base_image, 255, 255, 255);
$col[1] = ImageColorAllocate($base_image, 0, 0, 0);
imagefill($base_image, 0, 0, $col[0]);
for ($y = 0; $y < $h; $y++) {
for ($x = 0; $x < $w; $x++) {
if ($frame[$y][$x] == '1') {
ImageSetPixel($base_image, $x + $outerFrame, $y + $outerFrame, $col[1]);
}
}
}
$target_image = ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
ImageDestroy($base_image);
return $target_image;
}
}

View File

@ -0,0 +1,492 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrInput
{
public ?array $items;
private int $version;
private int $level;
/**
* @param int $version
* @param int $level
* @throws Exception
*/
public function __construct(int $version = 0, int $level = Constants::QR_ECLEVEL_L)
{
if ($version < 0 || $version > 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();
}
}

View File

@ -0,0 +1,280 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrInputItem
{
public $mode;
public $size;
public $data;
public $bstream;
public function __construct($mode, $size, $data, $bstream = null)
{
$setData = array_slice($data, 0, $size);
if (count($setData) < $size) {
$setData = array_merge($setData, array_fill(0, $size - count($setData), 0));
}
if (!QrInput::check($mode, $size, $setData)) {
throw new Exception('Error m:' . $mode . ',s:' . $size . ',d:' . join(',', $setData));
return null;
}
$this->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;
}
}
}

View File

@ -0,0 +1,347 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrMask
{
public array $runLength = [];
//----------------------------------------------------------------------
public function __construct()
{
$this->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;
}
//----------------------------------------------------------------------
}

View File

@ -0,0 +1,128 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrRawCode
{
public $version;
public $datacode = [];
public $ecccode = [];
public $blocks;
public $rsblocks = []; //of RSblock
public $count;
public $dataLength;
public $eccLength;
public $b1;
//----------------------------------------------------------------------
public function __construct(QrInput $input)
{
$spec = array(0, 0, 0, 0, 0);
$this->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;
}
}

View File

@ -0,0 +1,44 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrRs
{
public static array $items = [];
//----------------------------------------------------------------------
public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
{
foreach (self::$items as $rs) {
if ($rs->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;
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrRsBlock
{
public $dataLength;
public $data = [];
public $eccLength;
public $ecc = [];
public function __construct($dl, $data, $el, &$ecc, QrRsItem $rs)
{
$rs->encode_rs_char($data, $ecc);
$this->dataLength = $dl;
$this->data = $data;
$this->eccLength = $el;
$this->ecc = $ecc;
}
}

View File

@ -0,0 +1,173 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrRsItem
{
public $mm; // Bits per symbol
public $nn; // Symbols per block (= (1<<mm)-1)
public $alpha_to = []; // log lookup table
public $index_of = []; // Antilog lookup table
public $genpoly = []; // Generator polynomial
public $nroots; // Number of generator roots = number of parity symbols
public $fcr; // First consecutive root, index form
public $prim; // Primitive element, index form
public $iprim; // prim-th root of 1, index form
public $pad; // Padding bytes in shortened block
public $gfpoly;
//----------------------------------------------------------------------
public function modnn($x)
{
while ($x >= $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);
}
}
}
}

View File

@ -0,0 +1,600 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrSpec
{
public static array $capacity = array(
array(0, 0, 0, array(0, 0, 0, 0)),
array(21, 26, 0, array(7, 10, 13, 17)), // 1
array(25, 44, 7, array(10, 16, 22, 28)),
array(29, 70, 7, array(15, 26, 36, 44)),
array(33, 100, 7, array(20, 36, 52, 64)),
array(37, 134, 7, array(26, 48, 72, 88)), // 5
array(41, 172, 7, array(36, 64, 96, 112)),
array(45, 196, 0, array(40, 72, 108, 130)),
array(49, 242, 0, array(48, 88, 132, 156)),
array(53, 292, 0, array(60, 110, 160, 192)),
array(57, 346, 0, array(72, 130, 192, 224)), //10
array(61, 404, 0, array(80, 150, 224, 264)),
array(65, 466, 0, array(96, 176, 260, 308)),
array(69, 532, 0, array(104, 198, 288, 352)),
array(73, 581, 3, array(120, 216, 320, 384)),
array(77, 655, 3, array(132, 240, 360, 432)), //15
array(81, 733, 3, array(144, 280, 408, 480)),
array(85, 815, 3, array(168, 308, 448, 532)),
array(89, 901, 3, array(180, 338, 504, 588)),
array(93, 991, 3, array(196, 364, 546, 650)),
array(97, 1085, 3, array(224, 416, 600, 700)), //20
array(101, 1156, 4, array(224, 442, 644, 750)),
array(105, 1258, 4, array(252, 476, 690, 816)),
array(109, 1364, 4, array(270, 504, 750, 900)),
array(113, 1474, 4, array(300, 560, 810, 960)),
array(117, 1588, 4, array(312, 588, 870, 1050)), //25
array(121, 1706, 4, array(336, 644, 952, 1110)),
array(125, 1828, 4, array(360, 700, 1020, 1200)),
array(129, 1921, 3, array(390, 728, 1050, 1260)),
array(133, 2051, 3, array(420, 784, 1140, 1350)),
array(137, 2185, 3, array(450, 812, 1200, 1440)), //30
array(141, 2323, 3, array(480, 868, 1290, 1530)),
array(145, 2465, 3, array(510, 924, 1350, 1620)),
array(149, 2611, 3, array(540, 980, 1440, 1710)),
array(153, 2761, 3, array(570, 1036, 1530, 1800)),
array(157, 2876, 0, array(570, 1064, 1590, 1890)), //35
array(161, 3034, 0, array(600, 1120, 1680, 1980)),
array(165, 3196, 0, array(630, 1204, 1770, 2100)),
array(169, 3362, 0, array(660, 1260, 1860, 2220)),
array(173, 3532, 0, array(720, 1316, 1950, 2310)),
array(177, 3706, 0, array(750, 1372, 2040, 2430)) //40
);
/**
* @param int $version
* @param int $level
* @return mixed
*/
public static function getDataLength(int $version, int $level): mixed
{
return self::$capacity[$version][Constants::QRCAP_WORDS] - self::$capacity[$version][Constants::QRCAP_EC][$level];
}
public static function getECCLength(int $version,int $level)
{
return self::$capacity[$version][Constants::QRCAP_EC][$level];
}
//----------------------------------------------------------------------
public static function getWidth(int $version)
{
return self::$capacity[$version][Constants::QRCAP_WIDTH];
}
//----------------------------------------------------------------------
public static function getRemainder(int $version)
{
return self::$capacity[$version][Constants::QRCAP_REMINDER];
}
//----------------------------------------------------------------------
public static function getMinimumVersion(int $size, int $level)
{
for ($i = 1; $i <= Constants::QRSPEC_VERSION_MAX; $i++) {
$words = self::$capacity[$i][Constants::QRCAP_WORDS] - self::$capacity[$i][Constants::QRCAP_EC][$level];
if ($words >= $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];
}
}

View File

@ -0,0 +1,340 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
use Exception;
class QrSplit
{
public string $data_str = '';
public QrInput $input;
public int $mode_hint;
/**
* @param string $data_str
* @param QrInput $input
* @param int $mode_hint
*/
public function __construct(string $data_str, QrInput $input, int $mode_hint)
{
$this->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();
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrStr
{
/**
* @param array $src_tab
* @param int $x
* @param int $y
* @param string $repl
* @param int|bool $replLen
* @return void
*/
public static function set(array &$src_tab, int $x, int $y, string $repl, int|bool $replLen = false): void
{
$src_tab[$y] = substr_replace($src_tab[$y], ($replLen !== false) ? substr($repl, 0, $replLen) : $repl, $x, ($replLen !== false) ? $replLen : strlen($repl));
}
}

View File

@ -0,0 +1,39 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode\Lib;
class QrTools
{
/**
* @use 二值化
* @param array $frame
* @return array
*/
public static function binarize(array $frame): array
{
$len = count($frame);
foreach ($frame as &$frameLine) {
for ($i = 0; $i < $len; $i++) {
$frameLine[$i] = (ord($frameLine[$i]) & 1) ? '1' : '0';
}
}
return $frame;
}
}

View File

@ -0,0 +1,88 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Qrcode;
use Bhp\Util\Qrcode\Lib\Qrcode as QrConsole;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
define('QR_CACHEABLE', true); // use cache - more disk reads but less CPU power, masks and format templates are stored there
define('QR_CACHE_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR); // used when QR_CACHEABLE === true
define('QR_LOG_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); // default error logs dir
define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
define('QR_FIND_FROM_RANDOM', false); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false
define('QR_PNG_MAXIMUM_SIZE', 1024);
// Supported output formats
define('QR_FORMAT_TEXT', 0);
define('QR_FORMAT_PNG', 1);
class Qrcode
{
/**
* show qrCode on console.
*
* @param string $text
*
* @return void
*/
public static function show(string $text): void
{
$output = new ConsoleOutput();
static::initQrcodeStyle($output);
//
$pxMap[0] = static::isWin() ? '<whitec>mm</whitec>' : '<whitec> </whitec>';
$pxMap[1] = '<blackc> </blackc>';
//
$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);
}
}

3
src/Util/Qrcode/VERSION Normal file
View File

@ -0,0 +1,3 @@
aferrandini/phpqrcode
1.1.5
2012021604

View File

@ -0,0 +1,154 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Resource;
use Bhp\Util\Resource\Resource as EResource;
use Bhp\Util\DesignPattern\SingleTon;
abstract class BaseResource extends SingleTon
{
/**
* @use 配置对象变量
* @var EResource
*/
protected ?Resource $resource = null;
/**
* @use 配置文件名
* @var string
*/
protected string $filename = '';
/**
* @use 配置文件路径
* @var string
*/
protected string $filepath = '';
/**
* @use 最后访问文件时间
* @var int
*/
protected int $last_access = 0;
/**
* @use 解析器
* @var string
*/
protected string $parser = '';
/**
* @use 设置值
* @param string $key
* @param mixed $value
* @return void
*/
public function set(string $key, mixed $value): void
{
// 设置
$this->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;
}

View File

@ -0,0 +1,153 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Resource;
use Bhp\Util\Resource\Resource as EResource;
abstract class BaseResourcePoly
{
/**
* @use 配置对象变量
* @var EResource
*/
protected ?Resource $resource = null;
/**
* @use 配置文件名
* @var string
*/
protected string $filename = '';
/**
* @use 配置文件路径
* @var string
*/
protected string $filepath = '';
/**
* @use 最后访问文件时间
* @var int
*/
protected int $last_access = 0;
/**
* @use 解析器
* @var string
*/
protected string $parser = '';
/**
* @use 设置值
* @param string $key
* @param mixed $value
* @return void
*/
public function set(string $key, mixed $value): void
{
// 设置
$this->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;
}

View File

@ -0,0 +1,218 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Resource;
use RecursiveArrayIterator;
use Toolkit\Stdlib\Arr;
use Traversable;
use function is_array;
use function serialize;
use function strpos;
use function unserialize;
/**
* Class Collection -> 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
]);
}
}

View File

@ -0,0 +1,62 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Resource;
use ArrayAccess;
use Countable;
use IteratorAggregate;
use JsonSerializable;
/**
* Collection Interface
*/
interface CollectionInterface extends ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
public function set(string $key, mixed $value): void;
public function get(string $key, $default = null): mixed;
/**
* @param array $items
*/
public function replace(array $items): void;
/**
* @return array
*/
public function all(): array;
/**
* @param string $key
*
* @return bool
*/
public function has(string $key): bool;
/**
* @param string $key
*
* @return mixed
*/
public function remove(string $key): void;
/**
* clear all data
*/
public function clear(): void;
}

View File

@ -0,0 +1,109 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Util\Resource;
use Grasmash\Expander\Expander;
use Grasmash\Expander\Stringifier;
use JBZoo\Data\Data;
use function JBZoo\Data\data;
use function JBZoo\Data\ini;
use function JBZoo\Data\phpArray;
use function JBZoo\Data\json;
use function JBZoo\Data\yml;
class Resource extends Collection
{
protected const FORMAT_INI = 'ini';
protected const FORMAT_PHP = 'php';
protected const FORMAT_YAML = 'yaml';
protected const FORMAT_YML = 'yml';
protected const FORMAT_JSON = 'json';
/**
* @var Data
*/
protected Data $config;
/**
* @var string
*/
protected string $file_path;
/**
* @var string
*/
protected string $parser;
/**
* @use 加载资源文件
* @param string|array $file_path
* @param string $parser
* @return Resource
*/
public function loadF(string|array $file_path, string $parser): Resource
{
// 存储文件路径
$this->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;
}
}

View File

@ -1,234 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use Throwable;
use Amp\Loop;
use BiliHelper\Script\BaseTask;
use function Amp\asyncCall;
class App
{
private int $mode = 0;
/**
* App constructor.
* @param string $app_path
*/
public function __construct(string $app_path)
{
define('APP_MICROSECOND', 1000000);
define('APP_CONF_PATH', $app_path . '/conf/');
define('APP_DATA_PATH', $app_path . '/data/');
define('APP_LOG_PATH', $app_path . '/log/');
define('APP_TASK_PATH', $app_path . '/task/');
define('APP_CACHE_PATH', $app_path . '/cache/');
}
/**
* @use 初始化数据文件夹
* @param int $permissions
* @return void
*/
private function initDataFolder(int $permissions = 0777): void
{
// log task cache
$data_folder = [APP_LOG_PATH, APP_TASK_PATH, APP_CACHE_PATH];
foreach ($data_folder as $path) {
if (!file_exists($path)) {
mkdir($path);
chmod($path, $permissions);
}
}
}
/**
* @use 检查环境
* @return $this
*/
public function inspect(): App
{
(new Env())->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();
}
}
}

View File

@ -1,59 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use Ahc\Cli\Input\Command;
use Exception;
class BCommand
{
private array $argv;
/**
* Command constructor.
* @param $argv
*/
public function __construct($argv)
{
$this->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;
}
}

View File

@ -1,126 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
* Source: https://github.com/fzaninotto/Faker/
*/
namespace BiliHelper\Core;
use BiliHelper\Util\Singleton;
use Flintstone\Flintstone;
use Overtrue\Pinyin\Pinyin;
use Flintstone\Formatter\JsonFormatter;
class Cache
{
use Singleton;
private array $caches;
private Flintstone $cache;
// 文档
// https://www.xeweb.net/flintstone/documentation/
/**
* @use 加载一个缓存
* @param string $classname
* @return \BiliHelper\Core\Cache
*/
private function load(string $classname): static
{
if (!isset($this->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;
}
}

View File

@ -1,94 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use BiliHelper\Util\Singleton;
use Jelix\IniFile\IniModifier;
class Config
{
use Singleton;
private string|array $load_file;
private int|false $last_time;
private IniModifier $app_config;
private string $config_path;
/**
* @use 加载配置
* @param string $load_file
*/
public function load(string $load_file)
{
$config_path = str_replace("\\", "/", APP_CONF_PATH . $load_file);
if (!is_file($config_path)) {
die("配置文件 $load_file 加载错误,请参照文档添加配置文件!");
}
$this->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);
}
}

106
src/core/Core.php Normal file
View File

@ -0,0 +1,106 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
namespace Bhp\Core;
use Bhp\Console\Console;
use Bhp\Util\DesignPattern\SingleTon;
use Bhp\Util\Os\Path;
class Core extends SingleTon
{
/**
* @var string
*/
protected string $global_path;
/**
* @var string
*/
protected string $profile_name;
/**
* @param string $global_path
* @param string $profile_name
* @return void
*/
public function init(string $global_path, string $profile_name): void
{
$this->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);
}
}
}
}

View File

@ -1,362 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use Exception;
use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use BiliHelper\Tool\Generator;
use GuzzleHttp\Exception\RequestException;
class Curl
{
private static Client $client;
private static array $async_opt;
private static array $results = [];
private static array $result = [];
private static string $buvid = '';
/**
* @use POST请求
* @param $os
* @param $url
* @param array $params
* @param array $headers
* @param float $timeout
* @return mixed
*/
public static function post($os, $url, array $params = [], array $headers = [], float $timeout = 30.0): mixed
{
Log::debug("POST: $url");
$headers = self::getHeaders($os, $headers);
$payload['form_params'] = count($params) ? $params : [];
$options = self::getClientOpt($payload, $headers, $timeout);
$request = self::clientHandle($url, 'post', $options);
$body = $request->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);
}
}

View File

@ -1,92 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use BiliHelper\Util\Singleton;
use Consolidation\Config\Config;
use Consolidation\Config\Loader\YamlConfigLoader;
use Consolidation\Config\Loader\ConfigProcessor;
class Device
{
use Singleton;
private Config $device;
private string $bili_file = 'bili.yaml';
private string $device_file = 'device.yaml';
/**
* @use 真实路径
* @param string $file
* @return string
*/
private function fileRealPath(string $file): string
{
return APP_CONF_PATH . $file;
}
/**
* @use 加载配置
*/
public function load(string $load_file)
{
// 提前处理 后缀
$custom_file = str_replace(strrchr($load_file, "."), "", $load_file) . '_';
// 自定义客户端
if (is_file($this->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);
}
}

View File

@ -1,101 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use function JBZoo\Data\json;
class Env
{
private string $app_name;
private string $app_version;
private string $app_branch;
private string $app_source;
private string $repository = APP_DATA_PATH . 'latest_version.json';
/**
* Env constructor.
*/
public function __construct()
{
set_time_limit(0);
// header("Content-Type:text/html; charset=utf-8");
// ini_set('date.timezone', 'Asia/Shanghai');
date_default_timezone_set('Asia/Shanghai');
ini_set('display_errors', 'on');
error_reporting(E_ALL);
$this->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');
}
}

View File

@ -1,141 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
use BiliHelper\Core\Cache;
use BiliHelper\Core\Config;
use BiliHelper\Core\Device;
/**
* @use 配置读取
* @param $name
* @param int|string $section
* @param null $key
* @return mixed
*/
function getConf($name, int|string $section = 0, $key = null): mixed
{
return Config::getInstance()->_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);
}

View File

@ -1,169 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
* Resource: https://github.com/XcantloadX
*/
namespace BiliHelper\Core;
class HttpClient
{
// private $ch;
// private $ret;
// private $form;
// private $headers;
// private $url;
// private $query;
//
// public function __construct(string $url)
// {
// $this->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);
//}

View File

@ -1,180 +0,0 @@
<?php
/**
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| |
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* & l、
* (゚、
*  \、゙ ~ *
*  じしf_, )
*
*/
namespace BiliHelper\Core;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Bramus\Monolog\Formatter\ColoredLineFormatter;
class Log
{
protected static $instance;
/**
* @use 实体
*/
static public function getLogger()
{
if (!self::$instance) {
self::configureInstance();
}
return self::$instance;
}
/**
* @use 单例
*/
private static function configureInstance()
{
$logger = new Logger('BH');
$handler = new StreamHandler('php://stdout', getConf('enable', 'debug') ? Logger::DEBUG : Logger::INFO);
$handler->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));
}
}
}

Some files were not shown because too many files have changed in this diff Show More