This commit is contained in:
hashbro
2026-08-07 06:26:49 +08:00
parent 1026ce1285
commit 03a40105fc
5 changed files with 42 additions and 6 deletions
+3
View File
@@ -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 <fromAddress> [TRX|USDT] [amount] — omit amount = all
@@ -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.
@@ -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);
});
}
}
@@ -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;
}
+4 -2
View File
@@ -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' => [],