feat: bot

This commit is contained in:
hashbro
2026-08-15 23:05:51 +08:00
parent 9049fdccad
commit 376b442880
26 changed files with 944 additions and 352 deletions
@@ -3,16 +3,10 @@
namespace App\Http\Controllers\Hooks;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Telegram\TelegramBotContext;
use App\Telegram\TelegramRegistrar;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Nutgram\Laravel\RunningMode\LaravelWebhook;
use Psr\Log\LoggerInterface;
use SergiX44\Nutgram\Configuration;
use SergiX44\Nutgram\Nutgram;
use Throwable;
@@ -22,51 +16,6 @@ class TelegramWebhookController extends Controller
{
app(TelegramBotContext::class)->setAgent(null);
return $this->handle(
$request,
expectedSecret: (string) config('coruna.telegram.webhook_secret', ''),
resolveBot: static fn () => app(Nutgram::class),
label: 'official',
);
}
public function agent(Request $request, User $agent): Response
{
if (! $agent->isEnabled() || ! $agent->hasTelegramBot()) {
abort(404);
}
app(TelegramBotContext::class)->setAgent($agent);
$secret = TelegramBotContext::webhookSecretFor($agent);
return $this->handle(
$request,
expectedSecret: $secret,
resolveBot: function () use ($agent) {
if (app()->runningUnitTests()) {
return app(Nutgram::class);
}
$bot = $this->makeAgentBot($agent);
app(TelegramRegistrar::class)->registerAgent($bot, $agent);
return $bot;
},
label: 'agent:'.$agent->id,
requireSecret: true,
);
}
/**
* @param callable(): Nutgram $resolveBot
*/
private function handle(
Request $request,
string $expectedSecret,
callable $resolveBot,
string $label,
bool $requireSecret = false,
): Response {
$raw = $request->getContent();
$update = $request->all();
if ($update === [] && is_string($raw) && $raw !== '') {
@@ -81,9 +30,9 @@ class TelegramWebhookController extends Controller
$text = is_string($message['text'] ?? null) ? $message['text'] : null;
$updateId = $update['update_id'] ?? null;
$header = (string) $request->header('X-Telegram-Bot-Api-Secret-Token', '');
$expectedSecret = (string) config('coruna.telegram.webhook_secret', '');
$this->webhookLog('info', 'telegram webhook hit', [
'bot' => $label,
'ip' => $request->ip(),
'update_id' => $updateId,
'chat_id' => $chatId,
@@ -93,10 +42,9 @@ class TelegramWebhookController extends Controller
'body_bytes' => strlen($raw),
]);
if ($requireSecret || $expectedSecret !== '') {
if ($expectedSecret === '' || $header === '' || ! hash_equals($expectedSecret, $header)) {
if ($expectedSecret !== '') {
if ($header === '' || ! hash_equals($expectedSecret, $header)) {
$this->webhookLog('warning', 'telegram webhook rejected: bad secret', [
'bot' => $label,
'ip' => $request->ip(),
'update_id' => $updateId,
]);
@@ -105,17 +53,15 @@ class TelegramWebhookController extends Controller
}
try {
$bot = $resolveBot();
$bot = app(Nutgram::class);
$bot->run();
$this->webhookLog('info', 'telegram webhook handled', [
'bot' => $label,
'update_id' => $updateId,
'chat_id' => $chatId,
'handler' => $bot->currentHandler()?->getPattern(),
]);
} catch (Throwable $e) {
$this->webhookLog('error', 'telegram webhook failed', [
'bot' => $label,
'message' => $e->getMessage(),
'exception' => $e::class,
'file' => $e->getFile().':'.$e->getLine(),
@@ -132,40 +78,6 @@ class TelegramWebhookController extends Controller
return response()->noContent();
}
private function makeAgentBot(User $agent): Nutgram
{
$configuration = new Configuration(
apiUrl: config('nutgram.config.api_url', Configuration::DEFAULT_API_URL),
botId: config('nutgram.config.bot_id'),
botName: config('nutgram.config.bot_name'),
testEnv: config('nutgram.config.test_env', false),
isLocal: config('nutgram.config.is_local', false),
clientTimeout: config('nutgram.config.timeout', Configuration::DEFAULT_CLIENT_TIMEOUT),
clientOptions: config('nutgram.config.client', []),
container: app(),
hydrator: config('nutgram.config.hydrator', Configuration::DEFAULT_HYDRATOR),
cache: app(Cache::class),
logger: app(LoggerInterface::class)->channel(config('nutgram.log_channel', 'null')),
localPathTransformer: config('nutgram.config.local_path_transformer'),
pollingTimeout: config('nutgram.config.polling.timeout', Configuration::DEFAULT_POLLING_TIMEOUT),
pollingAllowedUpdates: config('nutgram.config.polling.allowed_updates', Configuration::DEFAULT_ALLOWED_UPDATES),
pollingLimit: config('nutgram.config.polling.limit', Configuration::DEFAULT_POLLING_LIMIT),
enableHttp2: config('nutgram.config.enable_http2', Configuration::DEFAULT_ENABLE_HTTP2),
conversationTtl: config('nutgram.config.conversation_ttl', Configuration::DEFAULT_CONVERSATION_TTL),
);
$bot = new Nutgram((string) $agent->bot_token, $configuration);
$secret = TelegramBotContext::webhookSecretFor($agent);
$webhook = new LaravelWebhook(
getToken: static fn () => request()?->header('X-Telegram-Bot-Api-Secret-Token'),
secretToken: $secret,
);
$webhook->setSafeMode(true);
$bot->setRunningMode($webhook);
return $bot;
}
/** @param array<string, mixed> $context */
private function webhookLog(string $level, string $message, array $context = []): void
{