[update] Version 0.1.0

This commit is contained in:
Lkeme 2019-12-27 02:19:57 +08:00
parent 4edfb295b4
commit 44e54d38a8
50 changed files with 2509 additions and 1851 deletions

View File

@ -1,62 +1,40 @@
# Release Notes
# 本项目Log
## v0.0.1.191223 alpha (2019-12-23)
## v0.1.0.191227 alpha (2019-12-27)
### Added
- anchor(beta)
-
- 新增备用官方分区监控
- 新增按勋章亲密度赠送
- 新增天选时刻获奖推送
-
### Changed
- 优化部分架构
- 优化独立监控
- 优化监控逻辑
- 优化礼物赠送
- 优化通用抽奖
- 优化推送逻辑
-
### Fixed
-
- 修复部分日志显示
- 修复数据统计异常
- 修复瓜子宝箱异常
- 修复Server酱异常
- 修复天选时刻异常
- 修复抽奖数据异常
- 修复部分已知BUG
- 修复获奖推送通知
-
## v0.0.1.191030 alpha (2019-10-30)
## v0.0.5.191223 alpha (2019-12-23)
### Added
-
### Changed
- unify raffle api
- danmu
- captcha ocr api
### Fixed
-
## v0.0.1.190730 alpha (2019-07-30)
### Added
-
### Changed
- unify raffle api
### Fixed
-
## v0.0.1.190723 alpha (2019-07-23)
### Added
- pk lottery
### Changed
-
### Fixed
-
## v0.0.4.191030 alpha (2019-10-30)
## v0.0.3.190730 alpha (2019-07-30)
## v0.0.2.190723 alpha (2019-07-23)
## v0.0.1.190713 alpha (2019-07-13)
### Added
- Initialization
### Changed
- Initialization
### Fixed
- Initialization

78
DOC.md
View File

