[update] XliveHeartBeat

This commit is contained in:
Lkeme 2021-04-23 14:01:00 +08:00
parent 2d50437b70
commit f6ad4fe262
11 changed files with 420 additions and 455 deletions

View File

@ -1,6 +1,23 @@
# Release Notes
# 本项目Log
## v0.8.1.210423 alpha (2021-04-23)
### Added
-
### Changed
- 更新天选敏感词
- 更新活动列表
- 优化直播间心跳
-
### Fixed
-
### Remarks
-
## v0.8.0.210327 alpha (2021-03-27)
### Added

4
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.8.0.210327 alpha-green.svg?longCache=true&style=for-the-badge">
<img src="https://img.shields.io/badge/version-0.8.1.210423 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,7 +17,7 @@ B 站直播实用脚本
|--------------------|--------------------|--------------------|
|Login |21.03.27 |账号登录 |
|Schedule |21.03.27 |休眠控制 |
|MainSite |21.03.27 |主站助手 |
|MainSite |21.03.27 |主站助手 |
|Daily |21.03.27 |每日礼包 |
|Heart |21.03.27 |双端心跳 |
|DailyTask |21.03.27 |每日任务 |

View File

@ -7,7 +7,7 @@ Group: [55308141](https://jq.qq.com/?_wv=1027&k=5AIDaJg) | **仅用于BUG提交
## 公告
> Personal Edition **0.8.0.210327 alpha**
> Personal Edition **0.8.1.210423 alpha**
```notice
---- 免费的东西总是得不到人的珍惜。

View File

@ -41,7 +41,7 @@
"follow": "false",
"share": "true",
"draw_times": 2,
"expire_at": "2021-04-15 11:59:59"
"expire_at": "2021-05-23 11:59:59"
},
{
"url": "https://www.bilibili.com/blackboard/activity-QEya2bouhQ.html",
@ -63,19 +63,30 @@
"follow": "false",
"share": "true",
"draw_times": 1,
"expire_at": "2021-04-30 23:59:59",
"expire_at": "2021-05-30 23:59:59",
"remarks": "时间不名,暂定一个月。"
},
{
"url": "https://www.bilibili.com/blackboard/activity-M22oqFLY1w.html",
"title": "最强搞事王",
"url": "https://www.bilibili.com/blackboard/activity-aT7zphkwF1.html",
"title": "初夏逛吃大会",
"description": "活动期间每天分享页面,新增一次机会。",
"sid": "e168f825-8be6-11eb-8597-246e966235d8",
"sid": "01aff78a-9e1a-11eb-8597-246e966235d8",
"login": "true",
"follow": "false",
"share": "true",
"draw_times": 1,
"expire_at": "2021-04-23 23:59:59"
"expire_at": "2021-06-10 23:59:59"
},
{
"url": "https://www.bilibili.com/blackboard/activity-oGiWFe0YTz.html",
"title": "京东超市超有young",
"description": "活动期间每天分享页面,新增一次机会。",
"sid": "4eb728e2-a0d7-11eb-8597-246e966235d8",
"login": "true",
"follow": "false",
"share": "true",
"draw_times": 1,
"expire_at": "2021-04-25 23:59:59"
}
]
}

View File

@ -34,23 +34,23 @@
"parent_area_id": 6,
"coin_id": 121,
"watch_time": 3720,
"draw_times": 9,
"draw_times": 2,
"expire_at": "2021-04-26 23:59:59"
},
{
"url": "https://live.bilibili.com/11218604",
"title": "PGI全球邀请赛",
"description": "每日在PGI直播间观看10分钟1张",
"title": "2021PCL春季赛观赛任务",
"description": "每日在PGI直播间观看10、15、30",
"room_id": 11218604,
"area_id": 0,
"parent_area_id": 2,
"coin_id": 91,
"coin_id": 129,
"watch_time": 3720,
"draw_times": 6,
"expire_at": "2021-03-28 23:59:59"
"draw_times": 3,
"expire_at": "2021-05-31 23:59:59"
},
{
"url": "https://live.bilibili.com/blackboard/2021-lpl-main.html",
"url": "https://live.bilibili.com/6",
"title": "2021LPL春季赛",
"description": "每日在LOL直播间观看5分钟1张15分钟1张",
"room_id": 7734200,
@ -59,19 +59,19 @@
"coin_id": 76,
"watch_time": 3720,
"draw_times": 3,
"expire_at": "2021-03-28 23:59:59"
"expire_at": "2021-05-31 23:59:59"
},
{
"url": "https://live.bilibili.com/23",
"title": "PEL和平精英职业联赛",
"title": "PEI亚洲邀请赛",
"description": "每日在PEL直播间观看5分钟1张",
"room_id": 13242892,
"area_id": 0,
"parent_area_id": 2,
"coin_id": 109,
"watch_time": 3720,
"draw_times": 6,
"expire_at": "2021-04-11 23:59:59"
"draw_times": 2,
"expire_at": "2021-05-31 23:59:59"
}
]
}

View File

@ -198,7 +198,106 @@
"1元红包",
"红包3.3元",
"5.2元红包",
"2.33元红包"
"2.33元红包",
"测试",
"钓鱼",
"炸鱼",
"黑屋",
"脚本",
"空气",
"大航海",
"上船",
"舰长",
"返现",
"抵用",
"代金",
"上车",
"上反船",
"照片",
"素颜",
"自拍",
"皂片",
"开舰",
"上舰",
"自画像",
"封面",
"取关",
"美照",
"随机照",
"女装照",
"日常照",
"好友",
"给主播",
"照骗",
"连麦",
"情书",
"一局",
"舰涨优惠卷",
"开黑",
"test",
"Test",
"金币",
"元宝",
"代打",
"上分",
"上段",
"台历",
"一毛",
"五毛",
"王者荣耀",
"玩游戏",
"encrypt",
"壁纸",
"相片",
"排位",
"语音",
"车位",
"网剧",
"一起玩",
"一次",
"专属头衔",
"手游",
"宠物",
"蒸汽",
"月饼",
"加速",
"挂件",
"渔夫",
"小黑屋",
"头像",
"许愿码",
"电池",
"赛车",
"保底",
"代币",
"越南盾",
"网点",
"机器",
"话梅",
"志愿",
"令牌",
"永久",
"第五人格",
"大蒜",
"唢呐",
"皇冠",
"徽章",
"铜牌",
"动物园",
"植物",
"钻石",
"宝石",
"尖叫",
"扭蛋机",
"点播",
"数字版月历",
"点歌一首",
"体验",
"点歌",
"三次取关",
"大航海",
"3块钱之前的巨款",
"礼金"
]
},
"CapsuleLottery": {

View File

@ -13,7 +13,7 @@ namespace BiliHelper\Core;
class Env
{
private $app_name = 'BiliHelper Personal';
private $app_version = '0.8.0.*';
private $app_version = '0.8.1.*';
/**
* Env constructor.

View File

@ -15,53 +15,37 @@ use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\AllotTasks;
use BiliHelper\Util\TimeLock;
use BiliHelper\Tool\Generator;
use BiliHelper\Util\XliveHeartBeat;
class CapsuleLottery
{
use TimeLock;
use AllotTasks;
use XliveHeartBeat;
private static $repository = APP_DATA_PATH . 'capsule_infos.json';
private static $work_room_id = null;
private static $enc_server = null; // 加密服务器 配置文件
private static $hb_payload = []; // 心跳请求数据
private static $hb_headers = []; // 心跳请求头
private static $hb_count_total = 0;
private static $hb_count = 0; // 心跳次数 max 24
private static $hb_room_info = []; // 心跳带勋章房间信息
private static $heartbeat_interval = 60; // 每次跳动时间
private static $interval = 60;
/**
* @throws \JsonDecodeStream\Exception\TokenizerException
* @throws \JsonDecodeStream\Exception\SelectorException
* @throws \JsonDecodeStream\Exception\ParserException
* @throws \JsonDecodeStream\Exception\CollectorException
*/
public static function run()
{
if (self::getLock() > time() || !self::init()) {
if (self::getLock() > time() || getenv('USE_CAPSULE') == 'false') {
return;
}
self::allotTasks();
if (self::workTask()) {
self::setLock(self::$heartbeat_interval);
self::setLock(self::$interval);
} else {
self::setLock(self::timing(5) + mt_rand(1, 180));
}
}
/**
* @use init
* @return bool
*/
private static function init(): bool
{
if (getenv('USE_CAPSULE') == 'false' || getenv('ENC_SERVER') == '') {
return false;
}
if (is_null(self::$enc_server)) {
self::$enc_server = getenv('ENC_SERVER');
}
return true;
}
/**
* @use 分配任务
@ -126,7 +110,8 @@ class CapsuleLottery
// 执行任务
switch ($task['operation']) {
case 'watch':
self::heartBeat($task['act']->room_id);
$interval = self::xliveHeartBeatTask($task['act']->room_id,999,999);
self::$interval = $interval == 0 ? 60 : $interval;
break;
case 'draw':
self::doLottery($task['act']->coin_id, $task['act']->url, 0);
@ -138,196 +123,6 @@ class CapsuleLottery
return true;
}
/**
* @use 重置变量
* @param false $reset_num
*/
private static function resetVar($reset_num = false)
{
self::$hb_payload = []; // 心跳请求数据
self::$hb_headers = []; // 心跳请求头
if ($reset_num) {
self::$hb_count_total = 0;
}
self::$hb_room_info = []; // 心跳带勋章房间信息
self::$hb_count = 0; // 心跳次数 max 24
self::$heartbeat_interval = 60; // 跳变时间
}
/**
* @use 心跳处理
* @param int $room_id
*/
private static function heartBeat(int $room_id)
{
if (self::$work_room_id != $room_id) {
self::resetVar();
self::$work_room_id = $room_id;
}
if (empty(self::$hb_room_info)) {
self::$hb_room_info = Live::webGetRoomInfo($room_id);
}
if (!self::$hb_count) {
$e_data = self::eHeartBeat(self::$hb_room_info['data']['room_info']);
if (!$e_data['status']) {
// 错误级别
return;
}
self::$hb_count += 1;
self::$hb_count_total += 1;
self::$hb_payload = $e_data['payload'];
self::$hb_headers = $e_data['headers'];
return;
}
$x_data = self::xHeartBeat(self::$hb_count);
if (!$x_data['status']) {
// 错误级别
self::resetVar();
return;
}
self::$hb_count_total += 1;
self::$hb_count += 1;
}
/**
* @use E心跳
* @param array $room_info
* @param int $index
* @return array|bool[]
*/
private static function eHeartBeat(array $room_info, $index = 0): array
{
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E';
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Origin' => 'https://live.bilibili.com',
'Referer' => 'https://live.bilibili.com/' . $room_info['room_id'],
];
$user_info = User::parseCookies();
$payload = [
'id' => json_encode([$room_info['parent_area_id'], $room_info['area_id'], $index, $room_info['room_id']], true),
'device' => json_encode([
Generator::hash(), Generator::uuid4()
], true),
'ts' => time() * 1000,
'is_patch' => 0,
'heart_beat' => [],
'ua' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
$raw = Curl::post('pc', $url, $payload, $headers);
$de_raw = json_decode($raw, true);
// {"code":0,"message":"0","ttl":1,"data":{"timestamp":1595342828,"heartbeat_interval":300,"secret_key":"seacasdgyijfhofiuxoannn","secret_rule":[2,5,1,4],"patch_status":2}}
if ($de_raw['code'] != 0) {
Log::warning("小心心礼物E-{$index}心跳失败");
return ['status' => false];
}
Log::info("小心心礼物E-{$index}心跳成功");
// Log::info($raw);
$payload['ets'] = $de_raw['data']['timestamp'];
$payload['secret_key'] = $de_raw['data']['secret_key'];
$payload['heartbeat_interval'] = $de_raw['data']['heartbeat_interval'];
$payload['secret_rule'] = $de_raw['data']['secret_rule'];
// 自动跳变时间
self::$heartbeat_interval = $de_raw['data']['heartbeat_interval'];
return [
'status' => true,
'payload' => $payload,
'headers' => $headers,
];
}
/**
* @use X心跳
* @param int $index
* @return array|bool[]
*/
private static function xHeartBeat(int $index = 1): array
{
$s_data = self::encParamS($index);
if (!$s_data) {
return ['status' => false];
}
$s = $s_data['s'];
$t = $s_data['payload'];
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X';
$user_info = User::parseCookies();
$payload = [
's' => $s,
'id' => $t['id'],
'device' => $t['device'],
'ets' => $t['ets'],
'benchmark' => $t['benchmark'],
'time' => $t['time'],
'ts' => $t['ts'],
'ua' => $t['ua'],
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
// print_r($payload);
$raw = Curl::post('pc', $url, $payload, self::$hb_headers);
$de_raw = json_decode($raw, true);
# {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":300,"timestamp":1595346846,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}}
if ($de_raw['code'] != 0) {
Log::warning("小心心礼物X-{$index}心跳失败");
return ['status' => false];
}
self::$hb_payload['ets'] = $de_raw['data']['timestamp'];
self::$hb_payload['secret_key'] = $de_raw['data']['secret_key'];
self::$hb_payload['heartbeat_interval'] = $de_raw['data']['heartbeat_interval'];
// 自动跳变时间
self::$heartbeat_interval = $de_raw['data']['heartbeat_interval'];
Log::info("小心心礼物X-{$index}心跳成功");
return ['status' => true];
}
/**
* @use 加密参数S
* @param int $index
* @return array|false
*/
private static function encParamS(int $index)
{
// 转换index
$temp = json_decode(self::$hb_payload['id'], true);
$temp[2] += 1;
self::$hb_payload['id'] = json_encode($temp, true);
// 加密部分
$payload = [
't' => [
'id' => self::$hb_payload['id'],
'device' => self::$hb_payload['device'],
'ets' => self::$hb_payload['ets'],
'benchmark' => self::$hb_payload['secret_key'],
'time' => self::$hb_payload['heartbeat_interval'],
'ts' => time() * 1000,
'ua' => self::$hb_payload['ua']
],
'r' => self::$hb_payload['secret_rule']
];
$headers = [
'Content-Type' => 'application/json',
];
$data = Curl::put('other', self::$enc_server, $payload, $headers);
$de_raw = json_decode($data, true);
if ($de_raw['code'] == 0) {
Log::info("S加密成功 {$de_raw['s']}");
return [
's' => $de_raw['s'],
'payload' => $payload['t']
];
} else {
Log::warning("S加密成功 {$de_raw['message']}");
return false;
}
}
/**
* @use 开始抽奖
* @param int $coin_id
@ -335,7 +130,7 @@ class CapsuleLottery
* @param int $num
* @return bool
*/
private static function doLottery(int $coin_id, string $referer, int $num)
private static function doLottery(int $coin_id, string $referer, int $num): bool
{
$url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/capsule/open_capsule_by_id';
$headers = [

View File

@ -390,7 +390,7 @@ class Live
'platform' => 'web',
'parent_area_id' => $parent_area_id,
'area_id' => $area_id,
'sort_type' => 'sort_type_282',
'sort_type' => 'online',
'page' => $page
];
$raw = Curl::get('other', $url, $payload);

View File

@ -11,34 +11,24 @@
namespace BiliHelper\Plugin;
use BiliHelper\Core\Log;
use BiliHelper\Core\Curl;
use BiliHelper\Util\TimeLock;
use BiliHelper\Tool\Generator;
use BiliHelper\Util\XliveHeartBeat;
class SmallHeart
{
use TimeLock;
use XliveHeartBeat;
private static $enc_server = null; // 加密服务器 配置文件
private static $hb_payload = []; // 心跳请求数据
private static $hb_headers = []; // 心跳请求头
private static $hb_count_total = 0;
private static $hb_count = 0; // 心跳次数 max 24
private static $hb_room_info = []; // 心跳带勋章房间信息
private static $fans_medals = []; // 全部勋章
private static $grey_fans_medals = []; // 灰色勋章
private static $metal_lock = 0; // 勋章时间锁
private static $heartbeat_interval = 60; // 每次跳动时间
private static $interval = 60; // 每次跳动时间
private static $total_time = 0;
public static function run()
{
if (!self::init()) {
if (getenv('USE_HEARTBEAT') == 'false') {
return;
}
@ -48,48 +38,16 @@ class SmallHeart
}
if (self::getLock() < time()) {
self::heartBeat();
if (self::$hb_count_total >= 200) {
self::resetVar(true);
if (self::$total_time >= 12000) {
self::$total_time = 0;
self::setLock(self::timing(2));
} else {
self::setLock(self::$heartbeat_interval);
self::setLock(self::$interval);
}
}
}
/**
* @use 重置变量
* @param false $reset_num
*/
private static function resetVar($reset_num = false)
{
self::$hb_payload = []; // 心跳请求数据
self::$hb_headers = []; // 心跳请求头
if ($reset_num) {
self::$hb_count_total = 0;
}
self::$hb_room_info = []; // 心跳带勋章房间信息
self::$hb_count = 0; // 心跳次数 max 24
self::$heartbeat_interval = 60; // 跳变时间
}
/**
* @use init
* @return bool
*/
private static function init(): bool
{
if (getenv('USE_HEARTBEAT') == 'false' || getenv('ENC_SERVER') == '') {
return false;
}
if (is_null(self::$enc_server)) {
self::$enc_server = getenv('ENC_SERVER');
}
return true;
}
/**
* @use 勋章处理
*/
@ -115,8 +73,6 @@ class SmallHeart
Live::sendGift($grey_fans_medal, $gift, 1);
}
}
}
@ -128,168 +84,12 @@ class SmallHeart
if (empty(self::$fans_medals)) {
return;
}
if (empty(self::$hb_room_info)) {
$metal = self::$fans_medals[array_rand(self::$fans_medals)];
self::$hb_room_info = Live::webGetRoomInfo($metal['roomid']);
}
if (self::$hb_count == 0) {
$e_data = self::eHeartBeat(self::$hb_room_info['data']['room_info']);
if (!$e_data['status']) {
// 错误级别
return;
}
self::$hb_count += 1;
self::$hb_count_total +=1;
self::$hb_payload = $e_data['payload'];
self::$hb_headers = $e_data['headers'];
return;
}
$x_data = self::xHeartBeat(self::$hb_count);
if (!$x_data['status']) {
// 错误级别
self::resetVar();
return;
}
self::$hb_count_total +=1;
self::$hb_count += 1;
}
/**
* @use E心跳
* @param array $room_info
* @param int $index
* @return array|bool[]
*/
private static function eHeartBeat(array $room_info, $index = 0): array
{
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E';
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Origin' => 'https://live.bilibili.com',
'Referer' => 'https://live.bilibili.com/' . $room_info['room_id'],
];
$user_info = User::parseCookies();
$payload = [
'id' => json_encode([$room_info['parent_area_id'], $room_info['area_id'], $index, $room_info['room_id']], true),
'device' => json_encode([
Generator::hash(), Generator::uuid4()
], true),
'ts' => time() * 1000,
'is_patch' => 0,
'heart_beat' => [],
'ua' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
$raw = Curl::post('pc', $url, $payload, $headers);
$de_raw = json_decode($raw, true);
// {"code":0,"message":"0","ttl":1,"data":{"timestamp":1595342828,"heartbeat_interval":300,"secret_key":"seacasdgyijfhofiuxoannn","secret_rule":[2,5,1,4],"patch_status":2}}
if ($de_raw['code'] != 0) {
Log::warning("小心心礼物E-{$index}心跳失败");
return ['status' => false];
}
Log::info("小心心礼物E-{$index}心跳成功");
// Log::info($raw);
$payload['ets'] = $de_raw['data']['timestamp'];
$payload['secret_key'] = $de_raw['data']['secret_key'];
$payload['heartbeat_interval'] = $de_raw['data']['heartbeat_interval'];
$payload['secret_rule'] = $de_raw['data']['secret_rule'];
// 自动跳变时间
self::$heartbeat_interval = $de_raw['data']['heartbeat_interval'];
return [
'status' => true,
'payload' => $payload,
'headers' => $headers,
];
}
/**
* @use X心跳
* @param int $index
* @return array|bool[]
*/
private static function xHeartBeat(int $index = 1): array
{
$s_data = self::encParamS($index);
if (!$s_data) {
return ['status' => false];
}
$s = $s_data['s'];
$t = $s_data['payload'];
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X';
$user_info = User::parseCookies();
$payload = [
's' => $s,
'id' => $t['id'],
'device' => $t['device'],
'ets' => $t['ets'],
'benchmark' => $t['benchmark'],
'time' => $t['time'],
'ts' => $t['ts'],
'ua' => $t['ua'],
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
// print_r($payload);
$raw = Curl::post('pc', $url, $payload, self::$hb_headers);
$de_raw = json_decode($raw, true);
# {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":300,"timestamp":1595346846,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}}
if ($de_raw['code'] != 0) {
Log::warning("小心心礼物X-{$index}心跳失败");
return ['status' => false];
}
self::$hb_payload['ets'] = $de_raw['data']['timestamp'];
self::$hb_payload['secret_key'] = $de_raw['data']['secret_key'];
self::$hb_payload['heartbeat_interval'] = $de_raw['data']['heartbeat_interval'];
// 自动跳变时间
self::$heartbeat_interval = $de_raw['data']['heartbeat_interval'];
Log::info("小心心礼物X-{$index}心跳成功");
return ['status' => true];
}
/**
* @use 加密参数S
* @param int $index
* @return array|false
*/
private static function encParamS(int $index)
{
// 转换index
$temp = json_decode(self::$hb_payload['id'], true);
$temp[2] += 1;
self::$hb_payload['id'] = json_encode($temp, true);
// 加密部分
$payload = [
't' => [
'id' => self::$hb_payload['id'],
'device' => self::$hb_payload['device'],
'ets' => self::$hb_payload['ets'],
'benchmark' => self::$hb_payload['secret_key'],
'time' => self::$hb_payload['heartbeat_interval'],
'ts' => time() * 1000,
'ua' => self::$hb_payload['ua']
],
'r' => self::$hb_payload['secret_rule']
];
$headers = [
'Content-Type' => 'application/json',
];
$data = Curl::put('other', self::$enc_server, $payload, $headers);
$de_raw = json_decode($data, true);
if ($de_raw['code'] == 0) {
Log::info("S加密成功 {$de_raw['s']}");
return [
's' => $de_raw['s'],
'payload' => $payload['t']
];
} else {
Log::warning("S加密成功 {$de_raw['message']}");
return false;
$metal = self::$fans_medals[array_rand(self::$fans_medals)];
$interval = self::xliveHeartBeatTask($metal['roomid'], 999, 999);
if ($interval != 0) {
self::$total_time += $interval;
}
self::$interval = $interval == 0 ? 60 : $interval;
}

243
src/util/XliveHeartBeat.php Normal file
View File

@ -0,0 +1,243 @@
<?php
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2021 ~ 2022
*/
namespace BiliHelper\Util;
use BiliHelper\Core\Curl;
use BiliHelper\Core\Log;
use BiliHelper\Plugin\Live;
use BiliHelper\Plugin\User;
use BiliHelper\Tool\Generator;
trait XliveHeartBeat
{
protected static $_data = ['id' => []]; // data [ets, benchmark, time, secret_rule, id] data->id [parent_area_id, area_id, 0, room_id]
protected static $_secret_rule = []; // secret_rule [2, 3, 1, 5]
protected static $_room_info = []; // 心跳房间信息
protected static $_retry = 3; // 重试次数
protected static $_count_num = 0; // 计数
protected static $_count_time = 0; // 计时间
protected static $_current_room_id = 0; // 当前运行的ROOM_ID
protected static $_enc_server = null; // 加密服务器 依赖配置文件
protected static $_default = 0; // 默认值
/**
* @use 重置变量
* @param false $force
*/
protected static function resetVar($force = false)
{
if ($force){
self::$_room_info = [];
self::$_current_room_id = 0;
self::$_retry = 3;
self::$_count_num = 0;
self::$_count_time = 0;
}
$data = [
'id' => self::$_data['id'],
];
$data["id"][2] = 0;
self::$_data = $data;
}
protected static function xliveHeartBeatTask(int $room_id, int $max_time, int $max_num)
{
// 加载依赖
if (!self::depend()) {
return self::$_default;
}
// 对比当前运行
if (self::$_current_room_id != $room_id) {
self::resetVar(true);
self::$_current_room_id = $room_id;
}
// 加载房间信息
if (empty(self::$_room_info)) {
$r_data = Live::webGetRoomInfo($room_id);
if ($r_data['code'] != 0) {
Log::warning('直播间信息获取失败');
return self::$_default;
}
$rdata = $r_data['data'];
$parent_area_id = $rdata['room_info']['parent_area_id'];
$area_id = $rdata['room_info']['area_id'];
# 短位转长位
$room_id = $rdata['room_info']['room_id'];
self::$_data['id'] = [$parent_area_id, $area_id, 0, $room_id];
}
// 执行心跳
$r_data = self::heartBeatIterator();
$index = self::$_data['id'][2];
if ($r_data['code'] != 0) {
if (self::$_retry) {
Log::warning("心跳失败-{$index} {$r_data['message']}");
self::resetVar();
self::$_retry -= 1;
return self::$_default;
}
}
self::$_count_num += 1;
self::$_count_time += $r_data['heartbeat_interval'];
// 最大次数限制
if ($max_num <= self::$_count_num) {
// 成功在id为{room_id}的直播间发送完{ii}次心跳,退出直播心跳(达到最大心跳次数)
}
// 最大时间限制
if ($max_time <= self::$_count_time) {
//成功在id为{room_id}的直播间发送第{ii}次心跳
}
$minute = round(self::$_count_time / 60);
Log::info("已在直播间 {$room_id} 连续观看了 {$minute} 分钟");
return $r_data['heartbeat_interval'];
}
/**
* @use 检查依赖
* @return bool
*/
protected static function depend(): bool
{
if (getenv('ENC_SERVER') == '') {
return false;
}
// 加载加密服务器
if (is_null(self::$_enc_server)) {
self::$_enc_server = getenv('ENC_SERVER');
}
return true;
}
protected static function heartBeatIterator(): array
{
$rdata = [];
# 第1次执行 eHeartBeat
if (self::$_data['id'][2] == 0) {
$r_data = self::eHeartBeat(self::$_data['id']);
} else {
# 第1次之后执行 xHeartBeat
self::$_data['ts'] = time() * 1000;
self::$_data['s'] = self::encParamS(self::$_data, self::$_secret_rule);
$r_data = self::xHeartBeat(self::$_data['id']);
}
if ($r_data['code'] == 0) {
$rdata = $r_data['data'];
self::$_data['ets'] = $rdata['timestamp'];
self::$_data['benchmark'] = $rdata['secret_key'];
self::$_data['time'] = $rdata['heartbeat_interval'];
self::$_secret_rule = $rdata['secret_rule'];
self::$_data['id'][2] += 1;
}
return [
'code' => $r_data['code'],
'message' => $r_data['message'],
'heartbeat_interval' => $rdata['heartbeat_interval']
];
}
/**
* @use E心跳
* @param array $id
* @return array|false[]
*/
protected static function eHeartBeat(array $id): array
{
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E';
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Origin' => 'https://live.bilibili.com',
'Referer' => 'https://live.bilibili.com/' . $id[3],
];
$user_info = User::parseCookies();
$payload = [
'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
'device' => json_encode([Generator::hash(), Generator::uuid4()], true),
'ts' => time() * 1000,
'is_patch' => 0,
'heart_beat' => [],
'ua' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
$raw = Curl::post('pc', $url, $payload, $headers);
// {'code':0,'message':'0','ttl':1,'data':{'timestamp':1595342828,'heartbeat_interval':300,'secret_key':'seacasdgyijfhofiuxoannn','secret_rule':[2,5,1,4],'patch_status':2}}
unset($payload['id']);
self::$_data = array_merge_recursive(self::$_data, $payload);
return json_decode($raw, true);
}
/**
* @use X心跳
* @param array $id
* @return array|bool[]
*/
protected static function xHeartBeat(array $id): array
{
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X';
$user_info = User::parseCookies();
$payload = [
's' => self::$_data['s'],
'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
'device' => self::$_data['device'],
'ets' => self::$_data['ets'],
'benchmark' => self::$_data['benchmark'],
'time' => self::$_data['time'],
'ts' => self::$_data['ts'],
'ua' => self::$_data['ua'],
'csrf_token' => $user_info['token'],
'csrf' => $user_info['token'],
'visit_id' => ''
];
// print_r($payload);
$raw = Curl::post('pc', $url, $payload, self::$hb_headers);
# {'code':0,'message':'0','ttl':1,'data':{'heartbeat_interval':300,'timestamp':1595346846,'secret_rule':[2,5,1,4],'secret_key':'seacasdgyijfhofiuxoannn'}}
return json_decode($raw, true);
}
/**
* @use 加密参数S
* @param array $t
* @param array $r
* @return string|false
*/
protected static function encParamS(array $t, array $r)
{
$headers = [
'Content-Type' => 'application/json',
];
// 加密部分
$payload = ['t' => $t, 'r' => $r];
$data = Curl::put('other', self::$_enc_server, $payload, $headers);
$de_raw = json_decode($data, true);
if ($de_raw['code'] == 0) {
// Log::info("S加密成功 {$de_raw['s']}");
return $de_raw['s'];
} else {
Log::warning("S加密失败 {$de_raw['message']}");
return false;
}
}
}