diff --git a/plugin/Login/Login.php b/plugin/Login/Login.php index ed21c48..745cfb6 100644 --- a/plugin/Login/Login.php +++ b/plugin/Login/Login.php @@ -373,7 +373,7 @@ class Login extends BasePlugin { // {"code":0,"message":"0","ttl":1,"data":{"mid":123,"access_token":"xxx","refresh_token":"xxx","expires_in":2592000}} $response = ApiQrcode::poll($auth_code); - echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + // echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); switch ($response['code']) { case 0: // 登录成功 diff --git a/plugin/MainSite/MainSite.php b/plugin/MainSite/MainSite.php new file mode 100644 index 0000000..abae6d3 --- /dev/null +++ b/plugin/MainSite/MainSite.php @@ -0,0 +1,345 @@ + __CLASS__, // hook + 'name' => 'MainSite', // 插件名称 + 'version' => '0.0.1', // 插件版本 + 'desc' => '主站任务(观看|分享|投币)', // 插件描述 + 'author' => 'Lkeme',// 作者 + 'priority' => 1100, // 插件优先级 + 'cycle' => '1(小时)', // 运行周期 + ]; + + /** + * @param Plugin $plugin + */ + public function __construct(Plugin &$plugin) + { + // + TimeLock::initTimeLock(); + // $this::class + $plugin->register($this, 'execute'); + } + + /** + * @use 执行 + * @return void + */ + 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); + } + } + + /** + * @use 投币任务 + * @return bool + */ + 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; + } + // 稿件列表 + if (getConf('main_site.add_coin_mode') == 'random') { + // 随机热门稿件榜单 + $aids = self::getTopAids($actual_num); + } else { + // 固定获取关注UP稿件榜单, 不足会随机补全 + $aids = self::getFollowUpAids($actual_num); + } + // + Log::info("主站任务: 预投币稿件 " . implode(" ", $aids)); + // 投币 + foreach ($aids as $aid) { + $this->reward((string)$aid); + } + return true; + } + + /** + * @use 投币 + * @param string $aid + * @return void + */ + protected function reward(string $aid): void + { + $response = ApiCoin::coin($aid); + // + if ($response['code']) { + Log::warning("主站任务: $aid 投币失败 {$response['code']} -> {$response['message']}"); + } else { + Log::notice("主站任务: $aid 投币成功"); + } + } + + /** + * @use 首页推荐 + * @param int $num + * @param int $ps + * @return array + */ + protected function getTopAids(int $num, int $ps = 30): array + { + $aids = []; + $response = ApiVideo::dynamicRegion($ps); + // + if ($response['code']) { + Log::warning("主站任务: 获取首页推荐失败 {$response['code']} -> {$response['message']}"); + return self::getDayRankingAids($num); + } + // + if ($num == 1) { + $temps = [array_rand($response['data']['archives'], $num)]; + } else { + $temps = array_rand($response['data']['archives'], $num); + } + foreach ($temps as $temp) { + $aids[] = $response['data']['archives'][$temp]['aid']; + } + return $aids; + } + + /** + * @use 获取榜单稿件列表 + * @param int $num + * @return array + */ + protected function getDayRankingAids(int $num): array + { + $aids = []; + $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; + } + } + $aid = $response['data']['list'][$rand_nums[$i]]['aid']; + $aids[] = $aid; + } + // + return $aids; + } + + /** + * @use 获取关注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->getTopAids($num - count($aids))); + } + return $aids; + } + + /** + * @use 已投币数量 + * @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; + } + + /** + * @use 获取硬币库存 + * @return int + */ + protected function getCoinStock(): int + { + // {"code":0,"status":true,"data":{"money":1707.9}} + $response = ApiCoin::getCoin(); + // + if ($response['code'] || !isset($response['data']['money'])) { + Log::warning("主站任务: 获取硬币库存失败 " . json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + return 0; + } + // + return intval($response['data']['money']); + } + + /** + * @use 分享任务 + * @return bool + */ + protected function shareTask(): bool + { + if (!getConf('main_site.share', false, 'bool')) return true; + // video*10 + $infos = $this->fetchRandomAvInfos(); + $info = array_pop($infos); + $aid = (string)$info['aid']; + // + $response = ApiShare::share($aid); + if ($response['code']) { + Log::warning("主站任务: $aid 分享失败 {$response['code']} -> {$response['message']}"); + return false; + } + Log::notice("主站任务: $aid 分享成功"); + return true; + } + + /** + * @use 观看任务 + * @return bool + */ + protected function watchTask(): bool + { + if (!getConf('main_site.watch', false, 'bool')) return true; + // video*10 + $infos = $this->fetchRandomAvInfos(); + $info = array_pop($infos); + $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; + } + + /** + * @use 获取随机 + * @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; + } + +} + \ No newline at end of file diff --git a/plugin/PluginTemplate/PluginTemplate.php b/plugin/PluginTemplate/PluginTemplate.php new file mode 100644 index 0000000..47620d8 --- /dev/null +++ b/plugin/PluginTemplate/PluginTemplate.php @@ -0,0 +1,66 @@ + __CLASS__, // hook + 'name' => 'PluginTemplate', // 插件名称 + 'version' => '0.0.1', // 插件版本 + 'desc' => '插件模板', // 插件描述 + 'author' => 'Lkeme',// 作者 + 'priority' => 9999, // 插件优先级 + 'cycle' => '1(小时)', // 运行周期 + ]; + + /** + * @param Plugin $plugin + */ + public function __construct(Plugin &$plugin) + { +// // 需要时间锁 +// TimeLock::initTimeLock(); +// // 需要缓存 +// Cache::initCache(); +// // $this::class +// $plugin->register($this, 'execute'); + } + + /** + * @use 执行 + * @return void + */ + public function execute(): void + { +// if (TimeLock::getTimes() > time()) return; + // + // todo + // +// TimeLock::setTimes(24 * 60 * 60); + } + + + +} + \ No newline at end of file diff --git a/profile/example/config/user.ini b/profile/example/config/user.ini index bd3b480..e3edc28 100644 --- a/profile/example/config/user.ini +++ b/profile/example/config/user.ini @@ -21,6 +21,22 @@ code = 86 [login_check] phone = true +####################### +# 插件功能设置 # +####################### + +; 主站每日任务(每日登录、观看、投币、分享) +[main_site] +enable = true +; 每日观看 +watch = true +; 每日分享 +share = true +; 每日视频投币/random(随机热门)/fixed(关注列表)/投币稿件数(每日任务最大5) +add_coin = true +add_coin_mode = random +add_coin_num = 5 + ####################### # 通知设置 # ####################### diff --git a/src/Api/DynamicSvr/ApiDynamicSvr.php b/src/Api/DynamicSvr/ApiDynamicSvr.php new file mode 100644 index 0000000..5d4128e --- /dev/null +++ b/src/Api/DynamicSvr/ApiDynamicSvr.php @@ -0,0 +1,47 @@ + $user['uid'], + 'type_list' => '8,512,4097,4098,4099,4100,4101' + ]; + $headers = [ + 'origin' => 'https://t.bilibili.com', + 'referer' => 'https://t.bilibili.com/pages/nav/index_new' + ]; + return Request::getJson(true, 'pc', $url, $payload, $headers); + } + +} + + \ No newline at end of file diff --git a/src/Api/Video/ApiCoin.php b/src/Api/Video/ApiCoin.php new file mode 100644 index 0000000..956e101 --- /dev/null +++ b/src/Api/Video/ApiCoin.php @@ -0,0 +1,84 @@ + $aid, + 'multiply' => $multiply, // 投币*1 + 'select_like' => $select_like,// 默认不点赞 + 'cross_domain' => 'true', + 'csrf' => $user['csrf'], + ]; + $headers = [ + 'origin' => 'https://www.bilibili.com', + 'Referer' => "https://www.bilibili.com/video/av$aid", + ]; + // {"code":34005,"message":"超过投币上限啦~","ttl":1,"data":{"like":false}} + // {"code":0,"message":"0","ttl":1,"data":{"like":false}} + // CODE -> 137001 MSG -> 账号封禁中,无法完成操作 + // CODE -> -650 MSG -> 用户等级太低 + return Request::postJson(true, 'pc', $url, $payload, $headers); + } + + /** + * @use 获取硬币 + * @return array + */ + public static function getCoin(): array + { + $url = 'https://account.bilibili.com/site/getCoin'; + $payload = []; + $headers = [ + 'referer' => 'https://account.bilibili.com/account/coin', + ]; + // {"code":0,"status":true,"data":{"money":1707.9}} + return Request::getJson(true, 'pc', $url, $payload, $headers); + } + + /** + * @use 投币日志 + * @return array + */ + public static function addLog(): array + { + $url = 'https://api.bilibili.com/x/member/web/coin/log'; + $payload = []; + return Request::getJson(true, 'pc', $url, $payload); + } + + +} \ No newline at end of file diff --git a/src/Api/Video/ApiShare.php b/src/Api/Video/ApiShare.php new file mode 100644 index 0000000..05c4620 --- /dev/null +++ b/src/Api/Video/ApiShare.php @@ -0,0 +1,51 @@ + $aid, + 'jsonp' => "jsonp", + 'csrf' => $user['csrf'], + ]; + $headers = [ + 'origin' => 'https://www.bilibili.com', + 'Referer' => "https://www.bilibili.com/video/av$aid", + ]; + // {"code":0,"message":"0","ttl":1} + return Request::postJson(true, 'pc', $url, $payload, $headers); + } + +} \ No newline at end of file diff --git a/src/Api/Video/ApiVideo.php b/src/Api/Video/ApiVideo.php new file mode 100644 index 0000000..f133100 --- /dev/null +++ b/src/Api/Video/ApiVideo.php @@ -0,0 +1,76 @@ + $pn, + 'ps' => $ps + ]; + return Request::getJson(true, 'other', $url, $payload); + } + + /** + * @use 获取分区动态/首页推荐 + * @param int $ps + * @return array + */ + public static function dynamicRegion(int $ps = 30): array + { + // 动画1 国创168 音乐3 舞蹈129 游戏4 知识36 科技188 汽车223 生活160 美食211 动物圈127 鬼畜119 时尚155 资讯202 娱乐5 影视181 + $rids = [1, 168, 3, 129, 4, 36, 188, 223, 160, 211, 127, 119, 155, 202, 5, 181]; + // + $url = 'https://api.bilibili.com/x/web-interface/dynamic/region'; + $payload = [ + 'ps' => $ps, + 'rid' => $rids[array_rand($rids)], + ]; + return Request::getJson(true, 'other', $url, $payload); + } + + /** + * @use 获取榜单稿件 + * @return array + */ + public static function ranking(): array + { + // day: 日榜1 三榜3 周榜7 月榜30 + $url = 'https://api.bilibili.com/x/web-interface/ranking'; + $payload = [ + 'rid' => 0, + 'day' => 1, + 'type' => 1, + 'arc_type' => 0 + ]; + return Request::getJson(true, 'other', $url, $payload); + } + + +} \ No newline at end of file diff --git a/src/Api/Video/ApiWatch.php b/src/Api/Video/ApiWatch.php new file mode 100644 index 0000000..e0d1381 --- /dev/null +++ b/src/Api/Video/ApiWatch.php @@ -0,0 +1,97 @@ + $aid, + 'cid' => $cid, + 'part' => 1, + 'ftime' => time(), + 'jsonp' => 'jsonp', + 'mid' => $user['uid'], + 'csrf' => $user['csrf'], + 'stime' => time(), + 'lv' => '', + 'auto_continued_play' => 0, + 'refer_url' => "https://www.bilibili.com/video/av$aid" + ]; + $headers = [ + 'origin' => 'https://www.bilibili.com', + 'Referer' => "https://www.bilibili.com/video/av$aid", + ]; + // {"code":0,"message":"0","ttl":1} + return Request::postJson(true, 'pc', $url, $payload, $headers); + } + + /** + * @use 发送心跳 + * @param string $aid + * @param string $cid + * @param int $duration + * @param array|null $data + * @return array + */ + public static function heartbeat(string $aid, string $cid, int $duration, ?array $data = null): array + { + // + $user = User::parseCookie(); + // + $url = 'https://api.bilibili.com/x/click-interface/web/heartbeat'; + $payload = [ + 'aid' => $aid, + 'cid' => $cid, + 'mid' => $user['uid'], + 'csrf' => $user['csrf'], + 'jsonp' => 'jsonp', + 'played_time' => 0, + 'realtime' => $duration, + 'pause' => false, + 'play_type' => 1, + 'start_ts' => time() + ]; + // + if (!is_null($data)) { + $payload = array_merge($payload, $data); + } + // + $headers = [ + 'origin' => 'https://www.bilibili.com', + 'Referer' => "https://www.bilibili.com/video/av$aid", + ]; + // {"code":0,"message":"0","ttl":1} + return Request::postJson(true, 'pc', $url, $payload, $headers); + } + +} \ No newline at end of file diff --git a/src/Request/Request.php b/src/Request/Request.php index 2c944ef..4c36334 100644 --- a/src/Request/Request.php +++ b/src/Request/Request.php @@ -174,16 +174,15 @@ class Request extends SingleTon * @use 客户端配置 * @param string $request_id * @param array $add_options - * @param array $headers * @param float $timeout * @return Request */ - protected function withOptions(string $request_id, array $add_options, array $headers = [], float $timeout = 30.0): static + protected function withOptions(string $request_id, array $add_options, float $timeout = 30.0): static { $default_options = [ // 'connect_timeout' => 10, // 'debug' => false, - 'headers' => $headers, + 'headers' => $this->getRequest($request_id, 'headers'), 'timeout' => $timeout, 'http_errors' => false, 'verify' => getConf('network_ssl.verify', false, 'bool'), @@ -254,7 +253,7 @@ class Request extends SingleTon ->withUrl($rid, $url) ->withMethod($rid, 'get') ->withHeaders($rid, $os, $headers) - ->withOptions($rid, $payload, $headers, $timeout) + ->withOptions($rid, $payload, $timeout) ->handle($rid); // self::getInstance()->stopRequest($rid); @@ -311,7 +310,7 @@ class Request extends SingleTon ->withUrl($rid, $url) ->withMethod($rid, 'post') ->withHeaders($rid, $os, $headers) - ->withOptions($rid, $payload, $headers, $timeout) + ->withOptions($rid, $payload, $timeout) ->handle($rid); // self::getInstance()->stopRequest($rid); @@ -368,7 +367,7 @@ class Request extends SingleTon ->withUrl($rid, $url) ->withMethod($rid, 'post') ->withHeaders($rid, $os, $headers) - ->withOptions($rid, $payload, $headers, $timeout) + ->withOptions($rid, $payload, $timeout) ->handle($rid); // self::getInstance()->stopRequest($rid); @@ -525,7 +524,6 @@ class Request extends SingleTon unset($this->caches[$request_id]); } - /** * @use GET请求 * @param $os @@ -550,7 +548,7 @@ class Request extends SingleTon ->withUrl($rid, $url) ->withMethod($rid, 'get') ->withHeaders($rid, $os, $headers) - ->withOptions($rid, $payload, $headers, $timeout) + ->withOptions($rid, $payload, $timeout) ->handle($rid); // self::getInstance()->stopRequest($rid); @@ -560,6 +558,4 @@ class Request extends SingleTon return $response->getHeaders(); } - - } \ No newline at end of file diff --git a/src/Util/AsciiTable/AsciiTable.php b/src/Util/AsciiTable/AsciiTable.php index 8d815d0..a03ea59 100644 --- a/src/Util/AsciiTable/AsciiTable.php +++ b/src/Util/AsciiTable/AsciiTable.php @@ -349,9 +349,10 @@ class AsciiTable * @use 数组转表格(外置) * @param array $data * @param string|null $title + * @param bool $print * @return array */ - public static function array2table(array $data, ?string $title = null): array + public static function array2table(array $data, ?string $title = null, bool $print = false): array { $th_list = []; // @@ -365,6 +366,7 @@ class AsciiTable foreach (explode("\r\n", $builder->renderTable()) as $value) { if ($value) { $th_list[] = $value; + if ($print) echo $value . PHP_EOL; } } return $th_list;