@ -2,7 +2,7 @@
<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.191213 alpha-green.svg?longCache=true&style=for-the-badge">
<img src="https://img.shields.io/badge/version-0.1.0.191227 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>
@ -17,35 +17,40 @@ B 站直播实用脚本
|plugin |version |description |
|--------------------|--------------------|--------------------|
|Daily |19.12.23 |每日背包奖励 |
|GiftSend |19.12.23 |自动清空过期礼物 |
|Heart |19.12.23 |双端直播间心跳 |
|Login |19.12.23 |帐号登录组件 |
|Silver |19.12.23 |自动领宝箱 |
|Task |19.12.23 |每日任务 |
|GiftHeart |19.12.23 |心跳礼物 |
|Silver2Coin |19.12.23 |银瓜子换硬币 |
|MaterialObject |19.12.23 |实物抽奖 |
|GroupSignIn |19.12.23 |应援团签到 |
|Storm |19.12.23 |节奏风暴 |
|Notice |19.12.23 |Server酱 |
|UnifyRaffle |19.12.23 |统一活动抽奖 |
|MasterSite |19.12.23 |主站(观看、分享、投币)|
|Guard |19.12.23 |舰长上船亲密度 |
|PkRaffle |19.12.23 |大乱斗 |
|Anchor |19.12.23 |天选时刻 |
|Login |19.12.27 |账号登录 |
|Sleep |19.12.27 |休眠控制 |
|MasterSite |19.12.27 |主站助手 |
|Daily |19.12.27 |每日礼包 |
|Heart |19.12.27 |双端心跳 |
|Task |19.12.27 |每日任务 |
|Silver |19.12.27 |银瓜子宝箱 |
|Barrage |19.12.27 |活跃弹幕 |
|Silver2Coin |19.12.27 |银瓜子换硬币 |
|GiftSend |19.12.27 |礼物赠送 |
|GroupSignIn |19.12.27 |友爱社签到 |
|GiftHeart |19.12.27 |心跳礼物 |
|MaterialObject |19.12.27 |实物抽奖 |
|AloneTcpClient |19.12.27 |独立监控 |
|ZoneTcpClient |19.12.27 |分区监控 |
|StormRaffle |19.12.27 |节奏风暴 |
|GiftRaffle |19.12.27 |活动礼物 |
|PkRaffle |19.12.27 |大乱斗 |
|GuardRaffle |19.12.27 |舰长总督 |
|AnchorRaffle |19.12.27 |天选时刻 |
|AwardRecord |19.12.27 |获奖通知 |
|Statistics |19.12.27 |数据统计 |
## 打赏赞助
![](https://i.loli.net/2018/04/07/5ac79ff8c2900.png)
![](https://i.loli.net/2019/07/13/5d2963e5cc1eb22973.png)
> 待添加
## 未完成功能
|待续 |
|-----------|
|添加多用户 |
|待添加 |
|多用户 |
## 环境依赖
@ -58,7 +63,6 @@ B 站直播实用脚本
|php_json |
|php_zlib |
|php_mbstring |
|待添加 |
通常使用 `composer` 工具会自动检测上述依赖问题。
@ -79,11 +83,7 @@ https://mirrors.cloud.tencent.com/composer/
composer config -g repos.packagist composer https://mirrors.cloud.tencent.com/composer/
```
## 打赏赞助
![](https://i.loli.net/2019/07/13/5d2963e5cc1eb22973.png)
> 待添加
## 使用指南
@ -196,22 +196,36 @@ APP_CALLBACK="https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAR_ID>&
## 直播间 ID 问题
`user.conf` 文件中有个 `ROOM_ID` 配置,填写此项可以清空临过期礼物给指定直播间。
文件 `user.conf`
`ROOM_ID` 配置,填写此项可以清空临过期礼物给指定直播间。
`ROOM_LIST` 配置,使用长位直播间,填写此项可以清空临礼物给指定有勋章的直播间。
`FEED_FILL` 配置,搭配上一条使用,使用过期礼物或者倒序使用正常礼物。
`SOCKET_ROOM_ID` 配置,监控使用,暂时没用到。
通常可以在直播间页面的 url 获取到它
```
http://live.bilibili.com/9522051
```
所有直播间号码小于 1000 的直播间为短号,该脚本在每次启动会自动修正,无需关心,
长位直播间ID获取
```
https://api.live.bilibili.com/room/v1/Room/room_init?id=3
```
所有直播间号码小于 1000 的直播间为短号,该脚本在每次启动会自动修正部分功能,特殊标注的请留意,
## 相关
> 本项目基于[BilibiliHelper](https://github.com/metowolf/BilibiliHelper)项目
> [BilibiliHelper](https://github.com/metowolf/BilibiliHelper)
> [BiliHelper](https://github.com/lkeme/BiliHelper)
> [Github](https://github.com/)
> 基于父项目的架构开发,在此感谢父项目的开发
> 保留父项目没必要修改的信息,另外欢迎重构(Haha)
## License 许可证

View File

@ -7,7 +7,7 @@ Group: [55308141](https://jq.qq.com/?_wv=1027&k=5AIDaJg)
## 公告
Currently for Personal Edition **0.0.1.191213 alpha**
Currently for Personal Edition **0.1.0.191227 alpha**
## 文档

View File

@ -10,10 +10,10 @@
"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.1"
"clue/socket-raw": "^1.4.1",
"vlucas/phpdotenv": "^4.1"
},
"license": "MIT",
"authors": [
@ -25,7 +25,9 @@
],
"autoload": {
"psr-4": {
"lkeme\\BiliHelper\\": "src/"
"BiliHelper\\Core\\": "src/core",
"BiliHelper\\Plugin\\": "src/plugin",
"BiliHelper\\Util\\": "src/util"
}
}
}

88
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d09e06835f7ff6a06fcb74285e6617c5",
"content-hash": "6034c5a467eb270c2c7d1ce12d7277fd",
"packages": [
{
"name": "bramus/ansi-php",
@ -245,6 +245,67 @@
],
"time": "2019-12-20T14:15:16+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959",
"reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^5.5.9 || ^7.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.3",
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"time": "2019-12-15T19:35:24+00:00"
},
{
"name": "psr/log",
"version": "1.1.2",
@ -364,16 +425,16 @@
},
{
"name": "vlucas/phpdotenv",
"version": "v2.6.1",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5"
"reference": "0176075a1b7ee9cf86f70143ec79edf7072c975a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
"reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/0176075a1b7ee9cf86f70143ec79edf7072c975a",
"reference": "0176075a1b7ee9cf86f70143ec79edf7072c975a",
"shasum": "",
"mirrors": [
{
@ -383,16 +444,18 @@
]
},
"require": {
"php": ">=5.3.9",
"php": "^5.5.9 || ^7.0",
"phpoption/phpoption": "^1.7.1",
"symfony/polyfill-ctype": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.0"
"bamarni/composer-bin-plugin": "^1.3",
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev"
"dev-master": "4.1-dev"
}
},
"autoload": {
@ -405,10 +468,15 @@
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "http://www.vancelucas.com"
"homepage": "https://vancelucas.com/"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
@ -417,7 +485,7 @@
"env",
"environment"
],
"time": "2019-01-29T11:11:52+00:00"
"time": "2019-12-14T13:59:29+00:00"
}
],
"packages-dev": [],

View File

@ -11,32 +11,6 @@ ACCESS_TOKEN=
REFRESH_TOKEN=
COOKIE=
#######################
# 固定设置 #
#######################
# 推送服务器
USE_SERVER=true
SERVER_ADDR=tcp://47.102.120.84:10010
SERVER_KEY=,*(?PVl]nIbo35sB
# 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
#######################
# 功能设置 #
#######################
@ -78,8 +52,51 @@ USE_ADD_COIN=false
ADD_COIN_AV_NUM=1
ADD_COIN_AV=33492180
# 休眠时间|时间区间(0-23)|逗号分隔
USE_SLEEP=true
SLEEP_SECTION=2,3,4,5,6
#######################
# 日志设置 #
# 基础设置 #
#######################
# 独立推送服务(主)
USE_ALONE_SERVER=true
ALONE_SERVER_ADDR=tcp://47.102.120.84:10010
ALONE_SERVER_KEY=,*(?PVl]nIbo35sB
# 分区推送服务(备)
USE_ZONE_SERVER=false
ZONE_SERVER_ADDR=tcp://broadcastlv.chat.bilibili.com:2243/sub
# 切换HTTPS,为真则使用https协议
USE_HTTPS=true
# 是否使用代理(前提保证有效代理)
USE_PROXY=false
PROXY_IP=127.0.0.1
PROXY_PORT=8888
# SERVER酱, 用于推送消息
USE_SCKEY=
#######################
# 房间设置 #
#######################
# 直播间ID用于礼物赠送
ROOM_ID=9522051
# 勋章亲密度,测试功能,使用长位房间号,左侧优先,仅支持辣条和亿元
# 是否填满(按时间投喂正常礼物,否则过期礼物)|直播间ID列表|逗号分隔
FEED_FILL=false
ROOM_LIST=9522051
# 弹幕监控房间(为空则随机)
SOCKET_ROOM_ID=9522051
#######################
# 程序设置 #
#######################
# 写入日志

View File

@ -8,89 +8,12 @@
* Updated: 2019 ~ 2020
*/
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');
$filename = isset($argv[1]) ? $argv[1] : 'user.conf';
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();
TcpClient::run();
Storm::run();
PkRaffle::run();
UnifyRaffle::run();
Guard::run();
Anchor::run();
Statistics::run();
usleep(0.2 * 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);
$app = new BiliHelper\Core\App();
$app->load(__DIR__, $filename);
$app->start();

View File

@ -1,31 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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);
}
}

View File

@ -1,112 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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['corner_mark'] == '永久'){
continue;
}
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']}");
}
}
}

View File

@ -1,124 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
class Guard
{
const ACTIVE_TITLE = '总督舰长';
const ACTIVE_SWITCH = 'USE_GUARD';
public static $lock = 0;
protected static $wait_list = [];
protected static $all_list = [];
public static function run()
{
if (getenv(self::ACTIVE_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::ACTIVE_TITLE);
$data = self::lottery($guard_rid, $guard_lid);
if ($data['code'] == 0) {
Statistics::addSuccessList(self::ACTIVE_TITLE);
Log::notice("房间 {$guard_rid} 编号 {$guard_lid} " . self::ACTIVE_TITLE . ": {$data['data']['message']}");
} elseif ($data['code'] == 400 && $data['msg'] == '你已经领取过啦') {
Log::info("房间 {$guard_rid} 编号 {$guard_lid} " . self::ACTIVE_TITLE . ": {$data['msg']}");
} else {
Log::warning("房间 {$guard_rid} 编号 {$guard_lid} " . self::ACTIVE_TITLE . ": {$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::ACTIVE_SWITCH) == 'false') {
return false;
}
if (self::toRepeatLid($data['lid'])) {
return false;
}
Statistics::addPushList(self::ACTIVE_TITLE);
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::ACTIVE_TITLE . "待抽奖");
}
return true;
}
}

View File

@ -1,154 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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;
GiftHeart::$lock = $second;
TcpClient::$lock = $second;
return;
}
}

View File

@ -1,123 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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);
}
}

View File

@ -1,193 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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;
}
}

View File

@ -1,201 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
class Storm
{
const ACTIVE_TITLE = '节奏风暴';
const ACTIVE_SWITCH = 'USE_STORM';
public static $lock = 0;
protected static $wait_list = [];
protected static $all_list = [];
private static $drop_rate = null;
private static $attempt = null;
/**
* @throws \Exception
*/
public static function run()
{
if (getenv(self::ACTIVE_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::ACTIVE_TITLE);
$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::ACTIVE_TITLE);
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::ACTIVE_SWITCH) == 'false') {
return false;
}
if (self::toRepeatLid($data['lid'])) {
return false;
}
Statistics::addPushList(self::ACTIVE_TITLE);
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::ACTIVE_TITLE . "待抽奖");
}
return true;
}
}

View File

@ -1,155 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
class UnifyRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '统一活动';
const ACTIVE_SWITCH = 'USE_ACTIVE';
public static $lock = 0;
public static $rw_lock = 0;
protected static $wait_list = [];
protected static $finish_list = [];
protected static $all_list = [];
/**
* 检查抽奖列表
* @param $rid
* @return bool
*/
protected static function check($rid): bool
{
$payload = [
'roomid' => $rid
];
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/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'],
'wait' => $de_raw['data']['list'][$i]['time_wait'] + strtotime(date("Y-m-d H:i:s")),
'room_id' => $rid,
];
if (static::toRepeatLid($data['raffle_id'])) {
continue;
}
Statistics::addPushList(static::ACTIVE_TITLE);
array_push(static::$wait_list, $data);
}
return true;
}
/**
* @use WEB中奖查询
*/
public static function resultWeb()
{
// 时间锁
if (static::$rw_lock > time()) {
return;
}
// 如果待查询为空 && 去重
if (!count(static::$finish_list)) {
static::$rw_lock = time() + 40;
return;
}
// 查询每次查询10个
$flag = 0;
foreach (static::$finish_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/xlive/lottery-interface/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(static::ACTIVE_TITLE);
// 提示信息
$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(static::$finish_list[$flag - 1]);
static::$finish_list = array_values(static::$finish_list);
break;
default:
break;
}
}
static::$rw_lock = time() + 40;
return;
}
/**
* @use 请求抽奖
* @param array $data
* @return bool
*/
protected static function lottery(array $data): bool
{
$user_info = User::parseCookies();
$payload = [
'raffleId' => $data['raffle_id'],
'roomid' => $data['room_id'],
'type' => $data['type'],
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => null,
];
// V3接口 暂做保留处理
// $url = 'https://api.live.bilibili.com/gift/v3/smalltv/join';
// $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v5/smalltv/Join';
$url = 'https://api.live.bilibili.com/gift/v4/smalltv/getAward';
$raw = Curl::post($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
if (isset($de_raw['code']) && $de_raw['code']) {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$de_raw['message']}");
} else {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$de_raw['msg']}");
array_push(static::$finish_list, $data);
}
return true;
}
}

View File

@ -1,217 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
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;
}
}

View File

@ -1,68 +0,0 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
class Winning
{
public static $lock = 0;
// RUN
public static function run()
{
// 活动统一
UnifyRaffle::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;
}
}

72
src/core/App.php Normal file
View File

@ -0,0 +1,72 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Core;
use BiliHelper\Plugin;
class App
{
/**
* App constructor.
*/
public function __construct()
{
set_time_limit(0);
header("Content-Type:text/html; charset=utf-8");
date_default_timezone_set('Asia/Shanghai');
}
/**
* @use 加载配置
* @param $app_path
* @param string $load_file
* @return $this
*/
public function load($app_path, $load_file = 'user.conf')
{
// define('APP_PATH', dirname(__DIR__));
define('APP_PATH', $app_path);
Config::load($load_file);
return $this;
}
/**
* @use 核心运行
*/
public function start()
{
while (true) {
Plugin\Login::run();
Plugin\Sleep::run();
Plugin\MasterSite::run();
Plugin\Daily::run();
Plugin\Heart::run();
Plugin\Task::run();
Plugin\Silver::run();
Plugin\Barrage::run();
Plugin\Silver2Coin::run();
Plugin\GiftSend::run();
Plugin\GroupSignIn::run();
Plugin\GiftHeart::run();
Plugin\MaterialObject::run();
Plugin\AloneTcpClient::run();
Plugin\ZoneTcpClient::run();
Plugin\StormRaffle::run();
Plugin\GiftRaffle::run();
Plugin\PkRaffle::run();
Plugin\GuardRaffle::run();
Plugin\AnchorRaffle::run();
Plugin\AwardRecord::run();
Plugin\Statistics::run();
usleep(0.1 * 1000000);
}
}
}

98
src/core/Config.php Normal file
View File

@ -0,0 +1,98 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Core;
use Dotenv\Dotenv;
class Config
{
private static $app_config;
private static $config_path;
private static $instance;
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = new static;
}
return self::$instance;
}
public static function load($load_file)
{
return self::getInstance()::_load($load_file);
}
public static function put($key, $val = null)
{
return self::getInstance()::_put($key, $val);
}
public static function get($key = null)
{
return self::getInstance()::_get($key);
}
/**
* @use 加载配置文件
* @param string $load_file
*/
private static function _load($load_file)
{
$config_path = str_replace("\\", "/", APP_PATH . "/conf/{$load_file}");
if (!is_file($config_path)) {
die("配置文件 {$load_file} 加载错误,请参照文档添加配置文件!");
}
$app_config = Dotenv::createImmutable(dirname($config_path), $load_file);
$app_config->load();
self::$app_config = $app_config;
self::$config_path = $config_path;
}
/**
* @use 写入配置
* @param $key
* @param $val
* @return bool
*/
private static function _put($key, $val)
{
if (!is_null($val)) {
if (!empty(self::$config_path)) {
file_put_contents(self::$config_path, preg_replace(
'/^' . $key . '=\S*/m',
$key . '=' . $val,
file_get_contents(self::$config_path)
));
}
}
putenv($key . '=' . $val);
// self::$app_config->load();
return true;
}
/**
* @use 读出配置
* @param string|null $key
* @return mixed|null
*/
private static function _get($key)
{
if (self::$app_config->required($key)) {
return getenv($key);
}
return null;
}
}

View File

@ -8,7 +8,7 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Core;
class Curl
{

View File

@ -8,7 +8,7 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Core;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

View File

@ -8,14 +8,18 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
use Exception;
use Socket\Raw\Factory;
class TcpClient
class AloneTcpClient
{
public static $lock = 0;
use TimeLock;
private static $heart_lock = 0;
private static $client = null;
private static $server_addr = null;
@ -23,11 +27,11 @@ class TcpClient
private static $socket_timeout = 0;
/**
* @desc 入口
* @use 入口
*/
public static function run()
{
if (self::$lock > time() || getenv('USE_SERVER') == 'false') {
if (self::getLock() > time() || getenv('USE_ALONE_SERVER') == 'false') {
return;
}
self::init();
@ -37,16 +41,16 @@ class TcpClient
/**
* @desc 初始化
* @use 初始化
*/
private static function init()
{
if (empty(getenv('SERVER_ADDR')) || empty(getenv('SERVER_KEY'))) {
if (empty(getenv('ALONE_SERVER_ADDR')) || empty(getenv('ALONE_SERVER_KEY'))) {
exit('推送服务器信息不完整, 请检查配置文件!');
}
if (!self::$server_addr || !self::$server_key) {
self::$server_addr = getenv('SERVER_ADDR');
self::$server_key = getenv('SERVER_KEY');
self::$server_addr = getenv('ALONE_SERVER_ADDR');
self::$server_key = getenv('ALONE_SERVER_KEY');
}
if (!self::$client) {
self::openConnect();
@ -55,7 +59,7 @@ class TcpClient
/**
* @desc 数据封装
* @use 数据封装
* @param $value
* @param $fmt
* @return string
@ -68,7 +72,7 @@ class TcpClient
}
/**
* @desc 数据解包
* @use 数据解包
* @param $value
* @param string $fmt
* @return array|false
@ -80,7 +84,7 @@ class TcpClient
}
/**
* @desc 连接认证
* @use 连接认证
*/
private static function handShake()
{
@ -96,7 +100,7 @@ class TcpClient
}
/**
* @desc 心跳
* @use 心跳
*/
private static function heartBeat()
{
@ -109,7 +113,7 @@ class TcpClient
}
/**
* @desc 读数据
* @use 读数据
* @param $length
* @return array|bool|false
*/
@ -132,7 +136,7 @@ class TcpClient
}
/**
* @desc 写数据
* @use 写数据
* @param $data
* @return bool
*/
@ -153,7 +157,7 @@ class TcpClient
/**
* @desc 打开连接
* @use 打开连接
*/
private static function openConnect()
{
@ -166,13 +170,13 @@ class TcpClient
Log::info("连接到 {$socket->getPeerName()} 推送服务器");
} catch (Exception $e) {
Log::error("连接到推送服务器失败, {$e->getMessage()}");
self::$lock = time() + 60;
self::setLock( 60);
}
}
}
/**
* @desc 重新连接
* @use 重新连接
*/
private static function reConnect()
{
@ -182,7 +186,7 @@ class TcpClient
}
/**
* @desc 断开连接
* @use 断开连接
*/
private static function closeConnect()
{

View File

@ -8,15 +8,18 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
class Anchor extends BaseRaffle
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class AnchorRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '天选时刻';
const ACTIVE_SWITCH = 'USE_ANCHOR';
public static $lock = 0;
public static $rw_lock = 0;
use TimeLock;
protected static $wait_list = [];
protected static $finish_list = [];
@ -24,41 +27,45 @@ class Anchor extends BaseRaffle
private static $filter_type = [];
/**
* 检查抽奖列表
* @param $rid
* @use 解析数据
* @param int $room_id
* @param array $data
* @return bool
*/
protected static function check($rid): bool
protected static function parse(int $room_id, array $data): bool
{
$payload = [
'roomid' => $rid
];
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/Anchor/Check';
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
// 防止异常情况
if (!isset($de_raw['data']) || $de_raw['data']['join_type'] || $de_raw['data']['lot_status']) {
// 防止异常
if (!array_key_exists('anchor', $data['data'])) {
return false;
}
// TODO
$de_raw = $data['data']['anchor'];
if (empty($de_raw)) {
return false;
}
// 无效抽奖
if ($de_raw['join_type'] || $de_raw['lot_status']) {
return false;
}
// 过滤抽奖范围
self::$filter_type = empty(self::$filter_type) ? explode(',', getenv('ANCHOR_TYPE')) : self::$filter_type;
if (!in_array((string)$de_raw['data']['require_type'], self::$filter_type)) {
if (!in_array((string)$de_raw['require_type'], self::$filter_type)) {
return false;
}
// 去重
if (self::toRepeatLid($de_raw['id'])) {
return false;
}
// 推入列表
$data = [
'room_id' => $de_raw['data']['room_id'],
'raffle_id' => $de_raw['data']['id'],
'prize' => $de_raw['data']['award_name'],
'wait' => strtotime(date("Y-m-d H:i:s"))
'room_id' => $room_id,
'raffle_id' => $de_raw['id'],
'raffle_name' => $de_raw['award_name'],
'wait' => time()
];
if (static::toRepeatLid($data['raffle_id'])) {
return false;
}
Statistics::addPushList(static::ACTIVE_TITLE);
array_push(static::$wait_list, $data);
Statistics::addPushList(self::ACTIVE_TITLE);
array_push(self::$wait_list, $data);
return true;
}
@ -84,11 +91,10 @@ class Anchor extends BaseRaffle
$de_raw = json_decode($raw, true);
if (isset($de_raw['code']) && $de_raw['code'] == 0) {
print_r($de_raw);
Statistics::addSuccessList(static::ACTIVE_TITLE);
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$data['prize']}");
Statistics::addSuccessList(self::ACTIVE_TITLE);
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$data['raffle_name']}");
} else {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$de_raw['message']}");
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['message']}");
}
return true;
}

135
src/plugin/AwardRecord.php Normal file
View File

@ -0,0 +1,135 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class AwardRecord
{
use TimeLock;
private static $raffle_lock = 0;
private static $raffle_list = [];
private static $anchor_lock = 0;
private static $anchor_list = [];
private static $gift_lock = 0;
private static $gift_list = [];
public static function run()
{
if (self::getLock() > time()) {
return;
}
if (self::$anchor_lock < time()) {
self::anchorAward();
}
if (self::$raffle_lock < time()) {
self::raffleAward();
}
// if (self::$gift_lock < time()) {
// self::giftAward();
// }
self::setLock(5 * 60);
}
/**
* @use 获取天选时刻中奖纪录
*/
private static function anchorAward()
{
$payload = [
'page' => '1',
];
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/Anchor/AwardRecord';
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
// 防止异常
if (!isset($de_raw['data']) || !isset($de_raw['data']['list'])) {
Log::warning("获取天选时刻获奖记录错误: " . json_encode($de_raw, JSON_FORCE_OBJECT));
self::$anchor_lock = time() + 1 * 60 * 60;
return;
}
foreach ($de_raw['data']['list'] as $anchor) {
$win_time = strtotime($anchor['end_time']); //礼物时间
$day = ceil((time() - $win_time) / 86400); //60s*60min*24h
// 去重
if (in_array($anchor['id'], self::$anchor_list)) {
continue;
}
// 范围
if ($day <= 2) {
$info = $anchor['award_name'] . 'x' . $anchor['award_num'];
Log::notice("天选时刻于" . $anchor['end_time'] . "获奖: {$info} ,请留意查看...");
Notice::push('anchor', $info);
}
array_push(self::$anchor_list, $anchor['id']);
}
self::$anchor_lock = time() + 6 * 60 * 60;
}
/**
* @use 获取实物抽奖中奖纪录
*/
private static function raffleAward()
{
$payload = [
'page' => '1',
'month' => '',
];
$url = 'https://api.live.bilibili.com/lottery/v1/award/award_list';
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
// 防止异常
if (!isset($de_raw['data']) || !isset($de_raw['data']['list']) || $de_raw['code']) {
Log::warning("获取实物奖励获奖记录错误: " . $de_raw['msg']);
self::$raffle_lock = time() + 1 * 60 * 60;
return;
}
foreach ($de_raw['data']['list'] as $raffle) {
$win_time = strtotime($raffle['create_time']); //礼物时间
$day = ceil((time() - $win_time) / 86400); //60s*60min*24h
// 去重
if (in_array($raffle['id'], self::$raffle_list)) {
continue;
}
// 范围
if ($day <= 2 && empty($raffle['update_time'])) {
$info = $raffle['gift_name'] . 'x' . $raffle['gift_num'];
Log::notice("实物奖励于" . $raffle['end_time'] . "获奖: {$info} ,请留意查看...");
Notice::push('raffle', $info);
}
array_push(self::$raffle_list, $raffle['id']);
}
self::$raffle_lock = time() + 6 * 60 * 60;
}
/**
* @use 获取活动礼物中奖纪录
*/
private static function giftAward()
{
$payload = [
'type' => 'type',
'raffleId' => 'raffle_id'
];
// Web V3 Notice
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/v3/smalltv/Notice';
// 请求 && 解码
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
}
}

View File

@ -8,15 +8,19 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
class Danmu
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Barrage
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time() || getenv('USE_DANMU') == 'false') {
if (self::getLock() > time() || getenv('USE_DANMU') == 'false') {
return;
}
$room_id = empty(getenv('DANMU_ROOMID')) ? Live::getUserRecommend() : Live::getRealRoomID(getenv('DANMU_ROOMID'));
@ -28,14 +32,18 @@ class Danmu
];
if (self::privateSendMsg($info)) {
self::$lock = time() + 3600;
self::setLock(3600);
return;
}
self::$lock = time() + 30;
self::setLock(30);
}
// 获取随机弹幕
/**
* @use 获取随机弹幕
* @return \Exception|false|mixed|string
*/
private static function getMsgInfo()
{
/**
@ -80,7 +88,11 @@ class Danmu
}
//发送弹幕通用模块
/**
* @use 弹幕通用模块
* @param $info
* @return bool|string
*/
private static function sendMsg($info)
{
$user_info = User::parseCookies();
@ -101,24 +113,26 @@ class Danmu
return Curl::post('https://api.live.bilibili.com/msg/send', Sign::api($payload));
}
//使用发送弹幕模块
/**
* @use 发送弹幕模块
* @param $info
* @return bool
*/
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('弹幕发送成功!');
Log::info('活跃弹幕发送成功!');
return true;
}
Log::error("活跃弹幕发送失败!, {$de_raw['msg']}");
Log::error("弹幕发送失败, {$de_raw['msg']}");
return false;
}
}

View File

@ -9,16 +9,16 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
abstract class BaseRaffle
{
const ACTIVE_TITLE = '';
const ACTIVE_SWITCH = '';
public static $lock;
public static $rw_lock;
protected static $wait_list;
protected static $finish_list;
protected static $all_list;
@ -28,29 +28,34 @@ abstract class BaseRaffle
if (getenv(static::ACTIVE_SWITCH) == 'false') {
return;
}
if (static::$lock > time()) {
if (static::getLock() > time()) {
return;
}
static::startLottery();
}
/**
* 抽奖逻辑
* @use 抽奖逻辑
* @return bool
*/
protected static function startLottery(): bool
{
$max_num = mt_rand(10, 20);
if (count(static::$wait_list) == 0) {
return false;
}
if (count(static::$wait_list) < 100) {
$max_num = mt_rand(10, 20);
} else {
$max_num = mt_rand(45, 90);
}
static::$wait_list = static::arrKeySort(static::$wait_list, 'wait');
for ($i = 0; $i <= $max_num; $i++) {
$raffle = array_shift(static::$wait_list);
if (is_null($raffle)) {
break;
}
if ($raffle['wait'] > strtotime(date("Y-m-d H:i:s"))) {
if ($raffle['wait'] > time()) {
array_push(static::$wait_list, $raffle);
continue;
}
@ -61,13 +66,34 @@ abstract class BaseRaffle
return true;
}
/**
* @use 返回抽奖数据
* @param int $room_id
* @return array
*/
protected static function check(int $room_id): array
{
$payload = [
'roomid' => $room_id
];
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/getLotteryInfo';
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
if (!isset($de_raw['data']) || $de_raw['code']) {
Log::error("获取抽奖数据错误,{$de_raw['message']}");
return [];
}
return $de_raw;
}
/**
* 检查抽奖列表
* @param $rid
* @use 解析抽奖数据
* @param int $room_id
* @param array $data
* @return bool
*/
abstract protected static function check($rid): bool;
abstract protected static function parse(int $room_id, array $data): bool;
/**
@ -100,12 +126,13 @@ abstract class BaseRaffle
/**
* 检测
* @param int $lid
* @use 重检测
* @param $lid
* @return bool
*/
protected static function toRepeatLid(int $lid): bool
protected static function toRepeatLid($lid): bool
{
$lid = (int)$lid;
if (in_array($lid, static::$all_list)) {
return true;
}
@ -113,12 +140,11 @@ abstract class BaseRaffle
static::$all_list = [];
}
array_push(static::$all_list, $lid);
return false;
}
/**
* 数据推入队列
* @use 数据推入队列
* @param array $data
* @return bool
*/
@ -131,9 +157,12 @@ abstract class BaseRaffle
if (Live::fishingDetection($data['rid'])) {
return false;
}
static::check($data['rid']);
$raffles_info = static::check($data['rid']);
if (!empty($raffles_info)) {
static::parse($data['rid'], $raffles_info);
}
$wait_num = count(static::$wait_list);
if ($wait_num > 2) {
if ($wait_num > 4 && ($wait_num % 2)) {
Log::info("当前队列中共有 {$wait_num}" . static::ACTIVE_TITLE . "待抽奖");
}
return true;

View File

@ -8,23 +8,29 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Daily
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
self::dailyBag();
self::$lock = time() + 3600;
self::setLock( 8 * 60 * 60);
}
protected static function dailyBag()
/**
* @use 领取每日包裹
*/
private static function dailyBag()
{
$payload = [];
$data = Curl::get('https://api.live.bilibili.com/gift/v2/live/receive_daily_bag', Sign::api($payload));

View File

@ -8,12 +8,17 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class DataTreating
{
// TODO 独立分发 Push||Pull数据
/**
* @desc 抽奖分发
* @use 抽奖分发
* @param array $data
*/
public static function distribute(array $data)
@ -28,19 +33,19 @@ class DataTreating
switch ($data['raffle_type']) {
case "storm":
// 风暴
Storm::pushToQueue($info);
StormRaffle::pushToQueue($info);
break;
case "raffle":
// 礼物
UnifyRaffle::pushToQueue($info);
GiftRaffle::pushToQueue($info);
break;
case "guard":
// 舰长
Guard::pushToQueue($info);
GuardRaffle::pushToQueue($info);
break;
case "small_tv":
// 电视
UnifyRaffle::pushToQueue($info);
GiftRaffle::pushToQueue($info);
break;
case 'pk':
// 乱斗
@ -48,7 +53,7 @@ class DataTreating
break;
case 'anchor':
// 天选时刻
Anchor::pushToQueue($info);
AnchorRaffle::pushToQueue($info);
break;
default:
break;

View File

@ -8,27 +8,34 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class GiftHeart
{
public static $lock = 0;
use TimeLock;
// RUN
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
if (self::giftheart()) {
self::$lock = time() + 60 * 60;
if (self::giftHeart()) {
self::setLock(60 * 60);
return;
}
self::$lock = time() + 5 * 60;
self::setLock(5 * 60);
}
// GIFT HEART
protected static function giftheart(): bool
/**
* @use 礼物心跳
* @return bool
*/
private static function giftHeart(): bool
{
$payload = [
'roomid' => getenv('ROOM_ID'),

102
src/plugin/GiftRaffle.php Normal file
View File

@ -0,0 +1,102 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class GiftRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '活动礼物';
const ACTIVE_SWITCH = 'USE_ACTIVE';
use TimeLock;
protected static $wait_list = [];
protected static $finish_list = [];
protected static $all_list = [];
/**
* @use 解析数据
* @param int $room_id
* @param array $data
* @return bool
*/
protected static function parse(int $room_id, array $data): bool
{
// 防止异常
if (!array_key_exists('gift_list', $data['data'])) {
return false;
}
$de_raw = $data['data']['gift_list'];
if (empty($de_raw)) {
return false;
}
foreach ($de_raw as $gift) {
// 无效抽奖
if ($gift['status'] != 1) {
continue;
}
// 去重
if (self::toRepeatLid($gift['raffleId'])) {
continue;
}
$data = [
'room_id' => $room_id,
'raffle_id' => $gift['raffleId'],
'title' => $gift['title'],
'type' => $gift['type'],
'wait' => $gift['time_wait'] + time(),
];
Statistics::addPushList(self::ACTIVE_TITLE);
array_push(self::$wait_list, $data);
}
return true;
}
/**
* @use 请求抽奖
* @param array $data
* @return bool
*/
protected static function lottery(array $data): bool
{
$user_info = User::parseCookies();
$payload = [
'raffleId' => $data['raffle_id'],
'roomid' => $data['room_id'],
'type' => $data['type'],
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => null,
];
// V3接口 暂做保留处理
// $url = 'https://api.live.bilibili.com/gift/v3/smalltv/join';
// $url = 'https://api.live.bilibili.com/xlive/lottery-interface/v5/smalltv/Join';
$url = 'https://api.live.bilibili.com/gift/v4/smalltv/getAward';
$raw = Curl::post($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
if (isset($de_raw['code']) && !$de_raw['code']) {
// 推送中奖信息
if ($de_raw['data']['gift_name'] != '辣条' && $de_raw['data']['gift_name'] != '') {
$info = $de_raw['data']['gift_name'] . 'x' . $de_raw['data']['gift_num'];
Notice::push('gift', $info);
}
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['data']['gift_name']}x{$de_raw['data']['gift_num']}");
Statistics::addSuccessList(self::ACTIVE_TITLE);
} else {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['msg']}");
}
return true;
}
}

275
src/plugin/GiftSend.php Normal file
View File

@ -0,0 +1,275 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class GiftSend
{
use TimeLock;
protected static $uid = 0;
protected static $tid = 0;
protected static $r_uid = 0;
protected static $room_id = 0;
protected static $room_list = [];
protected static $medal_list = [];
public static function run()
{
if (self::getLock() > time()) {
return;
}
if (!self::$uid) {
self::getUserInfo();
}
// 方案一未通过使用方案2
if (!self::procOne()) {
self::procTwo();
}
self::$room_list = [];
self::$medal_list = [];
self::$tid = 0;
self::setLock(5 * 60);
}
/**
* @use 方案1
*/
protected static function procOne()
{
if (!self::setTargetList()) {
return false;
}
self::getMedalList();
foreach (self::$medal_list as $key => $val) {
$bag_list = self::fetchBagList();
array_multisort(array_column($bag_list, "expire_at"), SORT_ASC, $bag_list);
if (getenv('FEED_FILL') == 'false') {
$bag_list = self::checkExpireGift($bag_list);
}
if (count($bag_list)) {
self::$tid = $key;
self::getRoomInfo();
} else {
break;
}
foreach ($bag_list as $gift) {
// 是辣条、亿元 && 不是过期礼物
if (!in_array($gift['gift_id'], [1, 6]) && getenv('FEED_FILL') != 'false') {
continue;
}
$amt = self::calcAmt($gift);
self::sendGift($gift, $amt);
$val -= $amt;
if (!$val) {
Log::notice("直播间 {$key} 亲密度 {$val} 送满啦~送满啦~");
break;
}
}
}
if (!count(self::$medal_list)) {
return false;
}
return true;
}
/**
* @use 方案2
*/
protected static function procTwo()
{
$bag_list = self::fetchBagList();
$expire_gift = self::checkExpireGift($bag_list);
if (count($expire_gift)) {
self::getRoomInfo();
foreach ($expire_gift as $gift) {
self::sendGift($gift, $gift['gift_num']);
}
}
}
/**
* @use 设置房间列表
* @return bool
*/
protected static function setTargetList(): bool
{
$temp = empty(getenv('ROOM_LIST')) ? null : getenv('ROOM_LIST');
if (is_null($temp)) return false;
self::$room_list = explode(',', getenv('ROOM_LIST'));
return true;
}
/**
* @use 获取背包列表
* @return array
*/
protected static function fetchBagList(): array
{
$bag_list = [];
$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']]);
return $bag_list;
}
if (isset($data['data']['list'])) {
$bag_list = $data['data']['list'];
array_multisort(array_column($bag_list, "gift_id"), SORT_DESC, $bag_list);
foreach ($bag_list as $vo) {
// 去除永久礼物
if ($vo['corner_mark'] == '永久') {
continue;
}
array_push($bag_list, $vo);
}
}
return $bag_list;
}
/**
* @use 查找过期礼物
* @param array $bag_list
* @return array
*/
protected static function checkExpireGift(array $bag_list): array
{
$expire_gift_list = [];
foreach ($bag_list as $gift) {
if ($gift['expire_at'] >= time() && $gift['expire_at'] <= time() + 3600) {
array_push($expire_gift_list, $gift);
}
}
return $expire_gift_list;
}
/**
* @use 获取勋章列表(过滤无勋章或已满)
*/
protected static function getMedalList()
{
self::$medal_list = [];
Log::info('正在获取勋章列表...');
$payload = [];
$data = Curl::get('https://api.live.bilibili.com/i/api/medal?page=1&pageSize=25', Sign::api($payload));
$data = json_decode($data, true);
if (isset($data['code']) && $data['code']) {
Log::warning('获取勋章列表失败!', ['msg' => $data['message']]);
return;
}
Log::info('勋章列表获取成功!');
if (isset($data['data']['fansMedalList'])) {
foreach ($data['data']['fansMedalList'] as $vo) {
if (in_array($vo['roomid'], self::$room_list) && ($vo['day_limit'] - $vo['today_feed'])) {
self::$medal_list[(string)$vo['roomid']] = ($vo['day_limit'] - $vo['today_feed']);
// $data = [
// $vo['roomid'] => ($vo['day_limit'] - $vo['today_feed'])
// ];
// array_push(self::$medal_list, $data);
}
}
}
}
/**
* @use 获取UID
*/
protected static function getUserInfo()
{
$payload = [];
$data = Curl::get('https://api.live.bilibili.com/xlive/web-ucenter/user/get_user_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;
}
self::$uid = $data['data']['uid'];
}
/**
* @use 获取直播间信息
*/
protected static function getRoomInfo()
{
Log::info('正在生成直播间信息...');
$payload = [
'id' => empty(self::$tid) ? getenv('ROOM_ID') : self::$tid,
];
$data = Curl::get('https://api.live.bilibili.com/room/v1/Room/room_init', 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::$r_uid = (string)$data['data']['uid'];
self::$room_id = (string)$data['data']['room_id'];
}
/**
* @use 计算赠送数量
* @param array $gift
* @return int
*/
protected static function calcAmt(array $gift): int
{
$amt = $gift['gift_num'];
if ($gift['gift_id'] == 1) {
$amt = (self::$medal_list[self::$room_id] > $gift['gift_num']) ? $gift['gift_num'] : self::$medal_list[self::$room_id];
}
if ($gift['gift_id'] == 6) {
$amt = (floor(self::$medal_list[self::$room_id] / 10) > $gift['gift_num']) ? $gift['gift_num'] : floor(self::$medal_list[self::$room_id] / 10);
}
return $amt;
}
/**
* @use 赠送礼物
* @param array $value
* @param int $amt
*/
protected static function sendGift(array $value, int $amt)
{
$payload = [
'coin_type' => 'silver',
'gift_id' => $value['gift_id'],
'ruid' => self::$r_uid,
'uid' => self::$uid,
'biz_id' => self::$room_id,
'gift_num' => $amt,
'data_source_id' => '',
'data_behavior_id' => '',
'bag_id' => $value['bag_id']
];
$data = Curl::post('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']} 投喂了 {$amt}{$value['gift_name']}");
}
}
}

View File

@ -8,22 +8,25 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class GroupSignIn
{
public static $lock = 0;
use TimeLock;
// RUN
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
$groups = self::getGroupList();
if (empty($groups)) {
self::$lock = time() + 24 * 60 * 60;
self::setLock(24 * 60 * 60);
return;
}
@ -31,10 +34,14 @@ class GroupSignIn
self::signInGroup($group);
}
self::$lock = time() + 8 * 60 * 60;
self::setLock(8 * 60 * 60);
}
//GROUP LIST
/**
* @use 获取友爱社列表
* @return array
*/
protected static function getGroupList(): array
{
$payload = [];
@ -48,7 +55,12 @@ class GroupSignIn
return $de_raw['data']['list'];
}
//SIGN IN
/**
* @use 签到
* @param array $groupInfo
* @return bool
*/
protected static function signInGroup(array $groupInfo): bool
{
$payload = [

113
src/plugin/GuardRaffle.php Normal file
View File

@ -0,0 +1,113 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class GuardRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '总督舰长';
const ACTIVE_SWITCH = 'USE_GUARD';
use TimeLock;
protected static $wait_list = [];
protected static $finish_list = [];
protected static $all_list = [];
/**
* @use 解析数据
* @param int $room_id
* @param array $data
* @return bool
*/
protected static function parse(int $room_id, array $data): bool
{
// 防止异常
if (!array_key_exists('guard', $data['data'])) {
return false;
}
$de_raw = $data['data']['guard'];
if (empty($de_raw)) {
return false;
}
foreach ($de_raw as $guard) {
// 无效抽奖
if ($guard['status'] != 1) {
continue;
}
// 去重
if (self::toRepeatLid($guard['id'])) {
continue;
}
// 获取等级名称
switch ($guard['privilege_type']) {
case 1:
$raffle_name = '总督';
break;
case 2:
$raffle_name = '提督';
break;
case 3:
$raffle_name = '舰长';
break;
default:
$raffle_name = '舰队';
break;
}
// 推入列表
$data = [
'room_id' => $room_id,
'raffle_id' => $guard['id'],
'raffle_name' => $raffle_name,
'wait' => time()
];
Statistics::addPushList(self::ACTIVE_TITLE);
array_push(self::$wait_list, $data);
}
return true;
}
/**
* @use 请求抽奖
* @param array $data
* @return bool
*/
protected static function lottery(array $data): bool
{
$user_info = User::parseCookies();
$payload = [
'id' => $data['raffle_id'],
'roomid' => $data['room_id'],
"type" => "guard",
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token']
];
$url = 'https://api.live.bilibili.com/lottery/v2/lottery/join';
$raw = Curl::post($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
if (isset($de_raw['code']) && $de_raw['code'] == 0) {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['data']['message']}");
Statistics::addSuccessList(self::ACTIVE_TITLE);
} else {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['msg']}");
}
return true;
}
}

View File

@ -8,24 +8,31 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Heart
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
self::pc();
self::mobile();
self::$lock = time() + 300;
self::setLock(5 * 60);
}
/**
* @use pc端心跳
*/
protected static function pc()
{
$payload = [
@ -41,6 +48,9 @@ class Heart
}
}
/**
* @use 手机端心跳
*/
protected static function mobile()
{
$payload = [

173
src/plugin/Live.php Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Live
{
use TimeLock;
public static function run()
{
return;
}
/**
* @use 获取分区列表
* @return array
*/
public static function fetchLiveAreas(): array
{
$areas = [];
$url = "http://api.live.bilibili.com/room/v1/Area/getList";
$raw = Curl::get($url);
$de_raw = json_decode($raw, true);
// 防止异常
if (!isset($de_raw['data']) || $de_raw['code']) {
Log::warning("获取直播分区异常: " . $de_raw['msg']);
$areas = range(1, 6);
} else {
foreach ($de_raw['data'] as $area) {
array_push($areas, $area['id']);
}
}
return $areas;
}
/**
* @use AREA_ID转ROOM_ID
* @param $area_id
* @return array
*/
public static function areaToRid($area_id): array
{
$url = "https://api.live.bilibili.com/room/v1/area/getRoomList?platform=web&parent_area_id={$area_id}&cate_id=0&area_id=0&sort_type=online&page=1&page_size=30";
$raw = Curl::get($url);
$de_raw = json_decode($raw, true);
// 防止异常
if (!isset($de_raw['data']) || $de_raw['code']) {
Log::warning("获取直播分区异常: " . $de_raw['msg']);
$area_info = [
'area_id' => $area_id,
'room_id' => 23058
];
} else {
$area_info = [
'area_id' => $area_id,
'room_id' => $de_raw['data'][0]['roomid']
];
}
return $area_info;
}
/**
* @use 获取随机直播房间号
* @return int
* @throws \Exception
*/
public static function getUserRecommend()
{
$raw = Curl::get('https://api.live.bilibili.com/room/v1/Area/getListByAreaID?areaId=0&sort=online&pageSize=30&page=1');
$de_raw = json_decode($raw, true);
if ($de_raw['code'] != '0') {
return 23058;
}
return $de_raw['data'][random_int(1, 29)]['roomid'];
}
/**
* @use 获取直播房间号
* @param $room_id
* @return bool
*/
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'];
}
/**
* @use 钓鱼检测
* @param $room_id
* @return bool
*/
public static function fishingDetection($room_id): bool
{
if (self::getRealRoomID($room_id)) {
return false;
}
return true;
}
/**
* @use 随机延迟
* @param int $min
* @param int $max
* @return bool
*/
public static function randDelay($min = 0, $max = 3): bool
{
$rand = $min + mt_rand() / mt_getrandmax() * ($max - $min);
sleep($rand);
return true;
}
/**
* @use 访问直播间
* @param $room_id
* @return bool
*/
public static function goToRoom($room_id): bool
{
$payload = [
'room_id' => $room_id,
];
// Log::info('进入直播间[' . $room_id . ']抽奖!');
Curl::post('https://api.live.bilibili.com/room/v1/Room/room_entry_action', Sign::api($payload));
return true;
}
/**
* @use 获取毫秒
* @return float
*/
public static function getMillisecond()
{
list($t1, $t2) = explode(' ', microtime());
return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
}
}

View File

@ -7,21 +7,29 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
use BiliHelper\Core\Config;
class Login
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::getLock()) {
self::check();
return;
}
Log::info('开始启动程序...');
if (getenv('ACCESS_TOKEN') == "") {
Log::info('令牌载入中...');
self::login();
}
// 重载配置文件
Index::$dotenv->overload();
Log::info('正在检查令牌合法性...');
if (!self::info()) {
@ -32,15 +40,19 @@ class Login
self::login();
}
}
self::$lock = time() + 3600;
self::setLock(3600);
}
/**
* @use 检查令牌
* @return bool
*/
public static function check()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return true;
}
self::$lock = time() + 7200;
self::setLock(7200);
if (!self::info()) {
Log::warning('令牌即将过期');
Log::info('申请更换令牌中...');
@ -53,6 +65,10 @@ class Login
return true;
}
/**
* @use 获取令牌信息
* @return bool
*/
protected static function info()
{
$access_token = getenv('ACCESS_TOKEN');
@ -69,6 +85,10 @@ class Login
return $data['data']['expires_in'] > 14400;
}
/**
* @use 刷新token
* @return bool
*/
public static function refresh()
{
$access_token = getenv('ACCESS_TOKEN');
@ -85,14 +105,19 @@ class Login
}
Log::info('令牌生成完毕!');
$access_token = $data['data']['access_token'];
File::writeNewEnvironmentFileWith('ACCESS_TOKEN', $access_token);
Config::put('ACCESS_TOKEN', $access_token);
Log::info(' > access token: ' . $access_token);
$refresh_token = $data['data']['refresh_token'];
File::writeNewEnvironmentFileWith('REFRESH_TOKEN', $refresh_token);
Config::put('REFRESH_TOKEN', $refresh_token);
Log::info(' > refresh token: ' . $refresh_token);
return true;
}
/**
* @use 普通登陆
* @param string $captcha
* @param array $headers
*/
protected static function login($captcha = '', $headers = [])
{
$user = getenv('APP_USER');
@ -129,7 +154,7 @@ class Login
$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_data = self::loginWithCaptcha();
$captcha = $captcha_data['captcha'];
$headers = $captcha_data['headers'];
continue;
@ -148,16 +173,20 @@ class Login
self::saveCookie($data);
Log::info('令牌获取成功!');
$access_token = $data['data']['token_info']['access_token'];
File::writeNewEnvironmentFileWith('ACCESS_TOKEN', $access_token);
Config::put('ACCESS_TOKEN', $access_token);
Log::info(' > access token: ' . $access_token);
$refresh_token = $data['data']['token_info']['refresh_token'];
File::writeNewEnvironmentFileWith('REFRESH_TOKEN', $refresh_token);
Config::put('REFRESH_TOKEN', $refresh_token);
Log::info(' > refresh token: ' . $refresh_token);
return;
}
/**
* @use 验证码登陆
* @return array
*/
protected static function loginWithCaptcha()
{
Log::info('登录需要验证, 启动验证码登录!');
@ -169,7 +198,7 @@ class Login
];
$data = Curl::other('https://passport.bilibili.com/captcha', null, $headers);
$data = base64_encode($data);
$captcha = static::ocrCaptcha($data);
$captcha = self::ocrCaptcha($data);
return [
'captcha' => $captcha,
'headers' => $headers,
@ -177,6 +206,11 @@ class Login
}
/**
* @use 识别验证码
* @param $captcha_img
* @return mixed
*/
private static function ocrCaptcha($captcha_img)
{
$payload = [
@ -192,6 +226,10 @@ class Login
return $de_raw['message'];
}
/**
* @use 保存cookie
* @param $data
*/
private static function saveCookie($data)
{
Log::info('COOKIE获取成功!');
@ -201,7 +239,7 @@ class Login
foreach ($cookies as $cookie) {
$temp .= $cookie['name'] . '=' . $cookie['value'] . ';';
}
File::writeNewEnvironmentFileWith('COOKIE', $temp);
Config::put('COOKIE', $temp);
Log::info(' > auth cookie: ' . $temp);
return;
}

View File

@ -8,25 +8,34 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class MasterSite
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time() || getenv('USE_MASTER_SITE') == 'false') {
if (self::getLock() > time() || getenv('USE_MASTER_SITE') == 'false') {
return;
}
if (self::watchAid() && self::shareAid() && self::coinAdd()) {
self::$lock = time() + 24 * 60 * 60;
self::setLock( 24 * 60 * 60);
return;
}
self::$lock = time() + 3600;
self::setLock( 3600);
}
// 投币
/**
* @use 投币
* @param $aid
* @return bool
*/
private static function reward($aid): bool
{
$user_info = User::parseCookies();
@ -54,7 +63,11 @@ class MasterSite
}
}
// 投币日志
/**
* @use 投币日志
* @return int
*/
protected static function coinLog(): int
{
$url = "https://api.bilibili.com/x/member/web/coin/log";
@ -87,7 +100,12 @@ class MasterSite
return $coins;
}
// 投币操作
/**
* @use 投币视频
* @return bool
* @throws \Exception
*/
protected static function coinAdd(): bool
{
switch (getenv('USE_ADD_COIN')) {
@ -123,7 +141,11 @@ class MasterSite
return true;
}
// 获取随机AID
/**
* @use 获取随机AID
* @return string
*/
private static function getRandomAid(): string
{
do {
@ -139,7 +161,13 @@ class MasterSite
return (string)$aid;
}
// 日榜AID
/**
* @use 获取日榜AID
* @param $num
* @return array
* @throws \Exception
*/
private static function getDayRankingAids($num): array
{
// day: 日榜1 三榜3 周榜7 月榜30
@ -166,7 +194,11 @@ class MasterSite
return $aids;
}
// 分享视频
/**
* @use 分享视频
* @return bool
*/
private static function shareAid(): bool
{
# aid = 稿件av号
@ -195,7 +227,11 @@ class MasterSite
}
}
// 观看视频
/**
* @use 观看视频
* @return bool
*/
private static function watchAid(): bool
{
$url = "https://api.bilibili.com/x/report/click/h5";
@ -259,7 +295,11 @@ class MasterSite
return false;
}
// 解析AID到CID
/**
* @use 解析AID到CID
* @return array
*/
private static function parseAid(): array
{
while (true) {

View File

@ -8,32 +8,33 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class MaterialObject
{
// 时间锁
public static $lock = 0;
// 丢弃列表
use TimeLock;
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()) {
if (self::getLock() > time()) {
return;
}
// 计算AID TODO 待优化
self::calculateAid(150, 550);
self::drawLottery();
self::$lock = time() + random_int(5, 10) * 60;
self::setLock(random_int(5, 10) * 60);
}
/**
@ -44,7 +45,7 @@ class MaterialObject
{
$block_key_list = ['测试', '加密', 'test', 'TEST', '钓鱼', '炸鱼', '调试'];
$flag = 5;
for ($i = self::$start_aid; $i < self::$end_aid; $i++) {
if (!$flag) {
break;
@ -125,11 +126,13 @@ class MaterialObject
return true;
}
/**
* @use 计算 开始结束的AID
* @use 计算开始结束的AID
* @param $min
* @param $max
* @return bool
* @throws \Exception
*/
private static function calculateAid($min, $max): bool
{

120
src/plugin/Notice.php Normal file
View File

@ -0,0 +1,120 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Notice
{
protected static $type = '';
protected static $result = '';
protected static $uname = '';
protected static $sckey = '';
/**
* @use 推送消息
* @param string $type
* @param string $result
*/
public static function push(string $type, string $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::sendInfoHandle();
}
/**
* @use 处理信息
* @return bool
*/
private static function sendInfoHandle(): bool
{
$now_time = date('Y-m-d H:i:s');
switch (self::$type) {
case 'anchor':
$info = [
'title' => '天选时刻获奖记录',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 在天选时刻中获得: ' . self::$result,
];
break;
case 'raffle':
$info = [
'title' => '实物奖励获奖纪录',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 在实物奖励中获得: ' . self::$result,
];
break;
case 'gift':
$info = [
'title' => '活动礼物获奖纪录',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 在活动礼物中获得: ' . self::$result,
];
break;
case 'storm':
$info = [
'title' => '节奏风暴获奖纪录',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 在节奏风暴中获得: ' . self::$result,
];
break;
case 'cookieRefresh':
$info = [
'title' => 'Cookie刷新',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 刷新Cookie: ' . self::$result,
];
break;
case 'todaySign':
$info = [
'title' => '每日签到',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 签到: ' . self::$result,
];
break;
case 'banned':
$info = [
'title' => '账号封禁',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 账号被封禁: 程序开始睡眠,凌晨自动唤醒,距离唤醒还有' . self::$result . '小时',
];
break;
default:
$info = [
'title' => '推送消息异常记录',
'content' => '[' . $now_time . ']' . ' 用户: ' . self::$uname . ' 推送消息key错误' . self::$type . self::$result,
];
break;
}
self::scSend($info);
return true;
}
/**
* @use ServerChan发送信息
* @param array $info
*/
private static function scSend(array $info)
{
$url = "https://sc.ftqq.com/" . self::$sckey . ".send?text=" . urlencode($info['title']) . "&desp=" . urlencode($info['content']);
$data = Curl::singleRequest('get', $url);
if (is_null($data)) {
Log::warning('Server酱推送信息失败,请检查!');
};
}
}

View File

@ -8,52 +8,60 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class PkRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '大乱斗';
const ACTIVE_SWITCH = 'USE_PK';
public static $lock = 0;
public static $rw_lock = 0;
use TimeLock;
protected static $wait_list = [];
protected static $finish_list = [];
protected static $all_list = [];
/**
* 检查抽奖列表
* @param $rid
* @use 解析数据
* @param int $room_id
* @param array $data
* @return bool
*/
protected static function check($rid): bool
protected static function parse(int $room_id, array $data): bool
{
$payload = [
'roomid' => $rid
];
$url = 'https://api.live.bilibili.com/xlive/lottery-interface/v1/pk/check';
$raw = Curl::get($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
// 计数 && 跳出
$total = count($de_raw['data']);
if (!$total) {
// 防止异常
if (!array_key_exists('pk', $data['data'])) {
return false;
}
$de_raw = $data['data']['pk'];
if (empty($de_raw)) {
return false;
}
for ($i = 0; $i < $total; $i++) {
$data = [
'raffle_id' => $de_raw['data'][$i]['pk_id'],
'title' => $de_raw['data'][$i]['title'],
'room_id' => $de_raw['data'][$i]['room_id'],
'wait' => strtotime(date("Y-m-d H:i:s"))
];
if (static::toRepeatLid($data['raffle_id'])) {
foreach ($de_raw as $pk) {
// 无效抽奖
if ($pk['status'] != 1) {
continue;
}
Statistics::addPushList(static::ACTIVE_TITLE);
array_push(static::$wait_list, $data);
// 去重
if (self::toRepeatLid($pk['id'])) {
continue;
}
// 推入列表
$data = [
'room_id' => $room_id,
'raffle_id' => $pk['id'],
'raffle_name' => '大乱斗',
'wait' => time()
];
Statistics::addPushList(self::ACTIVE_TITLE);
array_push(self::$wait_list, $data);
}
return true;
}
@ -83,10 +91,10 @@ class PkRaffle extends BaseRaffle
* {"code":-403,"data":null,"message":"访问被拒绝","msg":"访问被拒绝"}
*/
if (isset($de_raw['code']) && $de_raw['code'] == 0) {
Statistics::addSuccessList(static::ACTIVE_TITLE);
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$de_raw['data']['award_text']}");
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['data']['award_text']}");
Statistics::addSuccessList(self::ACTIVE_TITLE);
} else {
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . static::ACTIVE_TITLE . ": {$de_raw['message']}");
Log::notice("房间 {$data['room_id']} 编号 {$data['raffle_id']} " . self::ACTIVE_TITLE . ": {$de_raw['message']}");
}
return true;
}

View File

@ -8,10 +8,19 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Sign
{
/**
* @use 计算sign
* @param $payload
* @return array
*/
public static function api($payload)
{
# iOS 6680

View File

@ -8,61 +8,34 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Silver
{
public static $lock = 0;
use TimeLock;
protected static $task = [];
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
if (!empty(self::$task)) {
self::pushTask();
if (empty(self::$task)) {
self::getSilverBox();
} else {
self::pullTask();
self::openSilverBox();
}
}
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()
/**
* @use 获取宝箱
*/
private static function getSilverBox()
{
$payload = [];
$data = Curl::get('https://api.live.bilibili.com/lottery/v1/SilverBox/getCurrentTask', Sign::api($payload));
@ -70,7 +43,7 @@ class Silver
if (isset($data['code']) && $data['code'] == -10017) {
Log::notice($data['message']);
self::$lock = time() + 24 * 60 * 60;
self::setLock( 24 * 60 * 60);
return;
}
@ -86,7 +59,44 @@ class Silver
'time_start' => $data['data']['time_start'],
'time_end' => $data['data']['time_end'],
];
self::setLock( $data['data']['minute'] * 60 + 5);
}
self::$lock = time() + $data['data']['minute'] * 60 + 5;
/**
* @use 开启宝箱
*/
private static function openSilverBox()
{
$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::setLock( 12 * 60 * 60);
Log::warning("领取宝箱失败,{$data['message']}!");
return;
}
if ($data['code'] == -903) {
Log::warning("领取宝箱失败,{$data['message']}!");
self::$task = [];
self::setLock( 60);
return;
}
if (isset($data['code']) && $data['code']) {
Log::warning("领取宝箱失败,{$data['message']}!");
self::setLock( 60);
return;
}
Log::notice("领取宝箱成功Silver: {$data['data']['silver']}(+{$data['data']['awardSilver']})");
self::$task = [];
self::setLock( 10);
}
}

View File

@ -8,26 +8,33 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Silver2Coin
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time() || getenv('USE_SILVER2COIN') == 'false') {
if (self::getLock() > time() || getenv('USE_SILVER2COIN') == 'false') {
return;
}
if (self::appSilver2coin() && self::pcSilver2coin()) {
self::$lock = time() + 24 * 60 * 60;
self::setLock(24 * 60 * 60);
return;
}
self::$lock = time() + 3600;
self::setLock(3600);
}
// APP API
/**
* @use app兑换
* @return bool
*/
protected static function appSilver2coin(): bool
{
sleep(1);
@ -46,14 +53,18 @@ class Silver2Coin
return true;
}
// PC API
/**
* @use pc兑换
* @return bool
*/
protected static function pcSilver2coin(): bool
{
sleep(1);
$payload = [];
$url = "https://api.live.bilibili.com/exchange/silver2coin";
$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) {

80
src/plugin/Sleep.php Normal file
View File

@ -0,0 +1,80 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Sleep
{
use TimeLock;
// TODO 黑白名单 考虑添加到每个插件内部自动添加
private static $fillable = ['Login', 'Sleep', 'Daily', 'MasterSite', 'GiftSend', 'Task', 'Silver2Coin', 'GroupSignIn', 'GiftHeart', 'AwardRecord', 'Statistics'];
private static $guarded = ['Barrage', 'Heart', 'Silver', 'MaterialObject', 'AloneTcpClient', 'ZoneTcpClient', 'StormRaffle', 'GuardRaffle', 'PkRaffle', 'GiftRaffle', 'AnchorRaffle'];
private static $sleep_section = [];
public static function run()
{
if (getenv('USE_SLEEP') == 'false' || self::getLock() > time()) {
return;
}
self::isPause();
self::isRefuse();
self::setLock(5 * 60);
}
private static function isPause()
{
self::$sleep_section = empty(self::$filter_type) ? explode(',', getenv('SLEEP_SECTION')) : self::$sleep_section;
if (in_array(date('H'), self::$sleep_section)) {
$unlock_time = 60 * 60;
self::stopProc($unlock_time);
Log::warning('进入自定义休眠时间范围,暂停非必要任务,自动开启!');
}
return;
}
private static function isRefuse()
{
$payload = [];
$raw = Curl::get('https://api.live.bilibili.com/mobile/freeSilverAward', Sign::api($payload));
$de_raw = json_decode($raw, true);
if ($de_raw['msg'] == '访问被拒绝') {
$unlock_time = strtotime(date("Y-m-d", strtotime("+1 day", time()))) - time();
self::stopProc($unlock_time);
Log::warning('账号拒绝访问,暂停非必要任务,自动开启!');
// 推送被ban信息
Notice::push('banned', floor($unlock_time / 60 / 60));
}
return;
}
/**
* @use 停止运行
* @param int $unlock_time
*/
private static function stopProc(int $unlock_time)
{
$unlock_time = $unlock_time + 1 * 60;
// 检测时间提前5分钟
self::setLock($unlock_time - 5 * 60);
foreach (self::$fillable as $classname) {
Log::info("插件 {$classname} 白名单,保持当前状态继续");
}
foreach (self::$guarded as $classname) {
Log::info("插件 {$classname} 黑名单,锁定状态将于" . date("Y-m-d H:i", time() + $unlock_time) . "解除");
call_user_func(array(__NAMESPACE__ . '\\' . $classname, 'setLock'), $unlock_time);
}
}
}

View File

@ -8,32 +8,37 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Util\TimeLock;
class Statistics
{
public static $lock = 0;
use TimeLock;
// 推送 参加 成功
private static $push_list = [];
private static $join_list = [];
private static $success_list = [];
// 日志
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
self::getStormResult();
self::outputResult();
self::$lock = time() + 5 * 60;
self::setLock(5 * 60);
}
// 添加推送
/**
* @use 添加推送
* @param string $key
* @return bool
*/
public static function addPushList(string $key): bool
{
// 初始化三个必要值
@ -47,22 +52,36 @@ class Statistics
return true;
}
// 添加参与
/**
* @use 添加参与
* @param string $key
* @return bool
*/
public static function addJoinList(string $key): bool
{
array_push(self::$join_list[$key], 1);
return true;
}
// 添加成功
/**
* @use 添加成功
* @param string $key
* @return bool
*/
public static function addSuccessList(string $key): bool
{
array_push(self::$success_list[$key], 1);
return true;
}
// 获取结果
private static function getStormResult(): bool
/**
* @use 输出结果
* @return bool
*/
private static function outputResult(): bool
{
if (empty(self::$push_list)) {
return false;

141
src/plugin/StormRaffle.php Normal file
View File

@ -0,0 +1,141 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class StormRaffle extends BaseRaffle
{
const ACTIVE_TITLE = '节奏风暴';
const ACTIVE_SWITCH = 'USE_STORM';
use TimeLock;
protected static $wait_list = [];
protected static $finish_list = [];
protected static $all_list = [];
private static $drop_rate = null;
private static $attempt = null;
/**
* @use 解析数据
* @param int $room_id
* @param array $data
* @return bool
*/
protected static function parse(int $room_id, array $data): bool
{
// 防止异常
if (!array_key_exists('storm', $data['data'])) {
return false;
}
$de_raw = $data['data']['storm'];
if (empty($de_raw)) {
return false;
}
// 无效抽奖
if ($de_raw['hadJoin'] != 0) {
return false;
}
// 过滤抽奖范围
self::$drop_rate = getenv('STORM_DROPRATE') !== "" ? (int)getenv('STORM_DROPRATE') : 0;
if (mt_rand(1, 100) <= (int)self::$drop_rate) {
return false;
}
// 去重
if (self::toRepeatLid($de_raw['id'])) {
return false;
}
// 推入列表
$data = [
'room_id' => $room_id,
'raffle_id' => $de_raw['id'],
'raffle_name' => '节奏风暴',
'wait' => time()
];
Statistics::addPushList(self::ACTIVE_TITLE);
array_push(self::$wait_list, $data);
return true;
}
/**
* 格式化日志输出
* @param $id
* @param $num
* @param $info
* @return string
*/
private static function formatInfo($id, $num, $info): string
{
return "风暴 {$id} 请求 {$num} 状态 {$info}";
}
/**
* @use 请求抽奖
* @param array $data
* @return bool
* @throws \Exception
*/
protected static function lottery(array $data): bool
{
self::$attempt = getenv('STORM_ATTEMPT') !== "" ? explode(',', getenv('STORM_ATTEMPT')) : [30, 50];
$num = random_int((int)self::$attempt[0], (int)self::$attempt[1]);
$user_info = User::parseCookies();
$payload = [
'id' => $data['raffle_id'],
'roomid' => $data['room_id'],
"color" => "16772431",
"captcha_token" => "",
"captcha_phrase" => "",
"token" => $user_info['token'],
"csrf_token" => $user_info['token'],
"visit_id" => "",
];
$url = 'https://api.live.bilibili.com/lottery/v1/Storm/join';
for ($i = 1; $i < $num; $i++) {
$raw = Curl::post($url, Sign::api($payload));
$de_raw = json_decode($raw, true);
if ($de_raw['code'] == 429 || $de_raw['code'] == -429) {
Log::notice(self::formatInfo($data['raffle_id'], $num, '节奏风暴未实名或异常验证码'));
break;
}
if (isset($de_raw['data']) && empty($de_raw['data'])) {
Log::notice(self::formatInfo($data['raffle_id'], $num, '节奏风暴在小黑屋'));
break;
}
if ($de_raw['code'] == 0) {
Statistics::addSuccessList(self::ACTIVE_TITLE);
Log::notice(self::formatInfo($data['raffle_id'], $num, $de_raw['data']['mobile_content']));
break;
}
if ($de_raw['msg'] == '节奏风暴不存在') {
Log::notice(self::formatInfo($data['raffle_id'], $num, '节奏风暴已结束'));
break;
}
if ($de_raw['msg'] == '已经领取奖励') {
Log::notice(self::formatInfo($data['raffle_id'], $num, '节奏风暴已经领取'));
break;
}
if ($de_raw['msg'] == '你错过了奖励,下次要更快一点哦~') {
continue;
}
Log::notice(self::formatInfo($data['raffle_id'], $num, $de_raw['msg']));
continue;
}
return true;
}
}

View File

@ -8,22 +8,23 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
class Task
{
public static $lock = 0;
use TimeLock;
public static function run()
{
if (self::$lock > time()) {
if (self::getLock() > time()) {
return;
}
Log::info('正在检查每日任务...');
$data = self::check();
if (isset($data['data']['double_watch_info'])) {
self::double_watch_info($data['data']['double_watch_info']);
}
@ -31,15 +32,19 @@ class Task
self::sign_info($data['data']['sign_info']);
}
self::$lock = time() + 3600;
self::setLock(8 * 60 * 60);
}
protected static function check()
/**
* @use 检查每日任务
* @return bool|mixed|string
*/
private static function check()
{
$payload = [];
$data = Curl::get('https://api.live.bilibili.com/i/api/taskInfo', Sign::api($payload));
$data = json_decode($data, true);
Log::info('正在检查每日任务...');
if (isset($data['code']) && $data['code']) {
Log::warning('每日任务检查失败!', ['msg' => $data['message']]);
}
@ -47,7 +52,11 @@ class Task
return $data;
}
protected static function sign_info($info)
/**
* @use 每日签到
* @param $info
*/
private static function sign_info($info)
{
Log::info('检查任务「每日签到」...');
@ -65,11 +74,15 @@ class Task
} else {
Log::info('签到成功');
// 推送签到信息
Notice::run('todaySign', $data['message']);
Notice::push('todaySign', $data['message']);
}
}
protected static function double_watch_info($info)
/**
* @use 双端任务
* @param $info
*/
private static function double_watch_info($info)
{
Log::info('检查任务「双端观看直播」...');
@ -77,12 +90,10 @@ class Task
Log::notice('已经领取奖励');
return;
}
if ($info['mobile_watch'] != 1 || $info['web_watch'] != 1) {
Log::notice('任务未完成,请等待');
return;
}
$payload = [
'task_id' => 'double_watch_task',
];

View File

@ -8,16 +8,22 @@
* Updated: 2019 ~ 2020
*/
namespace lkeme\BiliHelper;
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
use BiliHelper\Core\Config;
class User
{
// RUN
public static function run()
{
}
// 实名检测
/**
* @use 实名检测
* @return bool
*/
public static function realNameCheck(): bool
{
$payload = [];
@ -30,7 +36,11 @@ class User
return true;
}
// 老爷检测
/**
* @use 是否是老爷
* @return bool
*/
public static function isMaster(): bool
{
$payload = [
@ -46,7 +56,11 @@ class User
return false;
}
// 用户名写入
/**
* @use 用户名写入
* @return bool
*/
public static function userInfo(): bool
{
$payload = [
@ -59,13 +73,17 @@ class User
return true;
}
if ($de_raw['msg'] == 'ok') {
File::writeNewEnvironmentFileWith('APP_UNAME', $de_raw['data']['uname']);
Config::put('APP_UNAME', $de_raw['data']['uname']);
return true;
}
return false;
}
//转换信息
/**
* @use 转换信息
* @return array
*/
public static function parseCookies(): array
{
$cookies = getenv('COOKIE');

View File

@ -0,0 +1,427 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Util\TimeLock;
use Exception;
use Socket\Raw\Factory;
class ZoneTcpClient
{
use TimeLock;
private static $raffle_id = 0;
private static $raffle_list = [];
private static $server_addr = null;
private static $server_key = null;
private static $area_id;
private static $room_id;
private static $client;
private static $client_maps = [];
private static $trigger_restart = [];
private static $socket_timeout = 0;
/**
* @use 入口
*/
public static function run()
{
if (self::getLock() > time() || getenv('USE_ZONE_SERVER') == 'false') {
return;
}
self::init();
self::updateConnection();
self::heartBeat();
self::receive();
self::pushHandle();
}
/**
* @use 初始化
*/
private static function init()
{
if (empty(getenv('ZONE_SERVER_ADDR'))) {
exit('推送服务器信息不完整, 请检查配置文件!');
}
if (!self::$server_addr) {
self::$server_addr = getenv('ZONE_SERVER_ADDR');
}
if (!self::$client) {
self::initConnect();
}
}
/**
* @use 初始化连接
*/
private static function initConnect()
{
$areas = Live::fetchLiveAreas();
foreach ($areas as $area_id) {
self::$client_maps["server{$area_id}"] = ["area_id" => null, "room_id" => null, "client" => null, "heart_beat" => 0];
self::triggerReConnect([
'area_id' => $area_id,
'wait_time' => time()
]);
}
}
/**
* @use 触发重连
* @param array $area_data
*/
private static function triggerReConnect(array $area_data)
{
array_push(self::$trigger_restart, $area_data);
}
/**
* @use 更新连接
*/
private static function updateConnection()
{
$num = count(self::$trigger_restart);
for ($i = 0; $i < $num; $i++) {
$area_data = array_shift(self::$trigger_restart);
if (is_null($area_data)) {
break;
}
if (time() < $area_data['wait_time']) {
array_push(self::$trigger_restart, $area_data);
continue;
}
Log::notice("update_connections triggered, info: {$area_data['area_id']}");
$area_info = Live::areaToRid($area_data['area_id']);
// $area_info = [
// 'area_id' => $area_id,
// 'room_id' => 23058
// ];
self::update($area_info);
}
}
/**
* @use 更新操作
* @param array $area_info
*/
private static function update(array $area_info)
{
self::$area_id = $area_info['area_id'];
self::$room_id = $area_info['room_id'];
try {
self::$client = (new Factory())->createClient(self::$server_addr, 40);
self::$client->setBlocking(false);
self::sendHandShake();
self::$client_maps["server" . self::$area_id]['client'] = self::$client;
self::$client_maps["server" . self::$area_id]['area_id'] = self::$area_id;
self::$client_maps["server" . self::$area_id]['room_id'] = self::$room_id;
self::$client_maps["server" . self::$area_id]['heart_beat'] = time() + 20;
Log::info("连接到 " . self::$client->getPeerName() . "#" . self::$area_id . " 推送服务器");
} catch (Exception $e) {
Log::error("连接到 #" . self::$area_id . " 推送服务器失败, {$e->getMessage()}");
self::triggerReConnect([
'area_id' => self::$area_id,
'wait_time' => time() + 60
]);
}
}
/**
* 判断字符串是否为 Json 格式
* @param string $data Json 字符串
* @param bool $assoc 是否返回对象or关联数组默认返回关联数组
* @return array|bool|object 成功返回转换后的对象或数组,失败返回 false
*/
private static function analyJson($data = '', $assoc = true)
{
$data = json_decode($data, $assoc);
if (($data && is_object($data)) || (is_array($data) && !empty($data))) {
return $data;
}
return false;
}
/**
* @use 响应数据
* @param $msg
* @return bool
*/
private static function onMessage($msg)
{
// 心跳后回复人气
if (strlen($msg) == 4) {
// $num = unpack('N', $msg)[1];
// Log::info("当前直播间现有 {$num} 人聚众搞基!");
return false;
}
$de_raw = self::analyJson($msg, true);
// 进入房间返回
if (isset($de_raw['code']) && !$de_raw['code']) {
return false;
}
// 部分cmd抽风
if (!$de_raw || !isset($de_raw['cmd'])) {
Log::warning("解析错误: {$msg}");
return false;
}
$data = [];
switch ($de_raw['cmd']) {
case 'NOTICE_MSG':
$msg_type = $de_raw['msg_type'];
$msg_self = $de_raw['msg_self'];
$msg_common = str_replace(' ', '', $de_raw['msg_common']);
$real_room_id = $de_raw['real_roomid'];
if (in_array($msg_type, [2, 8])) {
$data = [
'room_id' => $real_room_id,
'raffle_id' => self::$raffle_id++,
'raffle_title' => $msg_self,
'raffle_type' => 'raffle',
'source' => $msg
];
// echo self::$room_id . '--' . $real_room_id . PHP_EOL;
}
if ($msg_type == 6 && strpos($msg_common, '节奏风暴') !== false) {
$data = [
'room_id' => $real_room_id,
'raffle_id' => self::$raffle_id++,
'raffle_title' => '节奏风暴',
'raffle_type' => 'raffle',
'source' => $msg
];
}
break;
case 'GUARD_MSG':
$data = [
'room_id' => $de_raw['roomid'],
'raffle_id' => self::$raffle_id++,
'raffle_title' => '总督舰长',
'raffle_type' => 'raffle',
'source' => $msg
];
break;
case 'SPECIAL_GIFT':
if (array_key_exists('39', $de_raw['data'])) {
if ($de_raw['data']['39']['action'] == 'start') {
$data = [
'room_id' => $de_raw['roomid'],
'raffle_id' => self::$raffle_id++,
'raffle_title' => '节奏风暴',
'raffle_type' => 'raffle',
'source' => $msg
];
}
}
break;
case 'SYS_GIFT':
/**
* 系统礼物消息, 广播
*/
break;
case 'SYS_MSG':
/**
* 系统消息, 广播
*/
break;
// TODO 支持更多消息类型
default:
$data = [];
break;
}
if (!empty($data)) {
unset($data['source']);
if (!isset(self::$raffle_list[$data['raffle_type']])) {
self::$raffle_list[$data['raffle_type']] = [];
}
array_push(self::$raffle_list[$data['raffle_type']], $data);
// Log::info("获取到分区 #" . self::$area_id . "# {$data['source']}");
// print_r($data);
}
}
/**
* @推送到上游处理
*/
private static function pushHandle()
{
foreach (self::$raffle_list as $type => $data) {
$temp_room_id = 0;
foreach (self::$raffle_list[$type] as $raffle) {
if ($temp_room_id != $raffle['room_id']) {
DataTreating::distribute($raffle);
$temp_room_id = $raffle['room_id'];
}
}
}
self::$raffle_list = [];
}
/**
* @use 响应关闭
* @param $client
*/
private static function onClosed($client)
{
}
/**
* @use 发送握手包
* @return bool
*/
private static function sendHandShake()
{
return self::writer(self::genHandshakePkg(self::$room_id));
}
/**
* @use 心跳包
* @return string
*/
private static function genHeartBeatPkg(): string
{
return self::packMsg('', 0x0002);
}
/**
* @use 握手包
* @param $room_id
* @return string
*/
private static function genHandshakePkg($room_id): string
{
return self::packMsg(json_encode([
'uid' => mt_rand(1000000, 2999999),
'roomid' => intval($room_id),
]), 0x0007);
}
/**
* @use 打包数据
* @param $value
* @param $option
* @return string
*/
private static function packMsg($value, $option)
{
$head = pack('NnnNN', 0x10 + strlen($value), 0x10, 0x01, $option, 0x0001);
return $head . $value;
}
/**
* @use 解包数据
* @param $value
* @return int|mixed
*/
private static function unPackMsg($value)
{
$res = unpack('N', $value);
return $res[1] - 16;
}
/**
* @use 心跳
*/
private static function heartBeat()
{
foreach (self::$client_maps as $key => $client_info) {
if ($client_info['heart_beat'] > time()) {
continue;
}
self::$client = $client_info['client'];
self::$area_id = $client_info['area_id'];
self::$room_id = $client_info['room_id'];
self::writer(self::genHeartBeatPkg());
self::$client_maps[$key]['heart_beat'] = time() + 20;
}
}
/**
* @use 读数据
* @param $length
* @return array|bool|false
*/
private static function reader($length)
{
$data = false;
try {
while (self::$client->selectRead(self::$socket_timeout)) {
$data = self::$client->read($length);
if (!$data) {
throw new Exception("Connection failure");
}
if ($length == 16) $data = self::unPackMsg($data);
break;
}
} catch (Exception $exception) {
self::triggerReConnect([
'area_id' => self::$area_id,
'wait_time' => time() + 60
]);
}
return $data;
}
/**
* @use 写数据
* @param $data
* @return bool
*/
private static function writer($data)
{
$status = false;
try {
while (self::$client->selectWrite(self::$socket_timeout)) {
$status = self::$client->write($data);
break;
}
} catch (Exception $exception) {
self::triggerReConnect([
'area_id' => self::$area_id,
'wait_time' => time() + 60
]);
}
return $status;
}
/**
* @use 读取数据
*/
private static function receive()
{
foreach (self::$client_maps as $client_info) {
self::$client = $client_info['client'];
self::$area_id = $client_info['area_id'];
self::$room_id = $client_info['room_id'];
$len_body = self::reader(16);
if (!$len_body) {
// 长度为0 ,空信息
continue;
}
Log::debug("(len=$len_body)");
$body = self::reader($len_body);
self::onMessage($body);
}
}
}

38
src/util/TimeLock.php Normal file
View File

@ -0,0 +1,38 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2019 ~ 2020
*/
namespace BiliHelper\Util;
trait TimeLock
{
public static $lock = 0;
/**
* @use 设置时间
* @param int $lock
*/
public static function setLock(int $lock)
{
static::$lock = time() + $lock;
}
/**
* @use 获取时间
* @return int
*/
public static function getLock(): int
{
return static::$lock;
}
}