mirror of
https://github.com/lkeme/BiliHelper-personal.git
synced 2025-12-19 01:20:08 +08:00
[init] Initialization
This commit is contained in:
parent
79d8c0e3d9
commit
f505b40d9e
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Example user template template
|
||||
### Example user template
|
||||
|
||||
# IntelliJ project files
|
||||
.idea
|
||||
.idea/
|
||||
index1.php
|
||||
user/
|
||||
record/
|
||||
temp/
|
||||
tmp/
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
/vendor/
|
||||
/configs/
|
||||
/tests/
|
||||
config
|
||||
*.log
|
||||
Traits/
|
||||
README1.md
|
||||
conf/user.conf
|
||||
/conf/user.conf
|
||||
/log/
|
||||
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Release Notes
|
||||
# 本项目Log
|
||||
|
||||
## v0.0.1.190713 alpha (2019-07-13)
|
||||
|
||||
### Added
|
||||
- Initialization
|
||||
|
||||
### Changed
|
||||
- Initialization
|
||||
|
||||
### Fixed
|
||||
- Initialization
|
||||
224
DOC.md
Normal file
224
DOC.md
Normal file
@ -0,0 +1,224 @@
|
||||
|
||||
<p align="center"><img width="300px" src="https://i.loli.net/2018/04/20/5ad97bd395912.jpeg"></p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-0.0.1.190713 alpha-green.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>
|
||||
|
||||
|
||||
# BiliHelper
|
||||
|
||||
B 站直播实用脚本
|
||||
|
||||
|
||||
|
||||
## 功能组件
|
||||
|
||||
|plugin |version |description |
|
||||
|--------------------|--------------------|--------------------|
|
||||
|Daily |19.07.13 |每日背包奖励 |
|
||||
|GiftSend |19.07.13 |自动清空过期礼物 |
|
||||
|Heart |19.07.13 |双端直播间心跳 |
|
||||
|Login |19.07.13 |帐号登录组件 |
|
||||
|Silver |19.07.13 |自动领宝箱 |
|
||||
|Task |19.07.13 |每日任务 |
|
||||
|GiftHeart |19.07.13 |心跳礼物 |
|
||||
|Silver2Coin |19.07.13 |银瓜子换硬币 |
|
||||
|MaterialObject |19.07.13 |实物抽奖 |
|
||||
|GroupSignIn |19.07.13 |应援团签到 |
|
||||
|Storm |19.07.13 |节奏风暴 |
|
||||
|Notice |19.07.13 |Server酱 |
|
||||
|RaffleHandler |19.07.13 |统一活动抽奖 |
|
||||
|MasterSite |19.07.13 |主站(观看、分享、投币)|
|
||||
|Guard |19.07.13 |舰长上船亲密度 |
|
||||
|
||||
## 打赏赞助
|
||||
|
||||

|
||||
|
||||
|
||||
## 未完成功能
|
||||
|
||||
|待续 |
|
||||
|-----------|
|
||||
|添加多用户 |
|
||||
|待添加 |
|
||||
|
||||
## 环境依赖
|
||||
|
||||
|Requirement |
|
||||
|--------------------|
|
||||
|PHP >=7.0 |
|
||||
|php_curl |
|
||||
|php_sockets |
|
||||
|php_openssl |
|
||||
|php_json |
|
||||
|php_zlib |
|
||||
|php_mbstring |
|
||||
|待添加 |
|
||||
|
||||
通常使用 `composer` 工具会自动检测上述依赖问题。
|
||||
|
||||
## Composer
|
||||
* 项目 `composer.lock` 基于阿里云Composer镜像生成
|
||||
+ 阿里云(推荐)
|
||||
```
|
||||
# 使用帮助
|
||||
https://developer.aliyun.com/composer
|
||||
# 使用命令
|
||||
> composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
|
||||
```
|
||||
* 腾讯云(备用)
|
||||
```
|
||||
# 使用帮助
|
||||
https://mirrors.cloud.tencent.com/composer/
|
||||
# 使用命令
|
||||
composer config -g repos.packagist composer https://mirrors.cloud.tencent.com/composer/
|
||||
```
|
||||
|
||||
## 打赏赞助
|
||||
|
||||

