mirror of
https://github.com/lkeme/BiliHelper-personal.git
synced 2025-12-19 01:20:08 +08:00
[fix] XliveHeartBeat
This commit is contained in:
parent
3a2dfff7c9
commit
4792167bff
@ -134,13 +134,13 @@ class Barrage
|
||||
*/
|
||||
private static function privateSendMsg($info): bool
|
||||
{
|
||||
//TODO 短期功能 有需求就修改
|
||||
//TODO 短期功能 有需求就修改 // todo 加入直播间
|
||||
$response = self::sendMsg($info);
|
||||
if (isset($response['code']) && $response['code'] == 0) {
|
||||
Log::info('弹幕发送成功');
|
||||
return true;
|
||||
} else {
|
||||
Log::warning("弹幕发送失败, CODE -> {$response['code']} MSG -> {$response['msg']} ");
|
||||
Log::warning("弹幕发送失败, CODE -> {$response['code']} MSG -> {$response} ");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,18 @@ namespace BiliHelper\Tool;
|
||||
|
||||
class Common
|
||||
{
|
||||
|
||||
/**
|
||||
* @use 获取十三位时间戳
|
||||
* @return int
|
||||
*/
|
||||
public static function getMillisecond(): int
|
||||
{
|
||||
list($t1, $t2) = explode(' ', microtime());
|
||||
// return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
|
||||
return intval(sprintf('%u', (floatval($t1) + floatval($t2)) * 1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 替换字符串
|
||||
* @param $str
|
||||
@ -21,7 +33,7 @@ class Common
|
||||
* @param string $charset
|
||||
* @return string
|
||||
*/
|
||||
public static function replaceStar($str, $start, $end = 0, $dot = "*", $charset = "UTF-8")
|
||||
public static function replaceStar($str, $start, $end = 0, $dot = "*", $charset = "UTF-8"): string
|
||||
{
|
||||
$len = mb_strlen($str, $charset);
|
||||
if ($start == 0 || $start > $len) {
|
||||
|
||||
@ -32,27 +32,14 @@ trait XliveHeartBeat
|
||||
|
||||
protected static $_default = 0; // 默认值
|
||||
|
||||
|
||||
/**
|
||||
* @use 重置变量
|
||||
* @param false $force
|
||||
*/
|
||||
protected static function resetVar($force = false)
|
||||
{
|
||||
if ($force) {
|
||||
static::$_room_info = [];
|
||||
static::$_current_room_id = 0;
|
||||
|
||||
static::$_retry = 3;
|
||||
static::$_count_num = 0;
|
||||
static::$_count_time = 0;
|
||||
}
|
||||
$data = [
|
||||
'id' => static::$_data['id'],
|
||||
];
|
||||
$data["id"][2] = 0;
|
||||
static::$_data = $data;
|
||||
}
|
||||
// 请求配置
|
||||
protected static $_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36';
|
||||
protected static $_headers = [
|
||||
'content-type' => 'application/x-www-form-urlencoded',
|
||||
'origin' => 'https://live.bilibili.com',
|
||||
'referer' => 'https://live.bilibili.com/',
|
||||
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
@ -92,28 +79,214 @@ trait XliveHeartBeat
|
||||
$r_data = static::heartBeatIterator();
|
||||
$index = static::$_data['id'][2];
|
||||
if ($r_data['code'] != 0) {
|
||||
Log::warning("心跳失败-$index {$r_data['message']}");
|
||||
// 失败心跳
|
||||
if (static::$_retry) {
|
||||
Log::warning("心跳失败-$index {$r_data['message']}");
|
||||
// 重试次数 > 1 , 不全部清除
|
||||
static::resetVar();
|
||||
static::$_retry -= 1;
|
||||
return static::$_default;
|
||||
} else {
|
||||
// 重试次数 < 1 , 全部清除
|
||||
static::resetVar(true);
|
||||
}
|
||||
}
|
||||
static::$_count_num += 1;
|
||||
static::$_count_time += $r_data['heartbeat_interval'];
|
||||
return static::$_default;
|
||||
} else {
|
||||
// 成功心跳
|
||||
static::$_count_num += 1;
|
||||
static::$_count_time += $r_data['heartbeat_interval'];
|
||||
|
||||
// 最大次数限制
|
||||
if ($max_num <= static::$_count_num) {
|
||||
// 成功在id为{room_id}的直播间发送完{ii}次心跳,退出直播心跳(达到最大心跳次数)
|
||||
// 最大次数限制
|
||||
if ($max_num <= static::$_count_num) {
|
||||
// 成功在id为{room_id}的直播间发送完{ii}次心跳,退出直播心跳(达到最大心跳次数)
|
||||
}
|
||||
// 最大时间限制
|
||||
if ($max_time <= static::$_count_time) {
|
||||
//成功在id为{room_id}的直播间发送第{ii}次心跳
|
||||
}
|
||||
$minute = round(static::$_count_time / 60);
|
||||
Log::info("已在直播间 $room_id 连续观看了 $minute 分钟");
|
||||
return $r_data['heartbeat_interval'];
|
||||
}
|
||||
// 最大时间限制
|
||||
if ($max_time <= static::$_count_time) {
|
||||
//成功在id为{room_id}的直播间发送第{ii}次心跳
|
||||
}
|
||||
$minute = round(static::$_count_time / 60);
|
||||
Log::info("已在直播间 $room_id 连续观看了 $minute 分钟");
|
||||
return $r_data['heartbeat_interval'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 心跳迭代
|
||||
* @return array
|
||||
*/
|
||||
protected static function heartBeatIterator(): array
|
||||
{
|
||||
// print_r(static::$_data);
|
||||
$rdata = [];
|
||||
# 第1次执行 eHeartBeat
|
||||
if (static::$_data['id'][2] == 0) {
|
||||
$r_data = static::eHeartBeat(static::$_data['id']);
|
||||
} else {
|
||||
# 第1次之后执行 xHeartBeat
|
||||
static::$_data['ts'] = time() * 1000;
|
||||
static::$_data['s'] = static::encParamS(static::$_data, static::$_secret_rule);
|
||||
if (!static::$_data['s']) {
|
||||
return [404, '心跳加密错误', '心跳加密错误'];
|
||||
}
|
||||
$r_data = static::xHeartBeat(static::$_data['id']);
|
||||
}
|
||||
if ($r_data['code'] == 0) {
|
||||
$rdata = $r_data['data'];
|
||||
static::$_data['ets'] = $rdata['timestamp'];
|
||||
static::$_data['benchmark'] = $rdata['secret_key'];
|
||||
static::$_data['time'] = $rdata['heartbeat_interval'];
|
||||
static::$_secret_rule = $rdata['secret_rule'];
|
||||
static::$_data['id'][2] += 1;
|
||||
}
|
||||
Log::debug(json_encode(static::$_data['id'], true));
|
||||
return [
|
||||
'code' => $r_data['code'],
|
||||
'message' => $r_data['message'],
|
||||
'heartbeat_interval' => array_key_exists('heartbeat_interval', $rdata) ? $rdata['heartbeat_interval'] : static::$_default
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @use E心跳
|
||||
* @param array $id
|
||||
* @return array|false[]
|
||||
*/
|
||||
protected static function eHeartBeat(array $id): array
|
||||
{
|
||||
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E';
|
||||
$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' => static::$_user_agent,
|
||||
'csrf_token' => $user_info['token'],
|
||||
'csrf' => $user_info['token'],
|
||||
'visit_id' => ''
|
||||
];
|
||||
// print_r($payload);
|
||||
Log::debug(json_encode($payload, true));
|
||||
$raw = Curl::post('pc', $url, $payload, static::$_headers);
|
||||
// {'code':0,'message':'0','ttl':1,'data':{'timestamp':1595342828,'heartbeat_interval':300,'secret_key':'seacasdgyijfhofiuxoannn','secret_rule':[2,5,1,4],'patch_status':2}}
|
||||
|
||||
unset($payload['id']);
|
||||
static::$_data = array_merge_recursive(static::$_data, $payload);
|
||||
|
||||
return json_decode($raw, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @use X心跳
|
||||
* @param array $id
|
||||
* @return array|bool[]
|
||||
*/
|
||||
protected static function xHeartBeat(array $id): array
|
||||
{
|
||||
$url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X';
|
||||
$payload = [
|
||||
's' => static::$_data['s'],
|
||||
'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
|
||||
'device' => static::$_data['device'],
|
||||
'ets' => static::$_data['ets'],
|
||||
'benchmark' => static::$_data['benchmark'],
|
||||
'time' => static::$_data['time'],
|
||||
'ts' => static::$_data['ts'],
|
||||
'ua' => static::$_data['ua'],
|
||||
'csrf_token' => static::$_data['csrf_token'],
|
||||
'csrf' => static::$_data['csrf'],
|
||||
'visit_id' => ''
|
||||
];
|
||||
// print_r($payload);
|
||||
Log::debug(json_encode($payload, true));
|
||||
$raw = Curl::post('pc', $url, $payload, static::$_headers);
|
||||
# {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":60,"timestamp":1619419450,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}}
|
||||
# {'code':0,'message':'0','ttl':1,'data':{'heartbeat_interval':300,'timestamp':1595346846,'secret_rule':[2,5,1,4],'secret_key':'seacasdgyijfhofiuxoannn'}}
|
||||
return json_decode($raw, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 加密参数S
|
||||
* @param array $t
|
||||
* @param array $r
|
||||
* @return string|false
|
||||
*/
|
||||
protected static function encParamS(array $t, array $r)
|
||||
{
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
// 加密部分
|
||||
$payload = [
|
||||
't' => static::formatT($t),
|
||||
'r' => static::formatR($r)
|
||||
];
|
||||
// print_r($payload);
|
||||
$data = Curl::put('other', static::$_enc_server, $payload, $headers);
|
||||
$de_raw = json_decode($data, true);
|
||||
if ($de_raw['code'] == 0) {
|
||||
if (array_key_exists('s', $de_raw)) {
|
||||
// Log::info("S加密成功 {$de_raw['s']}");
|
||||
return $de_raw['s'];
|
||||
}
|
||||
Log::warning("S加密失败 加密服务器暂时错误,请检查更换");
|
||||
} else {
|
||||
Log::warning("S加密失败 {$de_raw['message']}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 格式T
|
||||
* @param array $t
|
||||
* @return array
|
||||
*/
|
||||
protected static function formatT(array $t): array
|
||||
{
|
||||
// print_r($t);
|
||||
return [
|
||||
'id' => $t['id'],
|
||||
'device' => $t['device'],
|
||||
'ets' => $t['ets'],
|
||||
'benchmark' => $t['benchmark'],
|
||||
'time' => $t['time'],
|
||||
'ts' => $t['ts'],
|
||||
'ua' => $t['ua'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 格式R
|
||||
* @param array $r
|
||||
* @return array
|
||||
*/
|
||||
protected static function formatR(array $r): array
|
||||
{
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @use 重置变量
|
||||
* @param false $force
|
||||
*/
|
||||
protected static function resetVar($force = false)
|
||||
{
|
||||
if ($force) {
|
||||
static::$_room_info = [];
|
||||
static::$_current_room_id = 0;
|
||||
|
||||
static::$_retry = 3;
|
||||
static::$_count_num = 0;
|
||||
static::$_count_time = 0;
|
||||
}
|
||||
static::$_data = null;
|
||||
static::$_data = ['id' => []];
|
||||
$data = [
|
||||
'id' => static::$_data['id'],
|
||||
];
|
||||
$data["id"][2] = 0;
|
||||
static::$_data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,133 +305,4 @@ trait XliveHeartBeat
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @use 心跳迭代
|
||||
* @return array
|
||||
*/
|
||||
protected static function heartBeatIterator(): array
|
||||
{
|
||||
$rdata = [];
|
||||
# 第1次执行 eHeartBeat
|
||||
if (static::$_data['id'][2] == 0) {
|
||||
$r_data = static::eHeartBeat(static::$_data['id']);
|
||||
} else {
|
||||
# 第1次之后执行 xHeartBeat
|
||||
static::$_data['ts'] = time() * 1000;
|
||||
static::$_data['s'] = static::encParamS(static::$_data, static::$_secret_rule);
|
||||
$r_data = static::xHeartBeat(static::$_data['id']);
|
||||
}
|
||||
if ($r_data['code'] == 0) {
|
||||
$rdata = $r_data['data'];
|
||||
static::$_data['ets'] = $rdata['timestamp'];
|
||||
static::$_data['benchmark'] = $rdata['secret_key'];
|
||||
static::$_data['time'] = $rdata['heartbeat_interval'];
|
||||
static::$_secret_rule = $rdata['secret_rule'];
|
||||
static::$_data['id'][2] += 1;
|
||||
}
|
||||
Log::debug(json_encode(static::$_data['id'], true));
|
||||
return [
|
||||
'code' => $r_data['code'],
|
||||
'message' => $r_data['message'],
|
||||
'heartbeat_interval' => $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' => ''
|
||||
];
|
||||
// print_r($payload);
|
||||
Log::debug(json_encode($payload, true));
|
||||
$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']);
|
||||
static::$_data = array_merge_recursive(static::$_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();
|
||||
$headers = [
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'Origin' => 'https://live.bilibili.com',
|
||||
'Referer' => 'https://live.bilibili.com/' . $id[3],
|
||||
];
|
||||
$payload = [
|
||||
's' => static::$_data['s'],
|
||||
'id' => json_encode([$id[0], $id[1], $id[2], $id[3]], true),
|
||||
'device' => static::$_data['device'],
|
||||
'ets' => static::$_data['ets'],
|
||||
'benchmark' => static::$_data['benchmark'],
|
||||
'time' => static::$_data['time'],
|
||||
'ts' => static::$_data['ts'],
|
||||
'ua' => static::$_data['ua'],
|
||||
'csrf_token' => $user_info['token'],
|
||||
'csrf' => $user_info['token'],
|
||||
'visit_id' => ''
|
||||
];
|
||||
// print_r($payload);
|
||||
Log::debug(json_encode($payload, true));
|
||||
$raw = Curl::post('pc', $url, $payload, $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', static::$_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user