Merge pull request #190 from MoeHero/feat/lottery

[feat(Lottery)]: BETA^Complete reservation live lottery
This commit is contained in:
lkeme 2022-10-08 14:06:42 +08:00 committed by GitHub
commit c37c0bcee4
3 changed files with 366 additions and 0 deletions

265
plugin/Lottery/Lottery.php Normal file
View File

@ -0,0 +1,265 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
use Bhp\Cache\Cache;
use Bhp\Log\Log;
use Bhp\Plugin\BasePlugin;
use Bhp\Plugin\Plugin;
use Bhp\TimeLock\TimeLock;
use Bhp\Request\Request;
use Bhp\User\User;
class Lottery extends BasePlugin
{
/**
* 预计每日抽奖数量
* 用于计算当前日期的MaxId
*/
private const LOTTERY_PER_DAY = 75;
/**
* 抽奖活动开始日期
* 用于计算当前日期的MaxId
*/
private const START_LOTTERY_START_TIME = '2018-05-01';
/**
* 插件信息
* @var array|string[]
*/
protected ?array $info = [
'hook' => __CLASS__, // hook
'name' => 'Lottery', // 插件名称
'version' => '0.0.1', // 插件版本
'desc' => '抽奖', // 插件描述
'author' => 'MoeHero',// 作者
'priority' => 1113, // 插件优先级
'cycle' => '2-6(小时)', // 运行周期
];
/**
* 上次任务是否完成
* @var bool
*/
protected bool $last_task_finish = false;
/**
* @param Plugin $plugin
*/
public function __construct(Plugin &$plugin)
{
include_once 'LotteryInfo.php';
TimeLock::initTimeLock();
Cache::initCache();
$plugin->register($this, 'execute');
}
/**
* 执行
* @return void
*/
public function execute(): void
{
if (!getEnable('lottery')) return;
if (TimeLock::getTimes() > time() && $this->last_task_finish) return;
$this->lotteryTask();
// 2-6小时 未完成6-10秒
TimeLock::setTimes($this->last_task_finish ? (mt_rand(2, 6) * 60 * 60) : mt_rand(6, 10));
}
/**
* 执行抽奖任务
* @return void
*/
protected function lotteryTask(): void
{
$last_lottery_id = ($tmp = Cache::get('last_lottery_id')) ? $tmp : $this->calcLastLotteryId();
$times = 0;
// 参加抽奖
while (true) {
if(LotteryInfo::isExist($last_lottery_id)) continue;
if($times > 9) {
$this->last_task_finish = false;
break;
}
$info = $this->getLotteryInfo($last_lottery_id);
if ($info['status'] === -9999) { // 当前抽奖不存在
$this->last_task_finish = true;
break;
} else if($info['status'] === -1 || $info['status'] === 2) { // 当前抽奖无效/当前抽奖已开奖
$last_lottery_id++;
continue;
}
$this->joinLottery($info);
$last_lottery_id++;
Cache::set('last_lottery_id', $last_lottery_id);
$times++;
}
$times = 0;
// 删除动态
$infos = LotteryInfo::getHasLotteryList();
foreach ($infos as $info) {
if($times > 9) {
$this->last_task_finish = false;
break;
}
//TODO 删除动态
LotteryInfo::delete($info['lottery_id']);
$times++;
}
}
/**
* 计算最新抽奖Id
* @return int
*/
protected function calcLastLotteryId(): int
{
$start_time = new DateTime(self::START_LOTTERY_START_TIME);
$end_time = new DateTime();
$elapsed_days = $start_time->diff($end_time)->days;
$max_id = $elapsed_days * self::LOTTERY_PER_DAY;
$min_id = max($max_id - 10000, 0);
// 如果计算出的MaxId不是未使用的Id则每次加5000
while (true) {
$info = $this->getLotteryInfo($max_id);
if ($info['status'] === -9999) break;
$min_id = $max_id;
$max_id += 5000;
}
// 如果计算出的MinId是未使用的Id则每次减5000
while (true) {
$info = $this->getLotteryInfo($min_id);
if ($info['status'] !== -9999) break;
$max_id = $min_id;
$min_id -= 5000;
}
$times = 0;
while (true) {
$times++;
$middle = intval(($min_id + $max_id) / 2);
$info = $this->getLotteryInfo($middle);
if ($info['status'] !== -9999) {
$min_id = $middle;
} else {
$max_id = $middle;
}
if ($max_id - $min_id == 1) break;
}
Log::info("抽奖计算出最新抽奖Id Id: $max_id 请求次数: $times");
// 抽奖模式 0.从最新Id开始抽奖 1.从最新Id的前2400个开始抽奖
if (getConf('lottery.lottery_mode', 0) == 1) return $max_id - 2400;
return $max_id;
}
/**
* 获取抽奖信息
* @param int $lottery_id
* @return array
*/
protected function getLotteryInfo(int $lottery_id): array
{
$user = User::parseCookie();
$url = 'https://api.vc.bilibili.com/lottery_svr/v1/lottery_svr/detail_by_lid';
$payload = [
'lottery_id' => $lottery_id,
'csrf' => $user['csrf'],
];
$response = Request::getJson(true, 'pc', $url, $payload);
// 抽奖不存在
if ($response['code'] === -9999) {
return [
'status' => -9999,
];
}
$data = $response['data'];
// business_type为0的则为无效抽奖
if ($data['business_type'] === 0) {
return [
'status' => -1,
];
}
// 已开奖
if ($data['lottery_time'] <= time()) {
return [
'status' => 2,
];
}
return [
'lottery_id' => $lottery_id,
'status' => $data['status'], // 0 未开奖 2 已开奖 -1 已失效 -9999 不存在
'type' => $data['business_type'], // 1.转发动态 10.直播预约
'need_feed' => $data['lottery_feed_limit'] === 1, // 是否需要关注
'business_id' => $data['business_id'], // business_type=1时是动态Id business_type=10时是预约直播Id
];
}
/**
* 参加抽奖
* @param array $info
* @return void
*/
protected function joinLottery(array $info): void
{
$dynamic_enable = getConf('lottery.dynamic_enable', false);
$live_enable = getConf('lottery.live_enable', false);
if ($info['type'] === 1 && $dynamic_enable) {
$dynamic_id = $info['business_id'];
//TODO 转发动态
//TODO 关注用户并放到指定分组
} else if ($info['type'] === 10 && $live_enable) {
$reserve_id = $info['business_id'];
$this->reserveLive($reserve_id);
}
}
/**
* 删除动态
* @param string $dynamic_id
* @return void
*/
protected function deleteDynamic(string $dynamic_id): void
{
//TODO 删除动态
}
/**
* 预约直播
* @param int $reserve_id
* @return void
*/
protected function reserveLive(int $reserve_id): void
{
$user = User::parseCookie();
$url = 'https://api.vc.bilibili.com/dynamic_mix/v1/dynamic_mix/reserve_attach_card_button';
$payload = [
'cur_btn_status' => 1,
'reserve_id' => $reserve_id,
'csrf' => $user['csrf'],
];
$response = Request::postJson(true, 'pc', $url, $payload);
if($response['code'] === 0 || $response['code'] === 7604003) { //预约成功/已经预约
Log::info("抽奖: 预约直播成功 ReserveId: $reserve_id");
} else {
Log::warning("抽奖: 预约直播失败 ReserveId: $reserve_id Error: {$response['code']} -> {$response['message']}");
}
}
}