|
||||
|
||||
> 待添加
|
||||
|
||||
## 使用指南
|
||||
|
||||
1. 下载(克隆)项目代码,初始化项目
|
||||
```
|
||||
$ git clone https://github.com/lkeme/BiliHelper-personal.git
|
||||
$ cd BiliHelper/conf
|
||||
$ cp user.conf.example user.conf
|
||||
```
|
||||
2. 使用 [composer](https://getcomposer.org/download/) 工具进行安装
|
||||
```
|
||||
$ composer install
|
||||
```
|
||||
3. 按照说明修改配置文件 `user.conf`,只需填写帐号密码即可
|
||||
4. 运行测试
|
||||
```
|
||||
$ php index.php
|
||||
```
|
||||
> 以下是`多开方案`,单个账户可以无视
|
||||
5. 复制一份example配置文件,修改账号密码即可
|
||||
```
|
||||
$ php index.php example.conf
|
||||
```
|
||||
6. 请保证配置文件存在,否则默认加载`user.conf`配置文件
|
||||
|
||||
<p align="center"><img width="680px" src="https://i.loli.net/2018/04/21/5adb497dc3ece.png"></p>
|
||||
|
||||
|
||||
## 升级指南
|
||||
|
||||
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=Bili Helper 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.conf` 文件中有个 `ROOM_ID` 配置,填写此项可以清空临过期礼物给指定直播间。
|
||||
|
||||
通常可以在直播间页面的 url 获取到它
|
||||
```
|
||||
http://live.bilibili.com/9522051
|
||||
```
|
||||
|
||||
所有直播间号码小于 1000 的直播间为短号,该脚本在每次启动会自动修正,无需关心,
|
||||
|
||||
## 相关
|
||||
|
||||
> 本项目基于[BilibiliHelper](https://github.com/metowolf/BilibiliHelper)项目
|
||||
|
||||
> 基于父项目的架构开发,在此感谢父项目的开发
|
||||
|
||||
> 保留父项目没必要修改的信息,另外欢迎重构(Haha)
|
||||
|
||||
## License 许可证
|
||||
|
||||
BiliHelper is under the MIT license.
|
||||
|
||||
本项目基于 MIT 协议发布,并增加了 SATA 协议。
|
||||
|
||||
当你使用了使用 SATA 的开源软件或文档的时候,在遵守基础许可证的前提下,你必须马不停蹄地给你所使用的开源项目 “点赞” ,比如在 GitHub 上 star,然后你必须感谢这个帮助了你的开源项目的作者,作者信息可以在许可证头部的版权声明部分找到。
|
||||
|
||||
本项目的所有代码文件、配置项,除另有说明外,均基于上述介绍的协议发布,具体请看分支下的 LICENSE。
|
||||
|
||||
此处的文字仅用于说明,条款以 LICENSE 文件中的内容为准。
|
||||
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# BiliHelper
|
||||
B 站直播实用脚本
|
||||
|
||||
## 公告
|
||||
|
||||
Currently for Personal Edition **0.0.1.190713 alpha**
|
||||
|
||||
## 文档
|
||||
|
||||
* [使用文档 / DOC.md](./DOC.md)
|
||||
* [更新日志 / CHANGELOG.md](./CHANGELOG.md)
|
||||
|
||||
## 交流
|
||||
|
||||
Group: [55308141](https://jq.qq.com/?_wv=1027&k=5AIDaJg)
|
||||
|
||||
## 打赏
|
||||
|
||||

|
||||
|
||||
## 效果
|
||||
|
||||

|
||||
|
||||
31
composer.json
Normal file
31
composer.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "lkeme/bilihelper",
|
||||
"description": "B 站自动领瓜子、直播助手、直播挂机脚本、主站助手 - PHP 版(Personal)",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-mbstring": "*",
|
||||
"vlucas/phpdotenv": "^2.4",
|
||||
"monolog/monolog": "^1.23",
|
||||
"bramus/monolog-colored-line-formatter": "^2.0",
|
||||
"clue/socket-raw": "1.4"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lkeme",
|
||||
"email": "Useri@live.cn",
|
||||
"homepage": "https://mudew.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"lkeme\\BiliHelper\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
439
composer.lock
generated
Normal file
439
composer.lock
generated
Normal file
@ -0,0 +1,439 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "6fca2d61232110e041d8cb569c30d02f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bramus/ansi-php",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bramus/ansi-php.git",
|
||||
"reference": "79d30c30651b0c6f23cf85503c779c72ac74ab8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bramus/ansi-php/zipball/79d30c30651b0c6f23cf85503c779c72ac74ab8a",
|
||||
"reference": "79d30c30651b0c6f23cf85503c779c72ac74ab8a",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Bramus\\Ansi\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bramus Van Damme",
|
||||
"email": "bramus@bram.us",
|
||||
"homepage": "https://www.bram.us/"
|
||||
}
|
||||
],
|
||||
"description": "ANSI Control Functions and ANSI Control Sequences (Colors, Erasing, etc.) for PHP CLI Apps",
|
||||
"time": "2019-02-12T15:05:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bramus/monolog-colored-line-formatter",
|
||||
"version": "2.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bramus/monolog-colored-line-formatter.git",
|
||||
"reference": "6bff15eee00afe2690642535b0f1541f10852c2b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bramus/monolog-colored-line-formatter/zipball/6bff15eee00afe2690642535b0f1541f10852c2b",
|
||||
"reference": "6bff15eee00afe2690642535b0f1541f10852c2b",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"bramus/ansi-php": "~3.0",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"monolog/monolog": "~1.0",
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Bramus\\Monolog\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bramus Van Damme",
|
||||
"email": "bramus@bram.us",
|
||||
"homepage": "https://www.bram.us/"
|
||||
}
|
||||
],
|
||||
"description": "Colored Line Formatter for Monolog",
|
||||
"time": "2015-01-07T22:12:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "clue/socket-raw",
|
||||
"version": "v1.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/clue/php-socket-raw.git",
|
||||
"reference": "2f6654445233407900c9a804490cecd8e4f2ae86"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/clue/php-socket-raw/zipball/2f6654445233407900c9a804490cecd8e4f2ae86",
|
||||
"reference": "2f6654445233407900c9a804490cecd8e4f2ae86",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"ext-sockets": "*",
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0 || ^6.0 || ^5.2 || ^4.8.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Socket\\Raw\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@lueck.tv"
|
||||
}
|
||||
],
|
||||
"description": "Simple and lightweight OOP wrapper for PHP's low level sockets extension (ext-sockets)",
|
||||
"homepage": "https://github.com/clue/php-socket-raw",
|
||||
"keywords": [
|
||||
"Socket",
|
||||
"client",
|
||||
"datagram",
|
||||
"dgram",
|
||||
"icmp",
|
||||
"ipv6",
|
||||
"server",
|
||||
"stream",
|
||||
"tcp",
|
||||
"udg",
|
||||
"udp",
|
||||
"unix"
|
||||
],
|
||||
"time": "2019-01-22T11:08:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
|
||||
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||
"doctrine/couchdb": "~1.0@dev",
|
||||
"graylog2/gelf-php": "~1.0",
|
||||
"jakub-onderka/php-parallel-lint": "0.9",
|
||||
"php-amqplib/php-amqplib": "~2.4",
|
||||
"php-console/php-console": "^3.1.3",
|
||||
"phpunit/phpunit": "~4.5",
|
||||
"phpunit/phpunit-mock-objects": "2.3.0",
|
||||
"ruflin/elastica": ">=0.90 <3.0",
|
||||
"sentry/sentry": "^0.13",
|
||||
"swiftmailer/swiftmailer": "^5.3|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
|
||||
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||
"ext-mongo": "Allow sending log messages to a MongoDB server",
|
||||
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||
"mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
|
||||
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
|
||||
"php-console/php-console": "Allow sending log messages to Google Chrome",
|
||||
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
||||
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
|
||||
"sentry/sentry": "Allow sending log messages to a Sentry server"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Monolog\\": "src/Monolog"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
}
|
||||
],
|
||||
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
|
||||
"homepage": "http://github.com/Seldaek/monolog",
|
||||
"keywords": [
|
||||
"log",
|
||||
"logging",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2018-11-05T09:00:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2018-11-20T15:27:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v2.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
|
||||
"reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-ctype": "^1.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dotenv\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Vance Lucas",
|
||||
"email": "vance@vancelucas.com",
|
||||
"homepage": "http://www.vancelucas.com"
|
||||
}
|
||||
],
|
||||
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
"environment"
|
||||
],
|
||||
"time": "2019-01-29T11:11:52+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.0.0",
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
114
conf/user.conf.example
Normal file
114
conf/user.conf.example
Normal file
@ -0,0 +1,114 @@
|
||||
#######################
|
||||
# 账户设置 #
|
||||
#######################
|
||||
|
||||
# 帐号|密码
|
||||
APP_USER=
|
||||
APP_PASS=
|
||||
|
||||
# 令牌(自动生成)
|
||||
ACCESS_TOKEN=
|
||||
REFRESH_TOKEN=
|
||||
COOKIE=
|
||||
|
||||
#######################
|
||||
# 功能设置 #
|
||||
#######################
|
||||
|
||||
# 推送服务器
|
||||
USE_SERVER=true
|
||||
SERVER_ADDR=
|
||||
SERVER_KEY=
|
||||
|
||||
# 舰长总督
|
||||
USE_GUARD=true
|
||||
|
||||
# 主站助手
|
||||
USE_MASTER_SITE=true
|
||||
|
||||
# 活跃弹幕|弹幕房间(为空则随机)|弹幕内容(为空随机,但是URL不能为空)
|
||||
USE_DANMU=true
|
||||
DANMU_ROOMID=9522051
|
||||
DANMU_CONTENT=
|
||||
CONTENT_FETCH="https://v1.hitokoto.cn/?encode=text"
|
||||
|
||||
# 视频投币|投币稿件数(每日任务最大5)|自定义稿件ID,空则随机(AV_NUM=1生效)
|
||||
USE_ADD_COIN=false
|
||||
ADD_COIN_AV_NUM=1
|
||||
ADD_COIN_AV=33492180
|
||||
|
||||
# SERVER酱,用于推送消息
|
||||
USE_SCKEY=
|
||||
|
||||
# 切换HTTPS,为真则使用https协议
|
||||
USE_HTTPS=true
|
||||
|
||||
# 是否使用代理(前提保证有效代理)
|
||||
USE_PROXY=false
|
||||
PROXY_IP=127.0.0.1
|
||||
PROXY_PORT=8888
|
||||
|
||||
# 直播间ID,用于礼物赠送
|
||||
ROOM_ID=9522051
|
||||
|
||||
# 弹幕监控房间(为空则随机)
|
||||
SOCKET_ROOM_ID=9522051
|
||||
|
||||
# 节奏风暴|丢弃率(0-100)|尝试数(范围值)
|
||||
USE_STORM=true
|
||||
STORM_DROPRATE=0
|
||||
STORM_ATTEMPT=30,50
|
||||
|
||||
# 统一活动
|
||||
USE_ACTIVE=true
|
||||
|
||||
# 银瓜子兑换硬币
|
||||
USE_SILVER2COIN=true
|
||||
|
||||
# 实物抽奖
|
||||
USE_MO=true
|
||||
|
||||
#######################
|
||||
# 日志设置 #
|
||||
#######################
|
||||
|
||||
# 写入日志
|
||||
APP_WRITELOG=false
|
||||
|
||||
# 日志路径
|
||||
APP_WRITELOGPATH=log
|
||||
|
||||
# 调试模式
|
||||
APP_DEBUG=false
|
||||
|
||||
# 用户名,可自定义
|
||||
APP_UNAME=
|
||||
|
||||
# 多账号区别输出
|
||||
APP_MULTIPLE=false
|
||||
|
||||
# 账号别名,如果为空则默认使用登陆账号作为标示
|
||||
APP_USER_IDENTITY=
|
||||
|
||||
# 日志回调地址
|
||||
APP_CALLBACK="http://www.example.com/api.send?text={account}[{level}]: {message}"
|
||||
|
||||
# 错误回调级别
|
||||
#
|
||||
# DEBUG 100
|
||||
# INFO 200
|
||||
# NOTICE 250
|
||||
# WARNING 300
|
||||
# ERROR 400
|
||||
#
|
||||
APP_CALLBACK_LEVEL=400
|
||||
|
||||
#######################
|
||||
# 固定设置 #
|
||||
#######################
|
||||
|
||||
# 用于打包请求
|
||||
ACTIONENTRY=7
|
||||
|
||||
# 用于发送心跳
|
||||
ACTIONHEARTBEAT=2
|
||||
94
index.php
Normal file
94
index.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
//autoload
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
set_time_limit(0);
|
||||
header("Content-Type:text/html; charset=utf-8");
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
class Index
|
||||
{
|
||||
public static $conf_file = null;
|
||||
public static $dotenv = null;
|
||||
|
||||
/**
|
||||
* @param $conf_file
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function run($conf_file)
|
||||
{
|
||||
self::$conf_file = $conf_file;
|
||||
self::loadConfigFile();
|
||||
while (true) {
|
||||
if (!Login::check()) {
|
||||
self::$dotenv->overload();
|
||||
}
|
||||
Daily::run();
|
||||
MasterSite::run();
|
||||
Danmu::run();
|
||||
GiftSend::run();
|
||||
Heart::run();
|
||||
Silver::run();
|
||||
Task::run();
|
||||
Silver2Coin::run();
|
||||
GroupSignIn::run();
|
||||
Live::run();
|
||||
GiftHeart::run();
|
||||
Winning::run();
|
||||
MaterialObject::run();
|
||||
TcpClinet::run();
|
||||
Storm::run();
|
||||
RaffleHandler::run();
|
||||
Guard::run();
|
||||
Statistics::run();
|
||||
usleep(0.1 * 1000000);
|
||||
}
|
||||
}
|
||||
|
||||
protected static function loadConfigFile()
|
||||
{
|
||||
$file_path = __DIR__ . '/conf/' . self::$conf_file;
|
||||
|
||||
if (is_file($file_path) && self::$conf_file != 'user.conf') {
|
||||
$load_files = [
|
||||
self::$conf_file,
|
||||
];
|
||||
} else {
|
||||
$default_file_path = __DIR__ . '/conf/user.conf';
|
||||
if (!is_file($default_file_path)) {
|
||||
exit('默认加载配置文件不存在,请按照文档添加配置文件!');
|
||||
}
|
||||
|
||||
$load_files = [
|
||||
'user.conf',
|
||||
];
|
||||
}
|
||||
foreach ($load_files as $load_file) {
|
||||
self::$dotenv = new Dotenv(__DIR__ . '/conf', $load_file);
|
||||
self::$dotenv->load();
|
||||
}
|
||||
|
||||
// load ACCESS_KEY
|
||||
Login::run();
|
||||
self::$dotenv->overload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// LOAD
|
||||
$conf_file = isset($argv[1]) ? $argv[1] : 'user.conf';
|
||||
// RUN
|
||||
Index::run($conf_file);
|
||||
183
src/Curl.php
Normal file
183
src/Curl.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Curl
|
||||
{
|
||||
public static $headers = array(
|
||||
'Accept' => '*/*',
|
||||
'Accept-Encoding' => 'gzip',
|
||||
'Accept-Language' => 'zh-cn',
|
||||
'Connection' => 'keep-alive',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'User-Agent' => 'bili-universal/8230 CFNetwork/975.0.3 Darwin/18.2.0',
|
||||
// 'Referer' => 'https://live.bilibili.com/',
|
||||
);
|
||||
|
||||
private static function getHeaders($headers)
|
||||
{
|
||||
return array_map(function ($k, $v) {
|
||||
return $k . ': ' . $v;
|
||||
}, array_keys($headers), $headers);
|
||||
}
|
||||
|
||||
public static function post($url, $payload = null, $headers = null, $timeout = 30)
|
||||
{
|
||||
$url = self::http2https($url);
|
||||
Log::debug($url);
|
||||
$header = is_null($headers) ? self::getHeaders(self::$headers) : self::getHeaders($headers);
|
||||
|
||||
// 重试次数
|
||||
$ret_count = 300;
|
||||
$waring = 280;
|
||||
|
||||
while ($ret_count) {
|
||||
// 网络断开判断 延时方便连接网络
|
||||
if ($ret_count < $waring) {
|
||||
Log::warning("正常等待网络连接状态恢复正常...");
|
||||
sleep(mt_rand(5, 10));
|
||||
}
|
||||
try {
|
||||
$curl = curl_init();
|
||||
if (!is_null($payload)) {
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($payload) ? http_build_query($payload) : $payload);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($curl, CURLOPT_HEADER, 0);
|
||||
curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
|
||||
curl_setopt($curl, CURLOPT_IPRESOLVE, 1);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
// 超时 重要
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||
if (($cookie = getenv('COOKIE')) != "") {
|
||||
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
|
||||
}
|
||||
if (getenv('USE_PROXY') == 'true') {
|
||||
curl_setopt($curl, CURLOPT_PROXY, getenv('PROXY_IP'));
|
||||
curl_setopt($curl, CURLOPT_PROXYPORT, getenv('PROXY_PORT'));
|
||||
}
|
||||
$raw = curl_exec($curl);
|
||||
|
||||
if ($err_no = curl_errno($curl)) {
|
||||
throw new \Exception(curl_error($curl));
|
||||
}
|
||||
|
||||
if ($raw === false || strpos($raw, 'timeout') !== false) {
|
||||
Log::warning('重试,获取的资源无效!');
|
||||
$ret_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
Log::debug($raw);
|
||||
curl_close($curl);
|
||||
return $raw;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("重试,Curl请求出错,{$e->getMessage()}!");
|
||||
$ret_count--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
exit('重试次数过多,请检查代码,退出!');
|
||||
}
|
||||
|
||||
public static function other($url, $payload = null, $headers = null, $cookie = null, $timeout = 30)
|
||||
{
|
||||
Log::debug($url);
|
||||
$header = is_null($headers) ? self::getHeaders(self::$headers) : self::getHeaders($headers);
|
||||
|
||||
// 重试次数
|
||||
$ret_count = 30;
|
||||
while ($ret_count) {
|
||||
try {
|
||||
$curl = curl_init();
|
||||
if (!is_null($payload)) {
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($payload) ? http_build_query($payload) : $payload);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($curl, CURLOPT_HEADER, 0);
|
||||
curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
|
||||
curl_setopt($curl, CURLOPT_IPRESOLVE, 1);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
// 超时 重要
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||
// 认证
|
||||
if (!is_null($cookie)) {
|
||||
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
|
||||
}
|
||||
if (getenv('USE_PROXY') == 'true') {
|
||||
curl_setopt($curl, CURLOPT_PROXY, getenv('PROXY_IP'));
|
||||
curl_setopt($curl, CURLOPT_PROXYPORT, getenv('PROXY_PORT'));
|
||||
}
|
||||
$raw = curl_exec($curl);
|
||||
|
||||
if ($err_no = curl_errno($curl)) {
|
||||
throw new \Exception(curl_error($curl));
|
||||
}
|
||||
|
||||
if ($raw === false || strpos($raw, 'timeout') !== false) {
|
||||
Log::warning('重试,获取的资源无效!');
|
||||
$ret_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
Log::debug($raw);
|
||||
curl_close($curl);
|
||||
return $raw;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("重试,Curl请求出错,{$e->getMessage()}!");
|
||||
$ret_count--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
exit('重试次数过多,请检查代码,退出!');
|
||||
}
|
||||
|
||||
|
||||
public static function get($url, $payload = null, $headers = null)
|
||||
{
|
||||
if (!is_null($payload)) {
|
||||
$url .= '?' . http_build_query($payload);
|
||||
}
|
||||
return self::post($url, null, $headers);
|
||||
}
|
||||
|
||||
protected static function http2https($url)
|
||||
{
|
||||
switch (getenv('USE_HTTPS')) {
|
||||
case 'false':
|
||||
if (strpos($url, 'ttps://')) {
|
||||
$url = str_replace('https://', 'http://', $url);
|
||||
}
|
||||
break;
|
||||
case 'true':
|
||||
if (strpos($url, 'ttp://')) {
|
||||
$url = str_replace('http://', 'https://', $url);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log::warning('当前协议设置不正确,请检查配置文件!');
|
||||
die();
|
||||
break;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
40
src/Daily.php
Normal file
40
src/Daily.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Daily
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::dailyBag();
|
||||
|
||||
self::$lock = time() + 3600;
|
||||
}
|
||||
|
||||
protected static function dailyBag()
|
||||
{
|
||||
$payload = [];
|
||||
$data = Curl::get('https://api.live.bilibili.com/gift/v2/live/receive_daily_bag', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('每日礼包领取失败!', ['msg' => $data['message']]);
|
||||
} else {
|
||||
Log::notice('每日礼包领取成功');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
130
src/Danmu.php
Normal file
130
src/Danmu.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Danmu
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time() || getenv('USE_DANMU') == 'false') {
|
||||
return;
|
||||
}
|
||||
$room_id = empty(getenv('DANMU_ROOMID')) ? Live::getUserRecommend() : Live::getRealRoomID(getenv('DANMU_ROOMID'));
|
||||
$msg = empty(getenv('DANMU_CONTENT')) ? self::getMsgInfo() : getenv('DANMU_CONTENT');
|
||||
|
||||
$info = [
|
||||
'roomid' => $room_id,
|
||||
'content' => $msg,
|
||||
];
|
||||
|
||||
if (self::privateSendMsg($info)) {
|
||||
self::$lock = time() + 3600;
|
||||
return;
|
||||
}
|
||||
|
||||
self::$lock = time() + 30;
|
||||
}
|
||||
|
||||
// 获取随机弹幕
|
||||
private static function getMsgInfo()
|
||||
{
|
||||
/*
|
||||
* 整理一部分API,收集于网络,侵权麻烦联系我删除.
|
||||
* 如果设置项不能用可以选择,只保证代码发布时正常.
|
||||
* 格式全部为TEXT,请自己测试好了再放进配置文件.
|
||||
* -7. https://api.lwl12.com/hitokoto/v1?encode=realjso
|
||||
* -6. https://api.ly522.com/yan.php?format=text
|
||||
* -5. https://v1.hitokoto.cn/?encode=text
|
||||
* -4. https://api.jysafe.cn/yy/
|
||||
* -3. https://m.mom1.cn/api/
|
||||
* -2. https://api.ooopn.com/yan/api.php?type=text
|
||||
* -1. https://api.imjad.cn/hitokoto/
|
||||
* 0. https://www.ly522.com/hitokoto/
|
||||
* 1. https://www.tddiao.online/word/
|
||||
* 2. https://api.guoch.xyz/
|
||||
* 3. http://www.ooomg.cn/dutang
|
||||
* 4. https://api.gushi.ci/rensheng.txt
|
||||
* 5. https://api.itswincer.com/hitokoto/v2/
|
||||
* 6. http://api.imiliy.cn/get/
|
||||
* 7. http://api.dsecret.com/yiyan/
|
||||
* 8. https://api.xygeng.cn/dailywd/api/api.php
|
||||
*/
|
||||
try {
|
||||
$url = getenv('CONTENT_FETCH');
|
||||
if (empty($url)) {
|
||||
exit('活跃弹幕配置错误,请检查相应配置');
|
||||
}
|
||||
$data = Curl::get($url);
|
||||
$punctuations = [',', ',', '。', '!', '.', ';', '——'];
|
||||
foreach ($punctuations as $punctuation) {
|
||||
if (strpos($data, $punctuation)) {
|
||||
$data = explode($punctuation, $data)[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
//转换信息
|
||||
private static function convertInfo()
|
||||
{
|
||||
preg_match('/bili_jct=(.{32})/', getenv('COOKIE'), $token);
|
||||
$token = isset($token[1]) ? $token[1] : '';
|
||||
return $token;
|
||||
}
|
||||
|
||||
//发送弹幕通用模块
|
||||
private static function sendMsg($info)
|
||||
{
|
||||
$raw = Curl::get('https://api.live.bilibili.com/room/v1/Room/room_init?id=' . $info['roomid']);
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
$payload = [
|
||||
'color' => '16777215',
|
||||
'fontsize' => 25,
|
||||
'mode' => 1,
|
||||
'msg' => $info['content'],
|
||||
'rnd' => 0,
|
||||
'roomid' => $de_raw['data']['room_id'],
|
||||
'csrf' => self::convertInfo(),
|
||||
'csrf_token' => self::convertInfo(),
|
||||
];
|
||||
|
||||
return Curl::post('https://api.live.bilibili.com/msg/send', Sign::api($payload));
|
||||
}
|
||||
|
||||
//使用发送弹幕模块
|
||||
private static function privateSendMsg($info)
|
||||
{
|
||||
//TODO 暂时性功能 有需求就修改
|
||||
$raw = self::sendMsg($info);
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] == 1001) {
|
||||
Log::warning($de_raw['msg']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$de_raw['code']) {
|
||||
Log::info('弹幕发送成功!');
|
||||
return true;
|
||||
}
|
||||
|
||||
Log::error("弹幕发送失败, {$de_raw['msg']}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
49
src/DataTreating.php
Normal file
49
src/DataTreating.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class DataTreating
|
||||
{
|
||||
/**
|
||||
* @desc 抽奖分发
|
||||
* @param array $data
|
||||
*/
|
||||
public static function distribute(array $data)
|
||||
{
|
||||
// var_dump($data);
|
||||
// room_id raffle_id raffle_title raffle_type
|
||||
try {
|
||||
$info = ['rid' => $data['room_id'], 'lid' => $data['raffle_id']];
|
||||
} catch (\Exception $e) {
|
||||
return;
|
||||
}
|
||||
switch ($data['raffle_type']) {
|
||||
case "storm":
|
||||
// 风暴
|
||||
Storm::pushToQueue($info);
|
||||
break;
|
||||
case "raffle":
|
||||
// 礼物
|
||||
RaffleHandler::pushToQueue($info);
|
||||
break;
|
||||
case "guard":
|
||||
// 舰长
|
||||
Guard::pushToQueue($info);
|
||||
break;
|
||||
case "small_tv":
|
||||
// 小电视
|
||||
RaffleHandler::pushToQueue($info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/File.php
Normal file
31
src/File.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class File
|
||||
{
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
}
|
||||
|
||||
// PUT CONF
|
||||
public static function writeNewEnvironmentFileWith($key, $value)
|
||||
{
|
||||
file_put_contents(__DIR__ . '/../conf/' . Index::$conf_file, preg_replace(
|
||||
'/^' . $key . '=' . getenv($key) . '/m',
|
||||
$key . '=' . $value,
|
||||
file_get_contents(__DIR__ . '/../conf/' . Index::$conf_file)
|
||||
));
|
||||
// 写入系统变量
|
||||
putenv($key . '=' . $value);
|
||||
}
|
||||
}
|
||||
66
src/GiftHeart.php
Normal file
66
src/GiftHeart.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class GiftHeart
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
if (self::giftheart()) {
|
||||
self::$lock = time() + 60 * 60;
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 5 * 60;
|
||||
}
|
||||
|
||||
// GIFT HEART
|
||||
protected static function giftheart(): bool
|
||||
{
|
||||
$payload = [
|
||||
'roomid' => getenv('ROOM_ID'),
|
||||
];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/gift/v2/live/heart_gift_receive', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] == -403) {
|
||||
Log::info($de_raw['msg']);
|
||||
$payload = [
|
||||
'ruid' => 17561885,
|
||||
];
|
||||
Curl::get('https://api.live.bilibili.com/eventRoom/index', Sign::api($payload));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($de_raw['code'] != 0) {
|
||||
Log::warning($de_raw['msg']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($de_raw['data']['heart_status'] == 0) {
|
||||
Log::info('没有礼物可以领了呢!');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($de_raw['data']['gift_list'])) {
|
||||
foreach ($de_raw['data']['gift_list'] as $vo) {
|
||||
Log::info("{$de_raw['msg']},礼物 {$vo['gift_name']} ({$vo['day_num']}/{$vo['day_limit']})");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
109
src/GiftSend.php
Normal file
109
src/GiftSend.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class GiftSend
|
||||
{
|
||||
public static $lock = 0;
|
||||
protected static $uid = 0;
|
||||
protected static $ruid = 0;
|
||||
protected static $roomid = 0;
|
||||
|
||||
protected static function getRoomInfo()
|
||||
{
|
||||
Log::info('正在生成直播间信息...');
|
||||
|
||||
$payload = [];
|
||||
$data = Curl::get('https://account.bilibili.com/api/myinfo/v2', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('获取帐号信息失败!', ['msg' => $data['message']]);
|
||||
Log::warning('清空礼物功能禁用!');
|
||||
self::$lock = time() + 100000000;
|
||||
return;
|
||||
}
|
||||
|
||||
self::$uid = $data['mid'];
|
||||
|
||||
$payload = [
|
||||
'id' => getenv('ROOM_ID'),
|
||||
];
|
||||
$data = Curl::get('https://api.live.bilibili.com/room/v1/Room/get_info', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('获取主播房间号失败!', ['msg' => $data['message']]);
|
||||
Log::warning('清空礼物功能禁用!');
|
||||
self::$lock = time() + 100000000;
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info('直播间信息生成完毕!');
|
||||
|
||||
self::$ruid = $data['data']['uid'];
|
||||
self::$roomid = $data['data']['room_id'];
|
||||
}
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (empty(self::$ruid)) {
|
||||
self::getRoomInfo();
|
||||
}
|
||||
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = [];
|
||||
$data = Curl::get('https://api.live.bilibili.com/gift/v2/gift/bag_list', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('背包查看失败!', ['msg' => $data['message']]);
|
||||
}
|
||||
|
||||
if (isset($data['data']['list'])) {
|
||||
foreach ($data['data']['list'] as $vo) {
|
||||
if ($vo['expire_at'] >= $data['data']['time'] && $vo['expire_at'] <= $data['data']['time'] + 3600) {
|
||||
self::send($vo);
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$lock = time() + 600;
|
||||
}
|
||||
|
||||
protected static function send($value)
|
||||
{
|
||||
$payload = [
|
||||
'coin_type' => 'silver',
|
||||
'gift_id' => $value['gift_id'],
|
||||
'ruid' => self::$ruid,
|
||||
'uid' => self::$uid,
|
||||
'biz_id' => self::$roomid,
|
||||
'gift_num' => $value['gift_num'],
|
||||
'data_source_id' => '',
|
||||
'data_behavior_id' => '',
|
||||
'bag_id' => $value['bag_id']
|
||||
];
|
||||
|
||||
$data = Curl::post('https://api.live.bilibili.com/gift/v2/live/bag_send', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('送礼失败!', ['msg' => $data['message']]);
|
||||
} else {
|
||||
Log::notice("成功向 {$payload['biz_id']} 投喂了 {$value['gift_num']} 个{$value['gift_name']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/GroupSignIn.php
Normal file
74
src/GroupSignIn.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class GroupSignIn
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$groups = self::getGroupList();
|
||||
if (empty($groups)) {
|
||||
self::$lock = time() + 24 * 60 * 60;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
self::signInGroup($group);
|
||||
}
|
||||
|
||||
self::$lock = time() + 8 * 60 * 60;
|
||||
}
|
||||
|
||||
//GROUP LIST
|
||||
protected static function getGroupList(): array
|
||||
{
|
||||
$payload = [];
|
||||
$raw = Curl::get('https://api.vc.bilibili.com/link_group/v1/member/my_groups', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if (empty($de_raw['data']['list'])) {
|
||||
Log::notice('你没有需要签到的应援团!');
|
||||
return [];
|
||||
}
|
||||
return $de_raw['data']['list'];
|
||||
}
|
||||
|
||||
//SIGN IN
|
||||
protected static function signInGroup(array $groupInfo): bool
|
||||
{
|
||||
$payload = [
|
||||
'group_id' => $groupInfo['group_id'],
|
||||
'owner_id' => $groupInfo['owner_uid'],
|
||||
];
|
||||
$raw = Curl::get('https://api.vc.bilibili.com/link_setting/v1/link_setting/sign_in', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] != '0') {
|
||||
Log::warning('在应援团{' . $groupInfo['group_name'] . '}中签到失败,原因待查');
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['data']['status'] == '0') {
|
||||
Log::info('在应援团{' . $groupInfo['group_name'] . '}中签到成功,增加{' . $de_raw['data']['add_num'] . '点}亲密度');
|
||||
} else {
|
||||
Log::notice('在应援团{' . $groupInfo['group_name'] . '}中不要重复签到');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
126
src/Guard.php
Normal file
126
src/Guard.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Guard
|
||||
{
|
||||
const KEY = '总督舰长';
|
||||
const SWITCH = 'USE_GUARD';
|
||||
|
||||
public static $lock = 0;
|
||||
|
||||
private static $wait_list = [];
|
||||
private static $finsh_list = [];
|
||||
private static $all_list = [];
|
||||
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::startLottery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽奖逻辑
|
||||
* @return bool
|
||||
*/
|
||||
protected static function startLottery(): bool
|
||||
{
|
||||
$max_num = 3;
|
||||
while ($max_num) {
|
||||
$guard = array_shift(self::$wait_list);
|
||||
if (is_null($guard)) {
|
||||
break;
|
||||
}
|
||||
$guard_lid = $guard['lid'];
|
||||
$guard_rid = $guard['rid'];
|
||||
Live::goToRoom($guard_rid);
|
||||
Statistics::addJoinList(self::KEY);
|
||||
$data = self::lottery($guard_rid, $guard_lid);
|
||||
if ($data['code'] == 0) {
|
||||
Statistics::addSuccessList(self::KEY);
|
||||
Log::notice("房间 {$guard_rid} 编号 {$guard_lid} " . self::KEY . ": {$data['data']['message']}");
|
||||
} elseif ($data['code'] == 400 && $data['msg'] == '你已经领取过啦') {
|
||||
Log::info("房间 {$guard_rid} 编号 {$guard_lid} " . self::KEY . ": {$data['msg']}");
|
||||
} else {
|
||||
Log::warning("房间 {$guard_rid} 编号 {$guard_lid} " . self::KEY . ": {$data['msg']}");
|
||||
}
|
||||
$max_num--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求抽奖
|
||||
* @param $rid
|
||||
* @param $lid
|
||||
* @return array
|
||||
*/
|
||||
private static function lottery($rid, $lid): array
|
||||
{
|
||||
$user_info = User::parseCookies();
|
||||
$url = "https://api.live.bilibili.com/lottery/v2/lottery/join";
|
||||
$payload = [
|
||||
"roomid" => $rid,
|
||||
"id" => $lid,
|
||||
"type" => "guard",
|
||||
"csrf_token" => $user_info['token']
|
||||
];
|
||||
$raw = Curl::post($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
return $de_raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重复检测
|
||||
* @param int $lid
|
||||
* @return bool
|
||||
*/
|
||||
private static function toRepeatLid(int $lid): bool
|
||||
{
|
||||
if (in_array($lid, self::$all_list)) {
|
||||
return true;
|
||||
}
|
||||
if (count(self::$all_list) > 2000) {
|
||||
self::$all_list = [];
|
||||
}
|
||||
array_push(self::$all_list, $lid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据推入队列
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public static function pushToQueue(array $data): bool
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return false;
|
||||
}
|
||||
if (self::toRepeatLid($data['lid'])) {
|
||||
return false;
|
||||
}
|
||||
Statistics::addPushList(self::KEY);
|
||||
self::$wait_list = array_merge(self::$wait_list, [['rid' => $data['rid'], 'lid' => $data['lid']]]);
|
||||
$wait_num = count(self::$wait_list);
|
||||
if ($wait_num > 2) {
|
||||
Log::info("当前队列中共有 {$wait_num} 个" . self::KEY . "待抽奖");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
58
src/Heart.php
Normal file
58
src/Heart.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Heart
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::pc();
|
||||
self::mobile();
|
||||
|
||||
self::$lock = time() + 300;
|
||||
}
|
||||
|
||||
protected static function pc()
|
||||
{
|
||||
$payload = [
|
||||
'room_id' => getenv('ROOM_ID'),
|
||||
];
|
||||
$data = Curl::post('https://api.live.bilibili.com/User/userOnlineHeart', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('WEB端 直播间心跳停止惹~', ['msg' => $data['message']]);
|
||||
} else {
|
||||
Log::info('WEB端 发送心跳正常!');
|
||||
}
|
||||
}
|
||||
|
||||
protected static function mobile()
|
||||
{
|
||||
$payload = [
|
||||
'room_id' => getenv('ROOM_ID'),
|
||||
];
|
||||
$data = Curl::post('https://api.live.bilibili.com/mobile/userOnlineHeart', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('APP端 直播间心跳停止惹~', ['msg' => $data['message']]);
|
||||
} else {
|
||||
Log::info('APP端 发送心跳正常!');
|
||||
}
|
||||
}
|
||||
}
|
||||
158
src/Live.php
Normal file
158
src/Live.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Live
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
// TODO
|
||||
self::isSleep();
|
||||
}
|
||||
|
||||
// GET RANDOW ROOM_ID
|
||||
public static function getUserRecommend()
|
||||
{
|
||||
while (1) {
|
||||
$raw = Curl::get('https://api.live.bilibili.com/area/liveList?area=all&order=online&page=' . random_int(0, 5));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] != '0') {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$rand_num = random_int(1, 29);
|
||||
|
||||
return $de_raw['data'][$rand_num]['roomid'];
|
||||
}
|
||||
|
||||
// GET REALROOM_ID
|
||||
public static function getRealRoomID($room_id)
|
||||
{
|
||||
$raw = Curl::get('https://api.live.bilibili.com/room/v1/Room/room_init?id=' . $room_id);
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code']) {
|
||||
Log::warning($room_id . ' : ' . $de_raw['msg']);
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['data']['is_hidden']) {
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['data']['is_locked']) {
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['data']['encrypted']) {
|
||||
return false;
|
||||
}
|
||||
return $de_raw['data']['room_id'];
|
||||
|
||||
}
|
||||
|
||||
// 钓鱼检测
|
||||
public static function fishingDetection($room_id): bool
|
||||
{
|
||||
if (self::getRealRoomID($room_id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// RANDOM DELAY
|
||||
public static function randFloat($min = 0, $max = 3): bool
|
||||
{
|
||||
$rand = $min + mt_rand() / mt_getrandmax() * ($max - $min);
|
||||
sleep($rand);
|
||||
return true;
|
||||
}
|
||||
|
||||
//TO ROOM
|
||||
public static function goToRoom($room_id): bool
|
||||
{
|
||||
$payload = [
|
||||
'room_id' => $room_id,
|
||||
];
|
||||
Curl::post('https://api.live.bilibili.com/room/v1/Room/room_entry_action', Sign::api($payload));
|
||||
// Log::info('进入直播间[' . $room_id . ']抽奖!');
|
||||
return true;
|
||||
}
|
||||
|
||||
// get Millisecond
|
||||
public static function getMillisecond()
|
||||
{
|
||||
list($t1, $t2) = explode(' ', microtime());
|
||||
return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
|
||||
}
|
||||
|
||||
// IS SLEEP
|
||||
public static function isSleep()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 5 * 60;
|
||||
|
||||
$hour = date('H');
|
||||
if ($hour >= 2 && $hour < 6) {
|
||||
self::bannedVisit('sleep');
|
||||
Log::warning('休眠时间,暂停非必要任务,4小时后自动开启!');
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = [];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/mobile/freeSilverAward', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['msg'] == '访问被拒绝') {
|
||||
self::bannedVisit('ban');
|
||||
Log::warning('账号拒绝访问,暂停非必要任务,凌晨自动开启!');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//被封禁访问
|
||||
public static function bannedVisit($arg)
|
||||
{
|
||||
//获取当前时间
|
||||
$block_time = strtotime(date("Y-m-d H:i:s"));
|
||||
|
||||
if ($arg == 'ban') {
|
||||
$unblock_time = strtotime(date("Y-m-d", strtotime("+1 day", $block_time)));
|
||||
} elseif ($arg == 'sleep') {
|
||||
// TODO
|
||||
$unblock_time = $block_time + 4 * 60 * 60;
|
||||
} else {
|
||||
$unblock_time = time();
|
||||
}
|
||||
|
||||
$second = time() + ceil($unblock_time - $block_time) + 5 * 60;
|
||||
$hour = floor(($second - time()) / 60 / 60);
|
||||
|
||||
if ($arg == 'ban') {
|
||||
// 推送被ban信息
|
||||
Notice::run('banned', $hour);
|
||||
}
|
||||
|
||||
self::$lock = $second;
|
||||
|
||||
Silver::$lock = $second;
|
||||
MaterialObject::$lock = $second;
|
||||
Websocket::$lock = $second;
|
||||
GiftHeart::$lock = $second;
|
||||
Guard::$lock = $second;
|
||||
RaffleHandler::$lock = $second;
|
||||
RaffleHandler::$rw_lock = $second;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
110
src/Log.php
Normal file
110
src/Log.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Bramus\Monolog\Formatter\ColoredLineFormatter;
|
||||
|
||||
class Log
|
||||
{
|
||||
protected static $instance;
|
||||
|
||||
static public function getLogger()
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::configureInstance();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
protected static function configureInstance()
|
||||
{
|
||||
$logger = new Logger('Bilibili');
|
||||
$handler = new StreamHandler('php://stdout', getenv('APP_DEBUG') == 'true' ? Logger::DEBUG : Logger::INFO);
|
||||
$handler->setFormatter(new ColoredLineFormatter());
|
||||
$logger->pushHandler($handler);
|
||||
self::$instance = $logger;
|
||||
}
|
||||
|
||||
private static function prefix()
|
||||
{
|
||||
if (getenv('APP_MULTIPLE') == 'true') {
|
||||
return '[' . (empty($t = getenv('APP_USER_IDENTITY')) ? getenv('APP_USER') : $t) . ']';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private static function writeLog($type, $message)
|
||||
{
|
||||
if (getenv('APP_WRITELOG') == 'true') {
|
||||
$path = './' . getenv("APP_WRITELOGPATH") . '/';
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
chmod($path, 0777);
|
||||
}
|
||||
$filename = $path . getenv('APP_USER') . ".log";
|
||||
$date = date('[Y-m-d H:i:s] ');
|
||||
$data = $date . ' Log.' . $type . ' ' . $message . PHP_EOL;
|
||||
file_put_contents($filename, $data, FILE_APPEND);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public static function debug($message, array $context = [])
|
||||
{
|
||||
self::writeLog('DEBUG', $message);
|
||||
self::getLogger()->addDebug($message, $context);
|
||||
}
|
||||
|
||||
public static function info($message, array $context = [])
|
||||
{
|
||||
$message = self::prefix() . $message;
|
||||
self::writeLog('INFO', $message);
|
||||
self::getLogger()->addInfo($message, $context);
|
||||
self::callback(Logger::INFO, 'INFO', $message);
|
||||
}
|
||||
|
||||
public static function notice($message, array $context = [])
|
||||
{
|
||||
$message = self::prefix() . $message;
|
||||
self::writeLog('NOTICE', $message);
|
||||
self::getLogger()->addNotice($message, $context);
|
||||
self::callback(Logger::NOTICE, 'NOTICE', $message);
|
||||
}
|
||||
|
||||
public static function warning($message, array $context = [])
|
||||
{
|
||||
$message = self::prefix() . $message;
|
||||
self::writeLog('WARNING', $message);
|
||||
self::getLogger()->addWarning($message, $context);
|
||||
self::callback(Logger::WARNING, 'WARNING', $message);
|
||||
}
|
||||
|
||||
public static function error($message, array $context = [])
|
||||
{
|
||||
$message = self::prefix() . $message;
|
||||
self::writeLog('ERROR', $message);
|
||||
self::getLogger()->addError($message, $context);
|
||||
self::callback(Logger::ERROR, 'ERROR', $message);
|
||||
}
|
||||
|
||||
public static function callback($levelId, $level, $message)
|
||||
{
|
||||
$callback_level = (('APP_CALLBACK_LEVEL') == '') ? (Logger::ERROR) : intval(getenv('APP_CALLBACK_LEVEL'));
|
||||
if ($levelId >= $callback_level) {
|
||||
$url = str_replace('{account}', self::prefix(), getenv('APP_CALLBACK'));
|
||||
$url = str_replace('{level}', $level, $url);
|
||||
$url = str_replace('{message}', urlencode($message), $url);
|
||||
Curl::get($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
204
src/Login.php
Normal file
204
src/Login.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Login
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
Log::info('开始启动程序...');
|
||||
if (getenv('ACCESS_TOKEN') == "") {
|
||||
Log::info('令牌载入中...');
|
||||
self::login();
|
||||
}
|
||||
// 重载配置文件
|
||||
Index::$dotenv->overload();
|
||||
|
||||
Log::info('正在检查令牌合法性...');
|
||||
if (!self::info()) {
|
||||
Log::warning('令牌即将过期');
|
||||
Log::info('申请更换令牌中...');
|
||||
if (!self::refresh()) {
|
||||
Log::warning('无效令牌,正在重新申请...');
|
||||
self::login();
|
||||
}
|
||||
}
|
||||
self::$lock = time() + 3600;
|
||||
}
|
||||
|
||||
public static function check()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return true;
|
||||
}
|
||||
self::$lock = time() + 7200;
|
||||
if (!self::info()) {
|
||||
Log::warning('令牌即将过期');
|
||||
Log::info('申请更换令牌中...');
|
||||
if (!self::refresh()) {
|
||||
Log::warning('无效令牌,正在重新申请...');
|
||||
self::login();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function info()
|
||||
{
|
||||
$access_token = getenv('ACCESS_TOKEN');
|
||||
$payload = [
|
||||
'access_token' => $access_token,
|
||||
];
|
||||
$data = Curl::get('https://passport.bilibili.com/api/v2/oauth2/info', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::error('检查令牌失败', ['msg' => $data['message']]);
|
||||
return false;
|
||||
}
|
||||
Log::info('令牌有效期: ' . date('Y-m-d H:i:s', $data['ts'] + $data['data']['expires_in']));
|
||||
return $data['data']['expires_in'] > 14400;
|
||||
}
|
||||
|
||||
public static function refresh()
|
||||
{
|
||||
$access_token = getenv('ACCESS_TOKEN');
|
||||
$refresh_token = getenv('REFRESH_TOKEN');
|
||||
$payload = [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
];
|
||||
$data = Curl::post('https://passport.bilibili.com/api/oauth2/refreshToken', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::error('重新生成令牌失败', ['msg' => $data['message']]);
|
||||
return false;
|
||||
}
|
||||
Log::info('令牌生成完毕!');
|
||||
$access_token = $data['data']['access_token'];
|
||||
File::writeNewEnvironmentFileWith('ACCESS_TOKEN', $access_token);
|
||||
Log::info(' > access token: ' . $access_token);
|
||||
$refresh_token = $data['data']['refresh_token'];
|
||||
File::writeNewEnvironmentFileWith('REFRESH_TOKEN', $refresh_token);
|
||||
Log::info(' > refresh token: ' . $refresh_token);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function login($captcha = '', $headers = [])
|
||||
{
|
||||
$user = getenv('APP_USER');
|
||||
$pass = getenv('APP_PASS');
|
||||
if (empty($user) || empty($pass)) {
|
||||
Log::error('空白的帐号和口令!');
|
||||
die();
|
||||
}
|
||||
|
||||
// get PublicKey
|
||||
Log::info('正在载入安全模块...');
|
||||
$payload = [];
|
||||
$data = Curl::post('https://passport.bilibili.com/api/oauth2/getKey', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::error('公钥获取失败', ['msg' => $data['message']]);
|
||||
die();
|
||||
} else {
|
||||
Log::info('安全模块载入完毕!');
|
||||
}
|
||||
$public_key = $data['data']['key'];
|
||||
$hash = $data['data']['hash'];
|
||||
openssl_public_encrypt($hash . $pass, $crypt, $public_key);
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
// login
|
||||
Log::info('正在获取令牌...');
|
||||
$payload = [
|
||||
'subid' => 1,
|
||||
'permission' => 'ALL',
|
||||
'username' => $user,
|
||||
'password' => base64_encode($crypt),
|
||||
'captcha' => $captcha,
|
||||
];
|
||||
$data = Curl::post('https://passport.bilibili.com/api/v2/oauth2/login', Sign::api($payload), $headers);
|
||||
$data = json_decode($data, true);
|
||||
if (isset($data['code']) && $data['code'] == -105) {
|
||||
$captcha_data = static::loginWithCaptcha();
|
||||
$captcha = $captcha_data['captcha'];
|
||||
$headers = $captcha_data['headers'];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::error('登录失败', ['msg' => $data['message']]);
|
||||
die();
|
||||
}
|
||||
self::saveCookie($data);
|
||||
Log::info('令牌获取成功!');
|
||||
$access_token = $data['data']['token_info']['access_token'];
|
||||
File::writeNewEnvironmentFileWith('ACCESS_TOKEN', $access_token);
|
||||
Log::info(' > access token: ' . $access_token);
|
||||
$refresh_token = $data['data']['token_info']['refresh_token'];
|
||||
File::writeNewEnvironmentFileWith('REFRESH_TOKEN', $refresh_token);
|
||||
Log::info(' > refresh token: ' . $refresh_token);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
protected static function loginWithCaptcha()
|
||||
{
|
||||
Log::info('登陆需要验证 ,启动验证码登陆!');
|
||||
$headers = [
|
||||
'Accept' => 'application/json, text/plain, */*',
|
||||
'User-Agent' => 'bili-universal/8230 CFNetwork/975.0.3 Darwin/18.2.0',
|
||||
'Host' => 'passport.bilibili.com',
|
||||
'Cookie' => 'sid=blhelper'
|
||||
];
|
||||
$data = Curl::other('https://passport.bilibili.com/captcha', null, $headers);
|
||||
$data = base64_encode($data);
|
||||
$captcha = static::ocrCaptcha($data);
|
||||
return [
|
||||
'captcha' => $captcha,
|
||||
'headers' => $headers,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private static function ocrCaptcha($captcha_img)
|
||||
{
|
||||
$payload = [
|
||||
'image' => (string)$captcha_img
|
||||
];
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
$data = Curl::other('http://captcha.biem.club:19951/', json_encode($payload), $headers);
|
||||
$de_raw = json_decode($data, true);
|
||||
Log::info("验证码识别结果 {$de_raw['message']}");
|
||||
|
||||
return $de_raw['message'];
|
||||
}
|
||||
|
||||
private static function saveCookie($data)
|
||||
{
|
||||
Log::info('COOKIE获取成功!');
|
||||
//临时保存cookie
|
||||
$temp = '';
|
||||
$cookies = $data['data']['cookie_info']['cookies'];
|
||||
foreach ($cookies as $cookie) {
|
||||
$temp .= $cookie['name'] . '=' . $cookie['value'] . ';';
|
||||
}
|
||||
File::writeNewEnvironmentFileWith('COOKIE', $temp);
|
||||
Log::info(' > auth cookie: ' . $temp);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
289
src/MasterSite.php
Normal file
289
src/MasterSite.php
Normal file
@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class MasterSite
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time() || getenv('USE_MASTER_SITE') == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::watchAid() && self::shareAid() && self::coinAdd()) {
|
||||
self::$lock = time() + 24 * 60 * 60;
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 3600;
|
||||
}
|
||||
|
||||
// 投币
|
||||
private static function reward($aid): bool
|
||||
{
|
||||
$user_info = User::parseCookies();
|
||||
$url = "https://api.bilibili.com/x/web-interface/coin/add";
|
||||
$payload = [
|
||||
"aid" => $aid,
|
||||
"multiply" => "1",
|
||||
"cross_domain" => "true",
|
||||
"csrf" => $user_info['token']
|
||||
];
|
||||
$headers = [
|
||||
'Host' => "api.bilibili.com",
|
||||
'Origin' => "https://www.bilibili.com",
|
||||
'Referer' => "https://www.bilibili.com/video/av{$aid}",
|
||||
'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
];
|
||||
$raw = Curl::post($url, Sign::api($payload), $headers);
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] == 0) {
|
||||
Log::notice("主站任务: av{$aid}投币成功!");
|
||||
return true;
|
||||
} else {
|
||||
Log::warning("主站任务: av{$aid}投币失败!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 投币日志
|
||||
protected static function coinLog(): int
|
||||
{
|
||||
$url = "https://api.bilibili.com/x/member/web/coin/log";
|
||||
$payload = [];
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
$logs = $de_raw['data']['list'];
|
||||
$coins = 0;
|
||||
foreach ($logs as $log) {
|
||||
$log_ux = strtotime($log['time']);
|
||||
$log_date = date('Y-m-d', $log_ux);
|
||||
$now_date = date('Y-m-d');
|
||||
if ($log_date != $now_date) {
|
||||
break;
|
||||
}
|
||||
if (strpos($log['reason'], "打赏") !== false) {
|
||||
switch ($log['delta']) {
|
||||
case -1:
|
||||
$coins += 1;
|
||||
break;
|
||||
case -2:
|
||||
$coins += 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $coins;
|
||||
}
|
||||
|
||||
// 投币操作
|
||||
protected static function coinAdd(): bool
|
||||
{
|
||||
switch (getenv('USE_ADD_COIN')) {
|
||||
case 'false':
|
||||
break;
|
||||
case 'true':
|
||||
$av_num = getenv('ADD_COIN_AV_NUM');
|
||||
$av_num = (int)$av_num;
|
||||
if ($av_num == 0) {
|
||||
Log::warning('当前视频投币设置不正确,请检查配置文件!');
|
||||
die();
|
||||
}
|
||||
if ($av_num == 1) {
|
||||
$aid = !empty(getenv('ADD_COIN_AV')) ? getenv('ADD_COIN_AV') : self::getRandomAid();
|
||||
self::reward($aid);
|
||||
} else {
|
||||
$coins = $av_num - self::coinLog();
|
||||
if ($coins <= 0) {
|
||||
Log::info('今日投币上限已满!');
|
||||
break;
|
||||
}
|
||||
$aids = self::getDayRankingAids($av_num);
|
||||
foreach ($aids as $aid) {
|
||||
self::reward($aid);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log::warning('当前视频投币设置不正确,请检查配置文件!');
|
||||
die();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取随机AID
|
||||
private static function getRandomAid(): string
|
||||
{
|
||||
$page = random_int(1, 100000);
|
||||
$payload = [];
|
||||
$url = "https://api.bilibili.com/x/web-interface/newlist?&pn={$page}&ps=1";
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
$aid = $de_raw['data']['archives'][0]['aid'];
|
||||
|
||||
if (is_null($aid) || empty($aid) || $aid == '') {
|
||||
$aid = random_int(10000000, 30000000);
|
||||
}
|
||||
return (string)$aid;
|
||||
}
|
||||
|
||||
// 日榜AID
|
||||
private static function getDayRankingAids($num): array
|
||||
{
|
||||
// day: 日榜1 三榜3 周榜7 月榜30
|
||||
$payload = [];
|
||||
$aids = [];
|
||||
$rand_nums = [];
|
||||
$url = "https://api.bilibili.com/x/web-interface/ranking?rid=0&day=1&type=1&arc_type=0";
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
while (true) {
|
||||
$rand_num = random_int(1, 100);
|
||||
if (in_array($rand_num, $rand_nums)) {
|
||||
continue;
|
||||
} else {
|
||||
array_push($rand_nums, $rand_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$aid = $de_raw['data']['list'][$rand_nums[$i]]['aid'];
|
||||
array_push($aids, $aid);
|
||||
}
|
||||
|
||||
return $aids;
|
||||
}
|
||||
|
||||
// 分享视频
|
||||
private static function shareAid(): bool
|
||||
{
|
||||
# aid = 稿件av号
|
||||
$url = "https://api.bilibili.com/x/web-interface/share/add";
|
||||
$av_info = self::parseAid();
|
||||
$user_info = User::parseCookies();
|
||||
$payload = [
|
||||
'aid' => $av_info['aid'],
|
||||
'jsonp' => "jsonp",
|
||||
'csrf' => $user_info['token'],
|
||||
];
|
||||
$headers = [
|
||||
'Host' => "api.bilibili.com",
|
||||
'Origin' => "https://www.bilibili.com",
|
||||
'Referer' => "https://www.bilibili.com/video/av{$av_info['aid']}",
|
||||
'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
];
|
||||
$raw = Curl::post($url, Sign::api($payload), $headers);
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] == 0) {
|
||||
Log::notice("主站任务: av{$av_info['aid']}分享成功!");
|
||||
return true;
|
||||
} else {
|
||||
Log::warning("主站任务: av{$av_info['aid']}分享失败!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 观看视频
|
||||
private static function watchAid(): bool
|
||||
{
|
||||
$url = "https://api.bilibili.com/x/report/click/h5";
|
||||
$av_info = self::parseAid();
|
||||
$user_info = User::parseCookies();
|
||||
$payload = [
|
||||
'aid' => $av_info['aid'],
|
||||
'cid' => $av_info['cid'],
|
||||
'part' => 1,
|
||||
'did' => $user_info['sid'],
|
||||
'ftime' => time(),
|
||||
'jsonp' => "jsonp",
|
||||
'lv' => "",
|
||||
'mid' => $user_info['uid'],
|
||||
'csrf' => $user_info['token'],
|
||||
'stime' => time()
|
||||
];
|
||||
|
||||
$headers = [
|
||||
'Host' => "api.bilibili.com",
|
||||
'Origin' => "https://www.bilibili.com",
|
||||
'Referer' => "https://www.bilibili.com/video/av{$av_info['aid']}",
|
||||
'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
|
||||
];
|
||||
|
||||
$raw = Curl::post($url, Sign::api($payload), $headers);
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] == 0) {
|
||||
$url = "https://api.bilibili.com/x/report/web/heartbeat";
|
||||
$payload = [
|
||||
"aid" => $av_info['aid'],
|
||||
"cid" => $av_info['cid'],
|
||||
"mid" => $user_info['uid'],
|
||||
"csrf" => $user_info['token'],
|
||||
"jsonp" => "jsonp",
|
||||
"played_time" => "0",
|
||||
"realtime" => $av_info['duration'],
|
||||
"pause" => false,
|
||||
"dt" => "7",
|
||||
"play_type" => "1",
|
||||
'start_ts' => time()
|
||||
];
|
||||
$raw = Curl::post($url, Sign::api($payload), $headers);
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] == 0) {
|
||||
sleep(5);
|
||||
$payload['played_time'] = $av_info['duration'] - 1;
|
||||
$payload['play_type'] = 0;
|
||||
$payload['start_ts'] = time();
|
||||
$raw = Curl::post($url, Sign::api($payload), $headers);
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] == 0) {
|
||||
Log::notice("主站任务: av{$av_info['aid']}观看成功!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::warning("主站任务: av{$av_info['aid']}观看失败!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解析AID到CID
|
||||
private static function parseAid(): array
|
||||
{
|
||||
while (true) {
|
||||
$aid = self::getRandomAid();
|
||||
$url = "https://api.bilibili.com/x/web-interface/view?aid={$aid}";
|
||||
$raw = Curl::get($url);
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] != 0) {
|
||||
continue;
|
||||
} else {
|
||||
if (!array_key_exists('cid', $de_raw['data'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$cid = $de_raw['data']['cid'];
|
||||
$duration = $de_raw['data']['duration'];
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'aid' => $aid,
|
||||
'cid' => $cid,
|
||||
'duration' => $duration
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
182
src/MaterialObject.php
Normal file
182
src/MaterialObject.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class MaterialObject
|
||||
{
|
||||
// 时间锁
|
||||
public static $lock = 0;
|
||||
// 丢弃列表
|
||||
private static $discard_aid_list = [];
|
||||
// 起始 和 结束
|
||||
private static $start_aid = 0;
|
||||
private static $end_aid = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
if (getenv('USE_MO') == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
// 计算AID TODO 待优化
|
||||
self::calculateAid(150, 550);
|
||||
self::drawLottery();
|
||||
|
||||
self::$lock = time() + random_int(5, 10) * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 实物抽奖
|
||||
* @return bool
|
||||
*/
|
||||
protected static function drawLottery(): bool
|
||||
{
|
||||
$block_key_list = ['测试', '加密', 'test', 'TEST', '钓鱼', '炸鱼', '调试'];
|
||||
$flag = 5;
|
||||
|
||||
for ($i = self::$start_aid; $i < self::$end_aid; $i++) {
|
||||
if (!$flag) {
|
||||
break;
|
||||
}
|
||||
// 在丢弃列表里 跳过
|
||||
if (in_array($i, self::$discard_aid_list)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'aid' => $i,
|
||||
];
|
||||
$url = 'https://api.live.bilibili.com/lottery/v1/box/getStatus';
|
||||
// 请求 && 解码
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
// -403 没有抽奖
|
||||
if ($de_raw['code'] != '0') {
|
||||
$flag--;
|
||||
continue;
|
||||
}
|
||||
// 如果最后一个结束时间已过 加入丢弃
|
||||
$lotterys = $de_raw['data']['typeB'];
|
||||
$total = count($lotterys);
|
||||
if ($lotterys[$total - 1]['join_end_time'] < time()) {
|
||||
array_push(self::$discard_aid_list, $i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果存在敏感词 加入丢弃
|
||||
$title = $de_raw['data']['title'];
|
||||
foreach ($block_key_list as $block_key) {
|
||||
if (strpos($title, $block_key) !== false) {
|
||||
array_push(self::$discard_aid_list, $i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$num = 1;
|
||||
foreach ($lotterys as $lottery) {
|
||||
$join_end_time = $lottery['join_end_time'];
|
||||
$join_start_time = $lottery['join_start_time'];
|
||||
|
||||
if ($join_end_time > time() && time() > $join_start_time) {
|
||||
switch ($lottery['status']) {
|
||||
case 3:
|
||||
Log::info("实物[{$i}]抽奖: 当前轮次已经结束!");
|
||||
break;
|
||||
case 1:
|
||||
Log::info("实物[{$i}]抽奖: 当前轮次已经抽过了!");
|
||||
break;
|
||||
case -1:
|
||||
Log::info("实物[{$i}]抽奖: 当前轮次暂未开启!");
|
||||
break;
|
||||
case 0:
|
||||
Log::info("实物[{$i}]抽奖: 当前轮次正在抽奖中!");
|
||||
|
||||
$payload = [
|
||||
'aid' => $i,
|
||||
'number' => $num,
|
||||
];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/lottery/v1/box/draw', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if ($de_raw['code'] == 0) {
|
||||
Log::notice("实物[{$i}]抽奖: 成功!");
|
||||
}
|
||||
$num++;
|
||||
break;
|
||||
|
||||
default:
|
||||
Log::info("实物[{$i}]抽奖: 当前轮次状态码[{$lottery['status'] }]未知!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 计算 开始结束的AID
|
||||
* @param $min
|
||||
* @param $max
|
||||
* @return bool
|
||||
*/
|
||||
private static function calculateAid($min, $max): bool
|
||||
{
|
||||
if (self::$end_aid != 0 && self::$start_aid != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
$middle = round(($min + $max) / 2);
|
||||
if (self::aidPost($middle)) {
|
||||
if (self::aidPost($middle + random_int(0, 3))) {
|
||||
$max = $middle;
|
||||
} else {
|
||||
$min = $middle;
|
||||
}
|
||||
} else {
|
||||
$min = $middle;
|
||||
}
|
||||
if ($max - $min == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self::$start_aid = $min - random_int(30, 40);
|
||||
self::$end_aid = $min + random_int(30, 40);
|
||||
Log::info("实物抽奖起始值[" . self::$start_aid . "],结束值[" . self::$end_aid . "]");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use Aid 请求
|
||||
* @param $aid
|
||||
* @return bool
|
||||
*/
|
||||
private static function aidPost($aid): bool
|
||||
{
|
||||
$payload = [
|
||||
'aid' => $aid,
|
||||
];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/lottery/v1/box/getStatus', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
// 等于0是有抽奖返回false
|
||||
if ($de_raw['code'] == 0) {
|
||||
return false;
|
||||
}
|
||||
// 没有抽奖
|
||||
return true;
|
||||
}
|
||||
}
|
||||
123
src/Notice.php
Normal file
123
src/Notice.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Notice
|
||||
{
|
||||
protected static $type = '';
|
||||
protected static $result = '';
|
||||
protected static $uname = '';
|
||||
protected static $sckey = '';
|
||||
|
||||
// RUN
|
||||
public static function run($type, $result = '')
|
||||
{
|
||||
if (getenv('USE_SCKEY') == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$type = $type;
|
||||
self::$result = $result;
|
||||
self::$sckey = getenv('USE_SCKEY');
|
||||
self::$uname = User::userInfo() ? getenv('APP_UNAME') : getenv('APP_USER');
|
||||
|
||||
self::infoSendManager();
|
||||
|
||||
}
|
||||
|
||||
// 信息管理分发
|
||||
private static function infoSendManager(): bool
|
||||
{
|
||||
$nowtime = date('Y-m-d H:i:s');
|
||||
|
||||
switch (self::$type) {
|
||||
case 'raffle':
|
||||
$info = [
|
||||
'title' => '活动抽奖结果',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 在活动抽奖中获得: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'storm':
|
||||
$info = [
|
||||
'title' => '节奏风暴中奖结果',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 在节奏风暴抽奖中: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'active':
|
||||
$info = [
|
||||
'title' => '活动中奖结果',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 在活动抽奖中获得: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'cookieRefresh':
|
||||
$info = [
|
||||
'title' => 'Cookie刷新',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 刷新Cookie: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'loginInit':
|
||||
break;
|
||||
|
||||
case 'todaySign':
|
||||
$info = [
|
||||
'title' => '每日签到',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 签到: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'winIng':
|
||||
$info = [
|
||||
'title' => '实物中奖',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 实物中奖: ' . self::$result,
|
||||
];
|
||||
break;
|
||||
case 'banned':
|
||||
$info = [
|
||||
'title' => '账号封禁',
|
||||
'content' => '[' . $nowtime . ']' . ' 用户: ' . self::$uname . ' 账号被封禁: 程序开始睡眠,凌晨自动唤醒,距离唤醒还有' . self::$result . '小时',
|
||||
];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
self::scSend($info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 发送信息
|
||||
private static function scSend($info)
|
||||
{
|
||||
|
||||
$postdata = http_build_query(
|
||||
[
|
||||
'text' => $info['title'],
|
||||
'desp' => $info['content']
|
||||
]
|
||||
);
|
||||
|
||||
$opts = ['http' =>
|
||||
[
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
]
|
||||
];
|
||||
try {
|
||||
$context = stream_context_create($opts);
|
||||
file_get_contents('https://sc.ftqq.com/' . self::$sckey . '.send', false, $context);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('Server酱推送信息失败,请检查!');
|
||||
}
|
||||
return;
|
||||
//return $result = file_get_contents('https://sc.ftqq.com/' . $this->_sckey . '.send', false, $context);
|
||||
}
|
||||
}
|
||||
219
src/RaffleHandler.php
Normal file
219
src/RaffleHandler.php
Normal file
@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class RaffleHandler
|
||||
{
|
||||
const KEY = '统一活动';
|
||||
const SWITCH = 'USE_ACTIVE';
|
||||
|
||||
public static $lock = 0;
|
||||
public static $rw_lock = 0;
|
||||
|
||||
private static $wait_list = [];
|
||||
private static $finsh_list = [];
|
||||
private static $all_list = [];
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::startLottery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽奖逻辑
|
||||
* @return bool
|
||||
*/
|
||||
protected static function startLottery(): bool
|
||||
{
|
||||
$max_num = mt_rand(5, 10);
|
||||
while ($max_num) {
|
||||
$raffle = array_shift(self::$wait_list);
|
||||
if (is_null($raffle)) {
|
||||
break;
|
||||
}
|
||||
Live::goToRoom($raffle['room_id']);
|
||||
Statistics::addJoinList(self::KEY);
|
||||
self::lottery($raffle);
|
||||
$max_num--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查抽奖列表
|
||||
* @param $rid
|
||||
* @return bool
|
||||
*/
|
||||
private static function checkWeb($rid): bool
|
||||
{
|
||||
$payload = [
|
||||
'roomid' => $rid
|
||||
];
|
||||
$url = 'https://api.live.bilibili.com/gift/v3/smalltv/check';
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
// 计数 && 跳出
|
||||
$total = count($de_raw['data']['list']);
|
||||
if (!$total) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $total; $i++) {
|
||||
/**
|
||||
* raffleId : 88995
|
||||
* title : C位光环抽奖
|
||||
* type : GIFT_30013
|
||||
*/
|
||||
$data = [
|
||||
'raffle_id' => $de_raw['data']['list'][$i]['raffleId'],
|
||||
'title' => $de_raw['data']['list'][$i]['title'],
|
||||
'type' => $de_raw['data']['list'][$i]['type'],
|
||||
'room_id' => $rid
|
||||
];
|
||||
if (self::toRepeatLid($data['raffle_id'])) {
|
||||
continue;
|
||||
}
|
||||
Statistics::addPushList(self::KEY);
|
||||
array_push(self::$wait_list, $data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use WEB中奖查询
|
||||
*/
|
||||
public static function resultWeb()
|
||||
{
|
||||
// 时间锁
|
||||
if (self::$rw_lock > time()) {
|
||||
return;
|
||||
}
|
||||
// 如果待查询为空 && 去重
|
||||
if (!count(self::$finsh_list)) {
|
||||
self::$rw_lock = time() + 40;
|
||||
return;
|
||||
}
|
||||
// 查询,每次查询10个
|
||||
$flag = 0;
|
||||
foreach (self::$finsh_list as $winning_web) {
|
||||
$flag++;
|
||||
if ($flag > 40) {
|
||||
break;
|
||||
}
|
||||
// 参数
|
||||
$payload = [
|
||||
'type' => $winning_web['type'],
|
||||
'raffleId' => $winning_web['raffle_id']
|
||||
];
|
||||
// Web V3 Notice
|
||||
$url = 'https://api.live.bilibili.com/gift/v3/smalltv/notice';
|
||||
// 请求 && 解码
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
// 判断
|
||||
switch ($de_raw['data']['status']) {
|
||||
case 3:
|
||||
break;
|
||||
case 2:
|
||||
Statistics::addSuccessList(self::KEY);
|
||||
// 提示信息
|
||||
$info = "房间 {$winning_web['room_id']} 编号 {$winning_web['raffle_id']} {$winning_web['title']}: 获得";
|
||||
$info .= "{$de_raw['data']['gift_name']}X{$de_raw['data']['gift_num']}";
|
||||
Log::notice($info);
|
||||
// 推送活动抽奖信息
|
||||
if ($de_raw['data']['gift_name'] != '辣条' && $de_raw['data']['gift_name'] != '') {
|
||||
Notice::run('raffle', $info);
|
||||
}
|
||||
// 删除查询完成ID
|
||||
unset(self::$finsh_list[$flag - 1]);
|
||||
self::$finsh_list = array_values(self::$finsh_list);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
self::$rw_lock = time() + 40;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 请求抽奖
|
||||
* @param array $data
|
||||
*/
|
||||
private static function lottery(array $data)
|
||||
{
|
||||
$payload = [
|
||||
'raffleId' => $data['raffle_id'],
|
||||
'roomid' => $data['room_id'],
|
||||
];
|
||||
$url = 'https://api.live.bilibili.com/gift/v3/smalltv/join';
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if (isset($de_raw['code']) && $de_raw['code']) {
|
||||
Log::notice("房间 {$data['raffle_id']} 编号 {$data['room_id']} " . self::KEY . ": {$de_raw['message']}");
|
||||
} else {
|
||||
Log::notice("房间 {$data['raffle_id']} 编号 {$data['room_id']} " . self::KEY . ": {$de_raw['msg']}");
|
||||
array_push(self::$finsh_list, $data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重复检测
|
||||
* @param int $lid
|
||||
* @return bool
|
||||
*/
|
||||
private static function toRepeatLid(int $lid): bool
|
||||
{
|
||||
if (in_array($lid, self::$all_list)) {
|
||||
return true;
|
||||
}
|
||||
if (count(self::$all_list) > 2000) {
|
||||
self::$all_list = [];
|
||||
}
|
||||
array_push(self::$all_list, $lid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据推入队列
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public static function pushToQueue(array $data): bool
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Live::fishingDetection($data['rid'])) {
|
||||
return false;
|
||||
}
|
||||
self::checkWeb($data['rid']);
|
||||
$wait_num = count(self::$wait_list);
|
||||
if ($wait_num > 2) {
|
||||
Log::info("当前队列中共有 {$wait_num} 个" . self::KEY . "待抽奖");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
47
src/Sign.php
Normal file
47
src/Sign.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Sign
|
||||
{
|
||||
public static function api($payload)
|
||||
{
|
||||
# iOS 6680
|
||||
$appkey = '27eb53fc9058f8c3';
|
||||
$appsecret = 'c2ed53a74eeefe3cf99fbd01d8c9c375';
|
||||
# Android
|
||||
// $appkey = '1d8b6e7d45233436';
|
||||
// $appsecret = '560c52ccd288fed045859ed18bffd973';
|
||||
# 云视听 TV
|
||||
// $appkey = '4409e2ce8ffd12b8';
|
||||
// $appsecret = '59b43e04ad6965f34319062b478f83dd';
|
||||
|
||||
$default = [
|
||||
'access_key' => getenv('ACCESS_TOKEN'),
|
||||
'actionKey' => 'appkey',
|
||||
'appkey' => $appkey,
|
||||
'build' => '8230',
|
||||
'device' => 'phone',
|
||||
'mobi_app' => 'iphone',
|
||||
'platform' => 'ios',
|
||||
'ts' => time(),
|
||||
];
|
||||
|
||||
$payload = array_merge($payload, $default);
|
||||
if (isset($payload['sign'])) {
|
||||
unset($payload['sign']);
|
||||
}
|
||||
ksort($payload);
|
||||
$data = http_build_query($payload);
|
||||
$payload['sign'] = md5($data . $appsecret);
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
92
src/Silver.php
Normal file
92
src/Silver.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Silver
|
||||
{
|
||||
public static $lock = 0;
|
||||
protected static $task = [];
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty(self::$task)) {
|
||||
self::pushTask();
|
||||
} else {
|
||||
self::pullTask();
|
||||
}
|
||||
}
|
||||
|
||||
protected static function pushTask()
|
||||
{
|
||||
$payload = [
|
||||
'time_end' => self::$task['time_end'],
|
||||
'time_start' => self::$task['time_start']
|
||||
];
|
||||
$data = Curl::get('https://api.live.bilibili.com/mobile/freeSilverAward', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if ($data['code'] == -800) {
|
||||
self::$lock = time() + 12 * 60 * 60;
|
||||
Log::warning("领取宝箱失败,{$data['message']}!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($data['code'] == -903) {
|
||||
Log::warning("领取宝箱失败,{$data['message']}!");
|
||||
self::$task = [];
|
||||
self::$lock = time() + 60;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning("领取宝箱失败,{$data['message']}!");
|
||||
self::$lock = time() + 60;
|
||||
return;
|
||||
}
|
||||
|
||||
Log::notice("领取宝箱成功,Silver: {$data['data']['silver']}(+{$data['data']['awardSilver']})");
|
||||
|
||||
self::$task = [];
|
||||
self::$lock = time() + 10;
|
||||
}
|
||||
|
||||
protected static function pullTask()
|
||||
{
|
||||
$payload = [];
|
||||
$data = Curl::get('https://api.live.bilibili.com/lottery/v1/SilverBox/getCurrentTask', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code'] == -10017) {
|
||||
Log::notice($data['message']);
|
||||
self::$lock = time() + 24 * 60 * 60;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::error("check freeSilverCurrentTask failed! Error message: {$data['message']}");
|
||||
die();
|
||||
}
|
||||
|
||||
Log::info("获得一个宝箱,内含 {$data['data']['silver']} 个瓜子");
|
||||
Log::info("开启宝箱需等待 {$data['data']['minute']} 分钟");
|
||||
|
||||
self::$task = [
|
||||
'time_start' => $data['data']['time_start'],
|
||||
'time_end' => $data['data']['time_end'],
|
||||
];
|
||||
|
||||
self::$lock = time() + $data['data']['minute'] * 60 + 5;
|
||||
}
|
||||
}
|
||||
66
src/Silver2Coin.php
Normal file
66
src/Silver2Coin.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Silver2Coin
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time() || getenv('USE_SILVER2COIN') == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::appSilver2coin() && self::pcSilver2coin()) {
|
||||
self::$lock = time() + 24 * 60 * 60;
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 3600;
|
||||
}
|
||||
|
||||
|
||||
// APP API
|
||||
protected static function appSilver2coin(): bool
|
||||
{
|
||||
sleep(1);
|
||||
$payload = [];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/AppExchange/silver2coin', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if (!$de_raw['code'] && $de_raw['msg'] == '兑换成功') {
|
||||
Log::info('[APP]银瓜子兑换硬币: ' . $de_raw['msg']);
|
||||
} elseif ($de_raw['code'] == 403) {
|
||||
Log::info('[APP]银瓜子兑换硬币: ' . $de_raw['msg']);
|
||||
} else {
|
||||
Log::warning('[APP]银瓜子兑换硬币: ' . $de_raw['msg']);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PC API
|
||||
protected static function pcSilver2coin(): bool
|
||||
{
|
||||
sleep(1);
|
||||
$payload = [];
|
||||
$url = "https://api.live.bilibili.com/pay/v1/Exchange/silver2coin";
|
||||
$url1 = "https://api.live.bilibili.com/exchange/silver2coin";
|
||||
|
||||
$raw = Curl::get($url, Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] == -403) {
|
||||
return false;
|
||||
}
|
||||
Log::info('[PC]银瓜子兑换硬币: ' . $de_raw['msg']);
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
193
src/Socket.php
Normal file
193
src/Socket.php
Normal file
@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Socket
|
||||
{
|
||||
protected static $socket_connection = null;
|
||||
protected static $ips = [];
|
||||
protected static $socket_ip = null;
|
||||
protected static $socket_port = null;
|
||||
public static $lock = 0;
|
||||
protected static $heart_lock = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 0.5;
|
||||
|
||||
self::start();
|
||||
$message = self::decodeMessage();
|
||||
if (!$message) {
|
||||
unset($message);
|
||||
self::resetConnection();
|
||||
return;
|
||||
}
|
||||
$data = DataTreating::socketJsonToArray($message);
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
DataTreating::socketArrayToDispose($data);
|
||||
return;
|
||||
}
|
||||
|
||||
// KILL
|
||||
protected static function killConnection()
|
||||
{
|
||||
socket_clear_error(self::$socket_connection);
|
||||
socket_shutdown(self::$socket_connection);
|
||||
socket_close(self::$socket_connection);
|
||||
self::$socket_connection = null;
|
||||
}
|
||||
|
||||
// RECONNECT
|
||||
protected static function resetConnection()
|
||||
{
|
||||
$errorcode = socket_last_error();
|
||||
$errormsg = socket_strerror($errorcode);
|
||||
unset($errormsg);
|
||||
unset($errorcode);
|
||||
self::killConnection();
|
||||
// Log::warning('SOCKET连接断开,5秒后重新连接...');
|
||||
// sleep(5);
|
||||
self::start();
|
||||
return;
|
||||
}
|
||||
|
||||
// SOCKET READER
|
||||
protected static function readerSocket(int $length)
|
||||
{
|
||||
return socket_read(self::$socket_connection, $length);
|
||||
}
|
||||
|
||||
// DECODE MESSAGE
|
||||
protected static function decodeMessage()
|
||||
{
|
||||
$res = '';
|
||||
$tmp = '';
|
||||
while (1) {
|
||||
while ($out = self::readerSocket(16)) {
|
||||
$res = unpack('N', $out);
|
||||
unset($out);
|
||||
if ($res[1] != 16) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($res[1])) {
|
||||
$length = $res[1] - 16;
|
||||
if ($length > 65535) {
|
||||
continue;
|
||||
}
|
||||
if ($length <= 0) {
|
||||
return false;
|
||||
}
|
||||
return self::readerSocket($length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// START
|
||||
protected static function start()
|
||||
{
|
||||
if (is_null(self::$socket_connection)) {
|
||||
$room_id = empty(getenv('SOCKET_ROOM_ID')) ? Live::getUserRecommend() : Live::getRealRoomID(getenv('SOCKET_ROOM_ID'));
|
||||
$room_id = intval($room_id);
|
||||
if ($room_id) {
|
||||
self::getSocketServer($room_id);
|
||||
self::connectServer($room_id, self::$socket_ip, self::$socket_port);
|
||||
}
|
||||
}
|
||||
self::sendHeartBeatPkg();
|
||||
return;
|
||||
}
|
||||
|
||||
// SEND HEART
|
||||
protected static function sendHeartBeatPkg()
|
||||
{
|
||||
if (self::$heart_lock > time()) {
|
||||
return;
|
||||
}
|
||||
$action_heart_beat = intval(getenv('ACTIONHEARTBEAT'));
|
||||
$str = pack('NnnNN', 16, 16, 1, $action_heart_beat, 1);
|
||||
socket_write(self::$socket_connection, $str, strlen($str));
|
||||
Log::info('发送心跳包到弹幕服务器!');
|
||||
self::$heart_lock = time() + 30;
|
||||
return;
|
||||
}
|
||||
|
||||
// SOCKET CONNECT
|
||||
protected static function connectServer($room_id, $ip, $port)
|
||||
{
|
||||
$falg = 10;
|
||||
while ($falg) {
|
||||
try {
|
||||
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
socket_connect($socket, $ip, $port);
|
||||
$str = self::packMsg($room_id);
|
||||
socket_write($socket, $str, strlen($str));
|
||||
self::$socket_connection = $socket;
|
||||
// TODO
|
||||
Log::info("连接到弹幕服务器[{$room_id}]成功!");
|
||||
return;
|
||||
} catch (\Exception $e) {
|
||||
Log::info("连接到弹幕服务器[{$room_id}]失败!");
|
||||
Log::warning($e);
|
||||
$falg -= 1;
|
||||
}
|
||||
}
|
||||
Log::info("连接弹幕服务器[{$room_id}]错误次数过多,检查网络!");
|
||||
exit();
|
||||
}
|
||||
|
||||
// PACK DATA
|
||||
protected static function packMsg($room_id)
|
||||
{
|
||||
$action_entry = intval(getenv('ACTIONENTRY'));
|
||||
$data = sprintf("{\"uid\":%d%08d,\"roomid\":%d}",
|
||||
random_int(1000000, 2999999),
|
||||
random_int(0, 99999999),
|
||||
intval($room_id)
|
||||
);
|
||||
return pack('NnnNN', 16 + strlen($data), 16, 1, $action_entry, 1) . $data;
|
||||
}
|
||||
|
||||
// GET SERVER
|
||||
protected static function getSocketServer(int $room_id): bool
|
||||
{
|
||||
while (1) {
|
||||
try {
|
||||
$payload = [
|
||||
'room_id' => $room_id,
|
||||
];
|
||||
$data = Curl::get('https://api.live.bilibili.com/room/v1/Danmu/getConf', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
// TODO 判断
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::$socket_ip = gethostbyname($data['data']['host']);
|
||||
self::$socket_port = $data['data']['port'];
|
||||
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("获取弹幕服务器出错,错误信息[{$e}]!");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
83
src/Statistics.php
Normal file
83
src/Statistics.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
|
||||
class Statistics
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
// 推送 参加 成功
|
||||
private static $push_list = [];
|
||||
private static $join_list = [];
|
||||
private static $success_list = [];
|
||||
|
||||
|
||||
// 日志
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::getStormResult();
|
||||
|
||||
self::$lock = time() + 5 * 60;
|
||||
}
|
||||
|
||||
|
||||
// 添加推送
|
||||
public static function addPushList(string $key): bool
|
||||
{
|
||||
// 初始化三个必要值
|
||||
if (!array_key_exists($key, self::$push_list)) {
|
||||
self::$push_list[$key] = [];
|
||||
self::$join_list[$key] = [];
|
||||
self::$success_list[$key] = [];
|
||||
}
|
||||
|
||||
array_push(self::$push_list[$key], 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 添加参与
|
||||
public static function addJoinList(string $key): bool
|
||||
{
|
||||
array_push(self::$join_list[$key], 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 添加成功
|
||||
public static function addSuccessList(string $key): bool
|
||||
{
|
||||
array_push(self::$success_list[$key], 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取结果
|
||||
private static function getStormResult(): bool
|
||||
{
|
||||
if (empty(self::$push_list)) {
|
||||
return false;
|
||||
} else {
|
||||
Log::info("-----------密----------封---------线------------");
|
||||
}
|
||||
foreach (self::$push_list as $key => $val) {
|
||||
$title = $key;
|
||||
$push_num = count(self::$push_list[$key]);
|
||||
$join_num = count(self::$join_list[$key]);
|
||||
$success_num = count(self::$success_list[$key]);
|
||||
$content = "{$title}: #推送 {$push_num} #参与 {$join_num} #成功 {$success_num}";
|
||||
Log::notice($content);
|
||||
}
|
||||
Log::info("-----------密----------封---------线------------");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
199
src/Storm.php
Normal file
199
src/Storm.php
Normal file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Storm
|
||||
{
|
||||
const KEY = '节奏风暴';
|
||||
const SWITCH = 'USE_STORM';
|
||||
|
||||
public static $lock = 0;
|
||||
private static $drop_rate = null;
|
||||
private static $attempt = null;
|
||||
private static $wait_list = [];
|
||||
private static $all_list = [];
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function run()
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return;
|
||||
}
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::init();
|
||||
self::startLottery();
|
||||
}
|
||||
|
||||
private static function init()
|
||||
{
|
||||
if (!is_null(self::$attempt) && !is_null(self::$drop_rate)) {
|
||||
return;
|
||||
}
|
||||
// TODO 信息不完整, 请检查配置文件 没有严格校验
|
||||
self::$drop_rate = getenv('STORM_DROPRATE') !== "" ? (int)getenv('STORM_DROPRATE') : 0;
|
||||
self::$attempt = getenv('STORM_ATTEMPT') !== "" ? explode(',', getenv('STORM_ATTEMPT')) : [30, 50];
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽奖逻辑
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function startLottery(): bool
|
||||
{
|
||||
while (true) {
|
||||
$storm = array_shift(self::$wait_list);
|
||||
if (is_null($storm)) {
|
||||
break;
|
||||
}
|
||||
// 丢弃
|
||||
if (mt_rand(1, 100) <= (int)self::$drop_rate) {
|
||||
break;
|
||||
}
|
||||
$storm_lid = $storm['lid'];
|
||||
$storm_rid = $storm['rid'];
|
||||
Live::goToRoom($storm_rid);
|
||||
Statistics::addJoinList(self::KEY);
|
||||
$num = random_int((int)self::$attempt[0], (int)self::$attempt[1]);
|
||||
for ($i = 1; $i < $num; $i++) {
|
||||
if (!self::lottery($storm_rid, $storm_lid, $i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日志输出
|
||||
* @param $id
|
||||
* @param $num
|
||||
* @param $info
|
||||
* @return string
|
||||
*/
|
||||
private static function formatInfo($id, $num, $info): string
|
||||
{
|
||||
return "风暴 {$id} 请求 {$num} 状态 {$info}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求抽奖
|
||||
* @param $rid
|
||||
* @param $lid
|
||||
* @param $num
|
||||
* @return bool
|
||||
*/
|
||||
protected static function lottery($rid, $lid, $num): bool
|
||||
{
|
||||
$user_info = User::parseCookies();
|
||||
$payload = [
|
||||
"id" => $lid,
|
||||
"color" => "16772431",
|
||||
"roomid" => $rid,
|
||||
"captcha_token" => "",
|
||||
"captcha_phrase" => "",
|
||||
"token" => $user_info['token'],
|
||||
"csrf_token" => $user_info['token'],
|
||||
"visit_id" => "",
|
||||
];
|
||||
$raw = Curl::post('https://api.live.bilibili.com/lottery/v1/Storm/join', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['code'] == 429 || $de_raw['code'] == -429) {
|
||||
Log::notice(self::formatInfo($lid, $num, '节奏风暴未实名或异常验证码'));
|
||||
return false;
|
||||
}
|
||||
if (isset($de_raw['data']) && empty($de_raw['data'])) {
|
||||
Log::notice(self::formatInfo($lid, $num, '节奏风暴在小黑屋'));
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['code'] == 0) {
|
||||
Statistics::addSuccessList(self::KEY);
|
||||
Log::notice(self::formatInfo($lid, $num, $de_raw['data']['mobile_content']));
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['msg'] == '节奏风暴不存在') {
|
||||
Log::notice(self::formatInfo($lid, $num, '节奏风暴已结束'));
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['msg'] == '已经领取奖励') {
|
||||
Log::notice(self::formatInfo($lid, $num, '节奏风暴已经领取'));
|
||||
return false;
|
||||
}
|
||||
if ($de_raw['msg'] == '你错过了奖励,下次要更快一点哦~') {
|
||||
return true;
|
||||
}
|
||||
Log::notice(self::formatInfo($lid, $num, $de_raw['msg']));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查ID
|
||||
* @param $room_id
|
||||
* @return array|bool
|
||||
*/
|
||||
protected static function stormCheckId($room_id)
|
||||
{
|
||||
$raw = Curl::get('https://api.live.bilibili.com/lottery/v1/Storm/check?roomid=' . $room_id);
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if (empty($de_raw['data']) || $de_raw['data']['hasJoin'] != 0) {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'id' => $de_raw['data']['id'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重复检测
|
||||
* @param $lid
|
||||
* @return bool
|
||||
*/
|
||||
private static function toRepeatLid($lid): bool
|
||||
{
|
||||
if (in_array($lid, self::$all_list)) {
|
||||
return true;
|
||||
}
|
||||
if (count(self::$all_list) > 2000) {
|
||||
self::$all_list = [];
|
||||
}
|
||||
array_push(self::$all_list, $lid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据推入队列
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public static function pushToQueue(array $data): bool
|
||||
{
|
||||
if (getenv(self::SWITCH) == 'false') {
|
||||
return false;
|
||||
}
|
||||
if (self::toRepeatLid($data['lid'])) {
|
||||
return false;
|
||||
}
|
||||
Statistics::addPushList(self::KEY);
|
||||
self::$wait_list = array_merge(self::$wait_list, [['rid' => $data['rid'], 'lid' => $data['lid']]]);
|
||||
$wait_num = count(self::$wait_list);
|
||||
if ($wait_num > 2) {
|
||||
Log::info("当前队列中共有 {$wait_num} 个" . self::KEY . "待抽奖");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
101
src/Task.php
Normal file
101
src/Task.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Task
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info('正在检查每日任务...');
|
||||
|
||||
$data = self::check();
|
||||
|
||||
if (isset($data['data']['double_watch_info'])) {
|
||||
self::double_watch_info($data['data']['double_watch_info']);
|
||||
}
|
||||
if (isset($data['data']['sign_info'])) {
|
||||
self::sign_info($data['data']['sign_info']);
|
||||
}
|
||||
|
||||
self::$lock = time() + 3600;
|
||||
}
|
||||
|
||||
protected static function check()
|
||||
{
|
||||
$payload = [];
|
||||
$data = Curl::get('https://api.live.bilibili.com/i/api/taskInfo', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('每日任务检查失败!', ['msg' => $data['message']]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected static function sign_info($info)
|
||||
{
|
||||
Log::info('检查任务「每日签到」...');
|
||||
|
||||
if ($info['status'] == 1) {
|
||||
Log::notice('该任务已完成');
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = [];
|
||||
$data = Curl::get('https://api.live.bilibili.com/appUser/getSignInfo', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning('签到失败', ['msg' => $data['message']]);
|
||||
} else {
|
||||
Log::info('签到成功');
|
||||
// 推送签到信息
|
||||
Notice::run('todaySign', $data['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
protected static function double_watch_info($info)
|
||||
{
|
||||
Log::info('检查任务「双端观看直播」...');
|
||||
|
||||
if ($info['status'] == 2) {
|
||||
Log::notice('已经领取奖励');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($info['mobile_watch'] != 1 || $info['web_watch'] != 1) {
|
||||
Log::notice('任务未完成,请等待');
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'task_id' => 'double_watch_task',
|
||||
];
|
||||
$data = Curl::post('https://api.live.bilibili.com/activity/v1/task/receive_award', Sign::api($payload));
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (isset($data['code']) && $data['code']) {
|
||||
Log::warning("「双端观看直播」任务奖励领取失败,{$data['message']}!");
|
||||
} else {
|
||||
Log::info('奖励领取成功!');
|
||||
foreach ($info['awards'] as $vo) {
|
||||
Log::notice(sprintf("获得 %s × %d", $vo['name'], $vo['num']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
src/TcpClinet.php
Normal file
253
src/TcpClinet.php
Normal file
@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
use Exception;
|
||||
use Socket\Raw\Factory;
|
||||
|
||||
class TcpClinet
|
||||
{
|
||||
public static $lock = 0;
|
||||
private static $heart_lock = 0;
|
||||
private static $client = null;
|
||||
private static $server_addr = null;
|
||||
private static $server_key = null;
|
||||
|
||||
/**
|
||||
* @desc 入口
|
||||
*/
|
||||
public static function run()
|
||||
{
|
||||
if (self::$lock > time() || getenv('USE_SERVER') == 'false') {
|
||||
return;
|
||||
}
|
||||
self::init();
|
||||
self::heartBeat();
|
||||
self::receive();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc 初始化
|
||||
*/
|
||||
private static function init()
|
||||
{
|
||||
if (empty(getenv('SERVER_ADDR')) || empty(getenv('SERVER_KEY'))) {
|
||||
exit('推送服务器信息不完整, 请检查配置文件!');
|
||||
}
|
||||
if (!self::$server_addr || !self::$server_key) {
|
||||
self::$server_addr = getenv('SERVER_ADDR');
|
||||
self::$server_key = getenv('SERVER_KEY');
|
||||
}
|
||||
if (!self::$client) {
|
||||
self::openConnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc 数据封装
|
||||
* @param $value
|
||||
* @param $fmt
|
||||
* @return string
|
||||
*/
|
||||
private static function packMsg($value, $fmt = "N")
|
||||
{
|
||||
$head = pack($fmt, strlen($value));
|
||||
$data = $head . $value;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 数据解包
|
||||
* @param $value
|
||||
* @param string $fmt
|
||||
* @return array|false
|
||||
*/
|
||||
private static function unPackMsg($value, $fmt = "N")
|
||||
{
|
||||
$data = unpack($fmt, $value);
|
||||
return $data[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 连接认证
|
||||
*/
|
||||
private static function handShake()
|
||||
{
|
||||
self::writer(
|
||||
json_encode([
|
||||
'code' => 0,
|
||||
'type' => 'ask',
|
||||
'data' => [
|
||||
'key' => self::$server_key,
|
||||
]
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 心跳
|
||||
*/
|
||||
private static function heartBeat()
|
||||
{
|
||||
if (self::$heart_lock <= time()) {
|
||||
if (self::writer("")) {
|
||||
// 心跳默认35s 调整数据错开错误
|
||||
self::$heart_lock = time() + 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 读数据
|
||||
* @param $length
|
||||
* @return array|bool|false
|
||||
*/
|
||||
private static function reader($length)
|
||||
{
|
||||
$data = false;
|
||||
try {
|
||||
$data = self::$client->read($length);
|
||||
if (!$data) {
|
||||
throw new Exception("Connection failure");
|
||||
}
|
||||
if ($length == 4) $data = self::unPackMsg($data);
|
||||
} catch (Exception $exception) {
|
||||
self::reConnect();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 写数据
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
private static function writer($data)
|
||||
{
|
||||
$status = false;
|
||||
try {
|
||||
$data = self::packMsg($data);
|
||||
$status = self::$client->write($data);
|
||||
} catch (Exception $exception) {
|
||||
self::reConnect();
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc 打开连接
|
||||
*/
|
||||
private static function openConnect()
|
||||
{
|
||||
if (!self::$client) {
|
||||
try {
|
||||
$socket = (new Factory())->createClient(self::$server_addr, 40);
|
||||
self::$client = $socket;
|
||||
self::handShake();
|
||||
Log::info("连接到 {$socket->getPeerName()} 推送服务器");
|
||||
} catch (Exception $e) {
|
||||
Log::error("连接到推送服务器失败, {$e->getMessage()}");
|
||||
self::$lock = time() + 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 重新连接
|
||||
*/
|
||||
private static function reConnect()
|
||||
{
|
||||
Log::info('重新连接到推送服务器');
|
||||
self::closeConnect();
|
||||
self::openConnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 断开连接
|
||||
*/
|
||||
private static function closeConnect()
|
||||
{
|
||||
Log::info('断开推送服务器');
|
||||
try {
|
||||
self::$client->shutdown();
|
||||
self::$client->close();
|
||||
} catch (Exception $exception) {
|
||||
// var_dump($exception);
|
||||
}
|
||||
self::$client = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 读取数据
|
||||
*/
|
||||
private static function receive()
|
||||
{
|
||||
$len_body = self::reader(4);
|
||||
if (!$len_body) {
|
||||
// 长度为0 ,空信息
|
||||
return;
|
||||
}
|
||||
Log::debug("(len=$len_body)");
|
||||
$body = self::reader($len_body);
|
||||
$raw_data = json_decode($body, true);
|
||||
// 人气值(或者在线人数或者类似)以及心跳
|
||||
$data_type = $raw_data['type'];
|
||||
switch ($data_type) {
|
||||
case 'raffle':
|
||||
// 抽奖推送
|
||||
// Log::notice($body);
|
||||
DataTreating::distribute($raw_data['data']);
|
||||
break;
|
||||
case 'entered':
|
||||
// 握手确认
|
||||
Log::info("确认到推送服务器 {$raw_data['type']}");
|
||||
break;
|
||||
case 'error':
|
||||
// 致命错误
|
||||
Log::warning($body);
|
||||
exit("推送服务器发生致命错误 {$raw_data['data']['msg']}");
|
||||
break;
|
||||
case 'heartbeat':
|
||||
// 服务端心跳推送
|
||||
// Log::info("推送服务器心跳推送 {$body}");
|
||||
Log::debug("(heartbeat={$raw_data['data']['now']})");
|
||||
break;
|
||||
default:
|
||||
// 未知信息
|
||||
var_dump($raw_data);
|
||||
Log::info("出现未知信息 {$body}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 写入log
|
||||
* @param $message
|
||||
*/
|
||||
private static function writeLog($message)
|
||||
{
|
||||
$path = './danmu/';
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
chmod($path, 0777);
|
||||
}
|
||||
$filename = $path . getenv('APP_USER') . ".log";
|
||||
$date = date('[Y-m-d H:i:s] ');
|
||||
$data = "[{$date}]{$message}" . PHP_EOL;
|
||||
file_put_contents($filename, $data, FILE_APPEND);
|
||||
return;
|
||||
}
|
||||
}
|
||||
84
src/User.php
Normal file
84
src/User.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class User
|
||||
{
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
}
|
||||
|
||||
// 实名检测
|
||||
public static function realnameCheck(): bool
|
||||
{
|
||||
$payload = [];
|
||||
$raw = Curl::get('https://account.bilibili.com/identify/index', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
//检查有没有名字,没有则没实名
|
||||
if (!$de_raw['data']['memberPerson']['realname']) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 老爷检测
|
||||
public static function isMaster(): bool
|
||||
{
|
||||
$payload = [
|
||||
'ts' => Live::getMillisecond(),
|
||||
];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/User/getUserInfo', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
if ($de_raw['msg'] == 'ok') {
|
||||
if ($de_raw['data']['vip'] || $de_raw['data']['svip']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 用户名写入
|
||||
public static function userInfo(): bool
|
||||
{
|
||||
$payload = [
|
||||
'ts' => Live::getMillisecond(),
|
||||
];
|
||||
$raw = Curl::get('https://api.live.bilibili.com/User/getUserInfo', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
|
||||
if (getenv('APP_UNAME') != "") {
|
||||
return true;
|
||||
}
|
||||
if ($de_raw['msg'] == 'ok') {
|
||||
File::writeNewEnvironmentFileWith('APP_UNAME', $de_raw['data']['uname']);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//转换信息
|
||||
public static function parseCookies(): array
|
||||
{
|
||||
$cookies = getenv('COOKIE');
|
||||
preg_match('/bili_jct=(.{32})/', $cookies, $token);
|
||||
$token = isset($token[1]) ? $token[1] : '';
|
||||
preg_match('/DedeUserID=(\d+)/', $cookies, $uid);
|
||||
$uid = isset($uid[1]) ? $uid[1] : '';
|
||||
preg_match('/DedeUserID__ckMd5=(.{16})/', $cookies, $sid);
|
||||
$sid = isset($sid[1]) ? $sid[1] : '';
|
||||
return [
|
||||
'token' => $token,
|
||||
'uid' => $uid,
|
||||
'sid' => $sid,
|
||||
];
|
||||
}
|
||||
}
|
||||
217
src/Websocket.php
Normal file
217
src/Websocket.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
use Wrench\Client;
|
||||
|
||||
class Websocket
|
||||
{
|
||||
public static $lock = 0;
|
||||
protected static $heart_lock = 0;
|
||||
protected static $websocket = null;
|
||||
protected static $room_id = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @use 入口
|
||||
*/
|
||||
public static function run()
|
||||
{
|
||||
if (static::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
static::init();
|
||||
static::heart();
|
||||
static::receive();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 初始化
|
||||
*/
|
||||
protected static function init()
|
||||
{
|
||||
if (!static::$websocket) {
|
||||
$client = new Client(
|
||||
'ws://broadcastlv.chat.bilibili.com:2244/sub',
|
||||
'http://live.bilibili.com'
|
||||
);
|
||||
static::$websocket = $client;
|
||||
}
|
||||
|
||||
if (!static::$room_id) {
|
||||
static::$room_id = empty(getenv('SOCKET_ROOM_ID')) ? Live::getUserRecommend() : Live::getRealRoomID(getenv('SOCKET_ROOM_ID'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 连接
|
||||
*/
|
||||
protected static function connect()
|
||||
{
|
||||
Log::info("连接弹幕服务器");
|
||||
if (!static::$websocket->connect()) {
|
||||
Log::error('连接弹幕服务器失败');
|
||||
static::$lock = time() + 60;
|
||||
return;
|
||||
}
|
||||
static::$websocket->sendData(
|
||||
static::packMsg(json_encode([
|
||||
'uid' => 0,
|
||||
'roomid' => static::$room_id,
|
||||
'protover' => 1,
|
||||
'platform' => 'web',
|
||||
'clientver' => '1.4.1',
|
||||
]), 0x0007)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 断开连接
|
||||
*/
|
||||
protected static function disconnect()
|
||||
{
|
||||
Log::info('断开弹幕服务器');
|
||||
static::$websocket->disconnect();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 读取数据
|
||||
*/
|
||||
protected static function receive()
|
||||
{
|
||||
$responses = static::$websocket->receive();
|
||||
if (is_array($responses) && !empty($responses)) {
|
||||
foreach ($responses as $response) {
|
||||
static::split($response->getPayload());
|
||||
}
|
||||
static::receive();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 发送心跳
|
||||
*/
|
||||
protected static function heart()
|
||||
{
|
||||
if (!static::$websocket->isConnected()) {
|
||||
static::connect();
|
||||
return;
|
||||
}
|
||||
if (static::$heart_lock <= time()) {
|
||||
if (static::$websocket->sendData(static::packMsg('', 0x0002))) {
|
||||
static::$heart_lock = time() + 30;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected static function type($id)
|
||||
{
|
||||
$option = [
|
||||
0x0002 => 'WS_OP_HEARTBEAT',
|
||||
0x0003 => 'WS_OP_HEARTBEAT_REPLY',
|
||||
0x0005 => 'WS_OP_MESSAGE',
|
||||
0x0007 => 'WS_OP_USER_AUTHENTICATION',
|
||||
0x0008 => 'WS_OP_CONNECT_SUCCESS',
|
||||
];
|
||||
return isset($option[$id]) ? $option[$id] : "WS_OP_UNKNOW($id)";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $bin
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function split($bin)
|
||||
{
|
||||
if (strlen($bin)) {
|
||||
$head = unpack('Npacklen/nheadlen/nver/Nop/Nseq', substr($bin, 0, 16));
|
||||
$bin = substr($bin, 16);
|
||||
|
||||
$length = isset($head['packlen']) ? $head['packlen'] : 16;
|
||||
$type = isset($head['op']) ? $head['op'] : 0x0000;
|
||||
$body = substr($bin, 0, $length - 16);
|
||||
|
||||
Log::debug(static::type($type) . " (len=$length)");
|
||||
|
||||
if (($length - 16) > 65535 || ($length - 16) < 0) {
|
||||
Log::notice("长度{$length}异常,重新连接服务器!");
|
||||
if (static::$websocket->isConnected()) {
|
||||
static::disconnect();
|
||||
}
|
||||
if (!static::$websocket->isConnected()) {
|
||||
static::connect();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($type == 0x0005 || $type == 0x0003) {
|
||||
if ($head['ver'] == 2) {
|
||||
$body = gzuncompress($body);
|
||||
if (substr($body, 0, 1) != '{') {
|
||||
static::split($bin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DataTreating::socketJsonToArray($body);
|
||||
}
|
||||
|
||||
$bin = substr($bin, $length - 16);
|
||||
if (strlen($bin)) {
|
||||
static::split($bin);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param $option
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function packMsg($value, $option)
|
||||
{
|
||||
$head = pack('NnnNN', 0x10 + strlen($value), 0x10, 0x01, $option, 0x0001);
|
||||
$str = $head . $value;
|
||||
static::split($str);
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 写入log
|
||||
* @param $message
|
||||
*/
|
||||
private static function writeLog($message)
|
||||
{
|
||||
$path = './danmu/';
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
chmod($path, 0777);
|
||||
}
|
||||
$filename = $path . getenv('APP_USER') . ".log";
|
||||
$date = date('[Y-m-d H:i:s] ');
|
||||
$data = "[{$date}]{$message}" . PHP_EOL;
|
||||
file_put_contents($filename, $data, FILE_APPEND);
|
||||
return;
|
||||
}
|
||||
}
|
||||
68
src/Winning.php
Normal file
68
src/Winning.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Website: https://mudew.com/
|
||||
* Author: Lkeme
|
||||
* License: The MIT License
|
||||
* Email: Useri@live.cn
|
||||
* Updated: 2019
|
||||
*/
|
||||
|
||||
namespace lkeme\BiliHelper;
|
||||
|
||||
class Winning
|
||||
{
|
||||
public static $lock = 0;
|
||||
|
||||
// RUN
|
||||
public static function run()
|
||||
{
|
||||
// 活动统一
|
||||
RaffleHandler::resultWeb();
|
||||
|
||||
// 实物
|
||||
self::winningRecords();
|
||||
}
|
||||
|
||||
// 中奖记录
|
||||
protected static function winningRecords()
|
||||
{
|
||||
if (self::$lock > time()) {
|
||||
return;
|
||||
}
|
||||
self::$lock = time() + 24 * 60 * 60;
|
||||
|
||||
$payload = [
|
||||
'page' => '1',
|
||||
'month' => '',
|
||||
];
|
||||
$raw = Curl::post('https://api.live.bilibili.com/lottery/v1/award/award_list', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
$month = $de_raw['data']['month_list'][0]['Ym'];
|
||||
|
||||
// TODO
|
||||
$payload = [
|
||||
'page' => '1',
|
||||
'month' => $month,
|
||||
];
|
||||
$raw = Curl::post('https://api.live.bilibili.com/lottery/v1/award/award_list', Sign::api($payload));
|
||||
$de_raw = json_decode($raw, true);
|
||||
// 没有记录
|
||||
if (empty($de_raw['data']['list'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$init_time = strtotime(date("y-m-d h:i:s")); //当前时间
|
||||
foreach ($de_raw['data']['list'] as $gift) {
|
||||
$next_time = strtotime($gift['create_time']); //礼物时间
|
||||
$day = ceil(($init_time - $next_time) / 86400); //60s*60min*24h
|
||||
|
||||
if ($day <= 2 && $gift['update_time'] == '') {
|
||||
$data_info = '您在: ' . $gift['create_time'] . '抽中[' . $gift['gift_name'] . 'X' . $gift['gift_num'] . ']未查看!';
|
||||
Log::notice($data_info);
|
||||
// TODO 推送 log
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user