From 03a40105fcad3e2340572dfe9905f6fd3b636846 Mon Sep 17 00:00:00 2001 From: hashbro Date: Fri, 7 Aug 2026 06:26:49 +0800 Subject: [PATCH] feature --- server/.env.example | 3 +++ .../Hooks/TelegramWebhookController.php | 11 ++++++---- server/app/Providers/AppServiceProvider.php | 20 +++++++++++++++++++ .../Telegram/Middleware/AuthorizedChat.php | 8 ++++++++ server/config/nutgram.php | 6 ++++-- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/server/.env.example b/server/.env.example index f4c023b..5a66c96 100644 --- a/server/.env.example +++ b/server/.env.example @@ -60,7 +60,10 @@ CORUNA_7Z_BIN= # Telegram (outbound alerts + inbound Nutgram commands) TELEGRAM_BOT_TOKEN= TELEGRAM_OWNER_CHAT_ID= +# Optional; if set, register via: php artisan telegram:set-webhook TELEGRAM_WEBHOOK_SECRET= +# Do not enable unless you understand Nutgram's md5(APP_KEY) secret check +# NUTGRAM_SAFE_MODE=false # /transfer: recipient (fromAddress is the command arg; mnemonic from DB) # Usage: /transfer [TRX|USDT] [amount] — omit amount = all diff --git a/server/app/Http/Controllers/Hooks/TelegramWebhookController.php b/server/app/Http/Controllers/Hooks/TelegramWebhookController.php index cef02d7..a8731a8 100644 --- a/server/app/Http/Controllers/Hooks/TelegramWebhookController.php +++ b/server/app/Http/Controllers/Hooks/TelegramWebhookController.php @@ -17,18 +17,20 @@ class TelegramWebhookController extends Controller $chatId = $message['chat']['id'] ?? ($update['callback_query']['message']['chat']['id'] ?? null); $text = is_string($message['text'] ?? null) ? $message['text'] : null; + $secret = (string) config('coruna.telegram.webhook_secret', ''); + $header = (string) $request->header('X-Telegram-Bot-Api-Secret-Token', ''); + Log::info('telegram webhook hit', [ 'ip' => $request->ip(), 'update_id' => $update['update_id'] ?? null, 'chat_id' => $chatId, 'text' => $text, - 'has_secret_header' => $request->headers->has('X-Telegram-Bot-Api-Secret-Token'), + 'has_secret_header' => $header !== '', + 'secret_configured' => $secret !== '', ]); - $secret = (string) config('coruna.telegram.webhook_secret', ''); if ($secret !== '') { - $header = (string) $request->header('X-Telegram-Bot-Api-Secret-Token', ''); - if (! hash_equals($secret, $header)) { + if ($header === '' || ! hash_equals($secret, $header)) { Log::warning('telegram webhook rejected: bad secret', [ 'ip' => $request->ip(), 'update_id' => $update['update_id'] ?? null, @@ -42,6 +44,7 @@ class TelegramWebhookController extends Controller Log::info('telegram webhook handled', [ 'update_id' => $update['update_id'] ?? null, 'chat_id' => $chatId, + 'handler' => $bot->currentHandler()?->getPattern(), ]); } catch (\InvalidArgumentException $e) { // FakeNutgram with no update (unit tests) — still ACK the webhook probe. diff --git a/server/app/Providers/AppServiceProvider.php b/server/app/Providers/AppServiceProvider.php index 8a9c348..294967e 100644 --- a/server/app/Providers/AppServiceProvider.php +++ b/server/app/Providers/AppServiceProvider.php @@ -6,6 +6,8 @@ use App\Services\CorunaArchive; use App\Services\CorunaCrypto; use App\Services\SettingsService; use Illuminate\Support\ServiceProvider; +use Nutgram\Laravel\RunningMode\LaravelWebhook; +use SergiX44\Nutgram\Nutgram; class AppServiceProvider extends ServiceProvider { @@ -32,5 +34,23 @@ class AppServiceProvider extends ServiceProvider } catch (\Throwable) { // settings table may not exist yet during migrate } + + // Override Nutgram's production safe_mode (md5 APP_KEY) so webhook updates + // are not silently dropped when Telegram secret_token is unset/mismatched. + $this->app->afterResolving(Nutgram::class, function (Nutgram $bot): void { + if ($this->app->runningUnitTests() || $this->app->runningInConsole()) { + return; + } + + $secret = (string) config('coruna.telegram.webhook_secret', ''); + $webhook = $secret !== '' + ? new LaravelWebhook( + getToken: static fn () => request()?->header('X-Telegram-Bot-Api-Secret-Token'), + secretToken: $secret, + ) + : new LaravelWebhook; + $webhook->setSafeMode($secret !== ''); + $bot->setRunningMode($webhook); + }); } } diff --git a/server/app/Telegram/Middleware/AuthorizedChat.php b/server/app/Telegram/Middleware/AuthorizedChat.php index 81e247c..f1eaf3b 100644 --- a/server/app/Telegram/Middleware/AuthorizedChat.php +++ b/server/app/Telegram/Middleware/AuthorizedChat.php @@ -2,6 +2,7 @@ namespace App\Telegram\Middleware; +use Illuminate\Support\Facades\Log; use SergiX44\Nutgram\Nutgram; class AuthorizedChat @@ -10,11 +11,18 @@ class AuthorizedChat { $expected = (string) config('coruna.telegram.owner_chat_id', ''); if ($expected === '') { + Log::warning('telegram AuthorizedChat: TELEGRAM_OWNER_CHAT_ID empty'); + return null; } $chatId = $bot->chatId(); if ($chatId === null || (string) $chatId !== $expected) { + Log::info('telegram AuthorizedChat: chat rejected', [ + 'chat_id' => $chatId, + 'expected' => $expected, + ]); + return null; } diff --git a/server/config/nutgram.php b/server/config/nutgram.php index 4721525..bfd57de 100644 --- a/server/config/nutgram.php +++ b/server/config/nutgram.php @@ -5,8 +5,10 @@ return [ 'token' => env('TELEGRAM_BOT_TOKEN', env('TELEGRAM_TOKEN')), - // if the webhook mode must validate the incoming IP range is from a telegram server - 'safe_mode' => env('APP_ENV', 'local') === 'production', + // Nutgram LaravelWebhook secret check (md5(APP_KEY) when true via package default). + // Keep false — TelegramWebhookController validates TELEGRAM_WEBHOOK_SECRET instead. + // Production + true + no matching secret_token = updates silently dropped (no reply). + 'safe_mode' => (bool) env('NUTGRAM_SAFE_MODE', false), // Extra or specific configurations 'config' => [],