Files
chatroom/app/Console/Commands/AiHeartbeatCommand.php

200 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能AI小班长专属极轻量心跳模拟器
*
* 专门用于让无法通过浏览器发送真实心跳的 AI实体用户
* 也能够完美触原有的法发经验/金币逻辑以及触发随机事件Autoact
* 每分钟由 Laravel Scheduler 调用。
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Console\Commands;
use App\Enums\CurrencySource;
use App\Events\MessageSent;
use App\Jobs\SaveMessageJob;
use App\Models\Autoact;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService;
use App\Services\VipService;
use Illuminate\Console\Command;
class AiHeartbeatCommand extends Command
{
/**
* Artisan 指令名称
*/
protected $signature = 'chatroom:ai-heartbeat';
/**
* 指令描述
*/
protected $description = '模拟 AI 小班长客户端心跳,触发经验金币与随机事件';
public function __construct(
private readonly ChatStateService $chatState,
private readonly VipService $vipService,
private readonly UserCurrencyService $currencyService,
) {
parent::__construct();
}
public function handle(): int
{
// 1. 检查总开关
if (Sysparam::getValue('chatbot_enabled', '0') !== '1') {
return Command::SUCCESS;
}
// 2. 获取 AI 实体
$user = User::where('username', 'AI小班长')->first();
if (! $user) {
return Command::SUCCESS;
}
// 3. 常规心跳经验与金币发放
// (模拟前端每30-60秒发一次心跳的过程此处每分钟跑一次发放单人心跳奖励)
$expGain = $this->parseRewardValue(Sysparam::getValue('exp_per_heartbeat', '1'));
if ($expGain > 0) {
$expMultiplier = $this->vipService->getExpMultiplier($user);
$actualExpGain = (int) round($expGain * $expMultiplier);
$user->exp_num += $actualExpGain;
}
$jjbGain = $this->parseRewardValue(Sysparam::getValue('jjb_per_heartbeat', '0'));
if ($jjbGain > 0) {
$jjbMultiplier = $this->vipService->getJjbMultiplier($user);
$actualJjbGain = (int) round($jjbGain * $jjbMultiplier);
$user->jjb = ($user->jjb ?? 0) + $actualJjbGain;
}
$user->save();
$user->refresh();
// 4. 重算等级(基础心跳升级)
$superLevel = (int) Sysparam::getValue('superlevel', '100');
$leveledUp = $this->calculateNewLevel($user, $superLevel);
// 5. 随机事件触发
$eventChance = (int) Sysparam::getValue('auto_event_chance', '10');
if ($eventChance > 0 && rand(1, 100) <= $eventChance) {
$autoEvent = Autoact::randomEvent();
if ($autoEvent) {
// 执行随机事件的金钱经验惩奖
if ($autoEvent->exp_change !== 0) {
$this->currencyService->change(
$user, 'exp', $autoEvent->exp_change, CurrencySource::AUTO_EVENT, "随机事件:{$autoEvent->text_body}", 1
);
}
if ($autoEvent->jjb_change !== 0) {
$this->currencyService->change(
$user, 'gold', $autoEvent->jjb_change, CurrencySource::AUTO_EVENT, "随机事件:{$autoEvent->text_body}", 1
);
}
$user->refresh();
// 重新计算等级
if ($this->calculateNewLevel($user, $superLevel)) {
$leveledUp = true;
}
// 广播随机事件
$this->broadcastSystemMessage(
'星海小博士',
$autoEvent->renderText($user->username),
match ($autoEvent->event_type) {
'good' => '#16a34a',
'bad' => '#dc2626',
default => '#7c3aed',
}
);
}
}
// 6. 如果由于心跳或事件导致了升级,广播升级消息
if ($leveledUp) {
$this->broadcastSystemMessage(
'系统传音',
"🌟 天道酬勤!恭喜侠客【{$user->username}】挂机苦修,境界突破至 LV.{$user->user_level}",
'#d97706',
'大声宣告'
);
}
return Command::SUCCESS;
}
/**
* 计算并更新用户等级
*/
private function calculateNewLevel(User $user, int $superLevel): bool
{
$oldLevel = $user->user_level;
if ($oldLevel >= $superLevel) {
return false; // 管理员不自动升降级
}
$newLevel = Sysparam::calculateLevel($user->exp_num);
if ($newLevel !== $oldLevel && $newLevel < $superLevel) {
$user->user_level = $newLevel;
$user->save();
return $newLevel > $oldLevel;
}
return false;
}
/**
* 解析配置的奖励范围,如 "1" 或 "1-5"
*/
private function parseRewardValue(string $raw): int
{
$raw = trim($raw);
if (str_contains($raw, '-')) {
[$min, $max] = explode('-', $raw, 2);
return rand((int) $min, (int) $max);
}
return (int) $raw;
}
/**
* 往所有活跃房间发送系统广播消息
*/
private function broadcastSystemMessage(string $fromUser, string $content, string $color, string $action = ''): void
{
$activeRoomIds = $this->chatState->getAllActiveRoomIds();
if (empty($activeRoomIds)) {
$activeRoomIds = [1];
}
foreach ($activeRoomIds as $roomId) {
$sysMsg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => $fromUser,
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => $color,
'action' => $action,
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $sysMsg);
broadcast(new MessageSent($roomId, $sysMsg));
SaveMessageJob::dispatch($sysMsg);
}
}
}