View File

@ -0,0 +1,93 @@
<?php declare(strict_types=1);
/**
* Website: https://mudew.com/
* Author: Lkeme
* License: The MIT License
* Email: Useri@live.cn
* Updated: 2022 ~ 2023
*
* _____ _ _ _ _ _ _____ _ _____ _____ _____
* | _ \ | | | | | | | | | | | ____| | | | _ \ | ____| | _ \ & l、
* | |_| | | | | | | | | |_| | | |__ | | | |_| | | |__ | |_| | (゚、
* | _ { | | | | | | | _ | | __| | | | ___/ | __| | _ /   \、゙ ~ *
* | |_| | | | | |___ | | | | | | | |___ | |___ | | | |___ | | \ \  じしf_, )
* |_____/ |_| |_____| |_| |_| |_| |_____| |_____| |_| |_____| |_| \_\
*/
use Bhp\Cache\Cache;
use Bhp\Util\DesignPattern\SingleTon;
class LotteryInfo extends SingleTon
{
/**
* @var array 抽奖信息列表
*/
protected array $info_list = [];
/**
* 初始化数据
* @return void
*/
public function init(): void
{
Cache::initCache();
$this->info_list = ($tmp = Cache::get('lottery_infos')) ? $tmp : [];
}
/**
* 获取已开奖列表
* @return array
*/
public static function getHasLotteryList(): array
{
return array_filter(self::getInstance()->info_list, function (array $i) {
return $i['lottery_time'] < (time() + 30 * 60);
});
}
/**
* 抽奖是否存在
* @param int $lottery_id
* @return bool
*/
public static function isExist(int $lottery_id): bool
{
foreach (self::getInstance()->info_list as $item) {
if ($item['lottery_id'] === $lottery_id) return true;
}
return false;
}
/**
* 新增抽奖信息
* @param int $lottery_id
* @param string $dynamic_id
* @param int $lottery_time
* @return void
*/
public static function add(int $lottery_id, string $dynamic_id, int $lottery_time, int $uid, int $group_id): void
{
self::getInstance()->info_list[] = [
'lottery_id' => $lottery_id,
'dynamic_id' => $dynamic_id,
'lottery_time' => $lottery_time,
'uid' => $uid,
'group_id' => $group_id,
];
Cache::set('lottery_infos', self::getInstance()->info_list);
}
/**
* 删除抽奖信息
* @param int $lottery_id
* @return void
*/
public static function delete(int $lottery_id): void
{
self::getInstance()->info_list = array_filter(self::getInstance()->info_list, function (array $i) use ($lottery_id) {
return $i['lottery_id'] === $lottery_id;
});
Cache::set('lottery_infos', self::getInstance()->info_list);
}
}

View File

@ -103,6 +103,14 @@ enable = true
[vip_point]
enable = false
; 动态抽奖&预约直播抽奖
[lottery]
enable = true
; 抽奖模式 0.从最新Id开始抽奖 1.从最新Id的前2400个开始抽奖
lottery_mode = 0
; 直播预约抽奖
live_enable = true
#######################
# 通知设置 #
#######################