mirror of
https://github.com/lkeme/BiliHelper-personal.git
synced 2025-12-19 01:20:08 +08:00
383 lines
11 KiB
PHP
383 lines
11 KiB
PHP
<?php declare(strict_types=1);
|
||
|
||
/**
|
||
* Website: https://mudew.com/
|
||
* Author: Lkeme
|
||
* License: The MIT License
|
||
* Email: Useri@live.cn
|
||
* Updated: 2022 ~ 2023
|
||
*
|
||
* _____ _ _ _ _ _ _____ _ _____ _____ _____
|
||
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & /l、
|
||
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、 。 7
|
||
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ / \、゙ ~ヽ *
|
||
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \ じしf_, )ノ
|
||
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
|
||
*/
|
||
|
||
use Bhp\Api\Api\X\Player\ApiPlayer;
|
||
use Bhp\Api\DynamicSvr\ApiDynamicSvr;
|
||
use Bhp\Api\Video\ApiCoin;
|
||
use Bhp\Api\Video\ApiShare;
|
||
use Bhp\Api\Video\ApiWatch;
|
||
use Bhp\Api\Video\ApiVideo;
|
||
use Bhp\Log\Log;
|
||
use Bhp\Plugin\BasePlugin;
|
||
use Bhp\Plugin\Plugin;
|
||
use Bhp\TimeLock\TimeLock;
|
||
use Bhp\Util\Exceptions\NoLoginException;
|
||
|
||
class MainSite extends BasePlugin
|
||
{
|
||
/**
|
||
* 插件信息
|
||
* @var array|string[]
|
||
*/
|
||
protected ?array $info = [
|
||
'hook' => __CLASS__, // hook
|
||
'name' => 'MainSite', // 插件名称
|
||
'version' => '0.0.1', // 插件版本
|
||
'desc' => '主站任务(观看|分享|投币)', // 插件描述
|
||
'author' => 'Lkeme',// 作者
|
||
'priority' => 1100, // 插件优先级
|
||
'cycle' => '24(小时)', // 运行周期
|
||
];
|
||
|
||
/**
|
||
* @param Plugin $plugin
|
||
*/
|
||
public function __construct(Plugin &$plugin)
|
||
{
|
||
//
|
||
TimeLock::initTimeLock();
|
||
// $this::class
|
||
$plugin->register($this, 'execute');
|
||
}
|
||
|
||
/**
|
||
* 执行
|
||
* @return void
|
||
* @throws NoLoginException
|
||
*/
|
||
public function execute(): void
|
||
{
|
||
if (TimeLock::getTimes() > time() || !getEnable('main_site')) return;
|
||
//
|
||
if ($this->watchTask() && $this->shareTask() && $this->coinTask()) {
|
||
TimeLock::setTimes(TimeLock::timing(10));
|
||
} else {
|
||
TimeLock::setTimes(3600);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取稿件列表
|
||
* @param int $num
|
||
* @return array
|
||
*/
|
||
protected function fetchCustomAids(int $num = 30): array
|
||
{
|
||
if (getConf('main_site.fetch_aids_mode') == 'random') {
|
||
// 随机热门稿件榜单
|
||
return $this->getTopArchives($num);
|
||
} else {
|
||
// 固定获取关注UP稿件榜单, 不足会随机补全
|
||
return $this->getFollowUpAids($num);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 投币任务
|
||
* @return bool
|
||
* @throws NoLoginException
|
||
*/
|
||
protected function coinTask(): bool
|
||
{
|
||
if (!getConf('main_site.add_coin', false, 'bool')) return true;
|
||
// 预计数量 失败默认0 避免损失
|
||
$estimate_num = getConf('main_site.add_coin_num', 0, 'int');
|
||
// 库存数量
|
||
$stock_num = $this->getCoinStock();
|
||
$already_num = $this->getCoinAlready();
|
||
// 实际数量 处理硬币库存少于预计数量
|
||
$actual_num = intval(min($estimate_num, $stock_num)) - $already_num;
|
||
//
|
||
Log::info("主站任务: 硬币库存 $stock_num 预投 $estimate_num 已投 $already_num 还需投币 $actual_num");
|
||
// 上限
|
||
if ($actual_num <= 0) {
|
||
Log::notice('主站任务: 今日投币上限已满');
|
||
return true;
|
||
}
|
||
// 稿件列表
|
||
$aids = $this->fetchCustomAids($actual_num);
|
||
//
|
||
Log::info("主站任务: 预投币稿件 " . implode(" ", $aids));
|
||
// 投币
|
||
foreach ($aids as $aid) {
|
||
$this->reward((string)$aid);
|
||
//
|
||
sleep(1);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 投币
|
||
* @param string $aid
|
||
* @return void
|
||
* @throws NoLoginException
|
||
*/
|
||
protected function reward(string $aid): void
|
||
{
|
||
$response = ApiCoin::coin($aid);
|
||
//
|
||
switch ($response['code']) {
|
||
case -101:
|
||
throw new NoLoginException($response['message']);
|
||
case 0:
|
||
Log::notice("主站任务: $aid 投币成功");
|
||
break;
|
||
default:
|
||
Log::warning("主站任务: $aid 投币失败 {$response['code']} -> {$response['message']}");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 首页推荐
|
||
* @param int $num
|
||
* @param int $ps
|
||
* @return array
|
||
*/
|
||
protected function getTopArchives(int $num, int $ps = 30): array
|
||
{
|
||
$response = ApiVideo::dynamicRegion($ps);
|
||
//
|
||
if ($response['code']) {
|
||
Log::warning("主站任务: 获取首页推荐失败 {$response['code']} -> {$response['message']}");
|
||
return $this->getDayRankingArchives($num);
|
||
}
|
||
//
|
||
if ($num == 1) {
|
||
return [array_rand($response['data']['archives'], $num)];
|
||
} else {
|
||
return array_rand($response['data']['archives'], $num);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取榜单稿件列表
|
||
* @param int $num
|
||
* @return array
|
||
*/
|
||
protected function getDayRankingArchives(int $num): array
|
||
{
|
||
$archives = [];
|
||
$rand_nums = [];
|
||
//
|
||
$response = ApiVideo::ranking();
|
||
//
|
||
for ($i = 0; $i < $num; $i++) {
|
||
while (true) {
|
||
$rand_num = mt_rand(1, 99);
|
||
if (in_array($rand_num, $rand_nums)) {
|
||
continue;
|
||
} else {
|
||
$rand_nums[] = $rand_num;
|
||
break;
|
||
}
|
||
}
|
||
$archives[] = $response['data']['list'][$rand_nums[$i]];
|
||
}
|
||
//
|
||
return $archives;
|
||
}
|
||
|
||
/**
|
||
* 获取关注UP稿件列表
|
||
* @param int $num
|
||
* @return array
|
||
*/
|
||
protected function getFollowUpAids(int $num): array
|
||
{
|
||
$aids = [];
|
||
//
|
||
$response = ApiDynamicSvr::followUpDynamic();
|
||
//
|
||
if ($response['code']) {
|
||
Log::warning("主站任务: 获取UP稿件失败 {$response['code']} -> {$response['message']}");
|
||
return $aids;
|
||
}
|
||
//
|
||
foreach ($response['data']['cards'] as $index => $card) {
|
||
if ($index >= $num) {
|
||
break;
|
||
}
|
||
$aids[] = $card['desc']['rid'];
|
||
}
|
||
// 此处补全缺失
|
||
if (count($aids) < $num) {
|
||
$aids = array_merge($aids, $this->getTopArchives($num - count($aids)));
|
||
}
|
||
return $aids;
|
||
}
|
||
|
||
/**
|
||
* 已投币数量
|
||
* @return int
|
||
*/
|
||
protected function getCoinAlready(): int
|
||
{
|
||
$response = ApiCoin::addLog();
|
||
//
|
||
if ($response['code'] || !isset($response['data']['list'])) {
|
||
Log::warning("主站任务: 获取已硬币失败 {$response['code']} -> {$response['message']}");
|
||
return 0;
|
||
}
|
||
//
|
||
$logs = $response['data']['list'] ?? [];
|
||
$coins = 0;
|
||
//
|
||
foreach ($logs as $log) {
|
||
$log_ux = strtotime($log['time']);
|
||
$log_date = date('Y-m-d', $log_ux);
|
||
$now_date = date('Y-m-d');
|
||
if ($log_date != $now_date) {
|
||
break;
|
||
}
|
||
if (str_contains($log['reason'], "打赏")) {
|
||
switch ($log['delta']) {
|
||
case -1:
|
||
$coins += 1;
|
||
break;
|
||
case -2:
|
||
$coins += 2;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return $coins;
|
||
}
|
||
|
||
/**
|
||
* 获取硬币库存
|
||
* @return int
|
||
*/
|
||
protected function getCoinStock(): int
|
||
{
|
||
// {"code":0,"status":true,"data":{"money":1707.9}}
|
||
// {"code":0,"status":true,"data":{"money":null}
|
||
$response = ApiCoin::getCoin();
|
||
//
|
||
if ($response['code'] || !isset($response['data']['money'])) {
|
||
Log::warning("主站任务: 获取硬币库存失败或者硬币为null " . json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||
return 0;
|
||
}
|
||
//
|
||
return (int)$response['data']['money'];
|
||
}
|
||
|
||
/**
|
||
* 分享任务
|
||
* @return bool
|
||
* @throws NoLoginException
|
||
*/
|
||
protected function shareTask(): bool
|
||
{
|
||
if (!getConf('main_site.share', false, 'bool')) return true;
|
||
|
||
$archives = $this->fetchCustomAids(10);
|
||
$archive = array_pop($archives);
|
||
$aid = (string)$archive['aid'];
|
||
|
||
//
|
||
$response = ApiShare::share($aid);
|
||
switch ($response['code']) {
|
||
case -101:
|
||
throw new NoLoginException($response['message']);
|
||
case 0:
|
||
Log::notice("主站任务: $aid 分享成功");
|
||
return true;
|
||
default:
|
||
Log::warning("主站任务: $aid 分享失败 {$response['code']} -> {$response['message']}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 观看任务
|
||
* @return bool
|
||
*/
|
||
protected function watchTask(): bool
|
||
{
|
||
if (!getConf('main_site.watch', false, 'bool')) return true;
|
||
|
||
$archives = $this->fetchCustomAids(10);
|
||
$archive = array_pop($archives);
|
||
// 额外处理信息
|
||
$info = $this->getArchiveInfo((string)$archive['aid']);
|
||
$aid = (string)$info['aid'];
|
||
$cid = (string)$info['cid'];
|
||
$duration = (int)$info['duration'];
|
||
//
|
||
$response = ApiWatch::video($aid, $cid);
|
||
// == 0
|
||
if ($response['code']) {
|
||
Log::warning("主站任务: $aid 观看失败 {$response['code']} -> {$response['message']}");
|
||
return false;
|
||
}
|
||
$response = ApiWatch::heartbeat($aid, $cid, $duration);
|
||
if ($response['code']) {
|
||
Log::warning("主站任务: $aid 观看失败 {$response['code']} -> {$response['message']}");
|
||
return false;
|
||
}
|
||
sleep(5);
|
||
//
|
||
$data = [];
|
||
$data['played_time'] = $duration - 1;
|
||
$data['play_type'] = 0;
|
||
$data['start_ts'] = time();
|
||
//
|
||
$response = ApiWatch::heartbeat($aid, $cid, $duration, $data);
|
||
if ($response['code']) {
|
||
Log::warning("主站任务: $aid 观看失败 {$response['code']} -> {$response['message']}");
|
||
return false;
|
||
}
|
||
//
|
||
Log::notice("主站任务: $aid 观看成功");
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取稿件详情/暂时不额外处理错误
|
||
* @param string $aid
|
||
* @return array
|
||
*/
|
||
protected function getArchiveInfo(string $aid): array
|
||
{
|
||
$response = ApiPlayer::pageList($aid);
|
||
return $response['data'];
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取随机
|
||
* @return array
|
||
*/
|
||
protected function fetchRandomAvInfos(): array
|
||
{
|
||
do {
|
||
$response = ApiVideo::newlist(mt_rand(1, 1000), 10);
|
||
} while (count($response['data']['archives']) == 0);
|
||
//
|
||
$info = $response['data']['archives'];
|
||
shuffle($info);
|
||
//
|
||
return $info;
|
||
}
|
||
|
||
}
|