diff --git a/core/app/Http/Controllers/TelegramWebhookController.php b/core/app/Http/Controllers/TelegramWebhookController.php index e4a176e..e2eaba6 100644 --- a/core/app/Http/Controllers/TelegramWebhookController.php +++ b/core/app/Http/Controllers/TelegramWebhookController.php @@ -67,21 +67,31 @@ class TelegramWebhookController extends Controller $this->telegram->touch($chatId); } + $callbackId = (string) ($callback['id'] ?? ''); [$action, $token] = array_pad(explode(':', (string) ($callback['data'] ?? ''), 2), 2, ''); $delivery = Delivery::where('action_token', $token)->with('event.user')->first(); if ($delivery === null || $delivery->event->isTerminal() || ! in_array($action, ['ack', 'done'], true)) { - $this->telegram->answerCallback((string) $callback['id'], 'Уже неактуально'); + $this->answerCallback($callbackId, 'Уже неактуально'); return; } if ($action === 'done') { $this->actions->done($delivery->event); - $this->telegram->answerCallback((string) $callback['id'], 'Закрыто'); + $this->answerCallback($callbackId, 'Закрыто'); } else { $this->actions->ack($delivery->event); - $this->telegram->answerCallback((string) $callback['id'], 'Ок, напомню позже'); + $this->answerCallback($callbackId, 'Ок, напомню позже'); } } + + private function answerCallback(string $callbackId, string $text): void + { + if ($callbackId === '') { + // Нет id — отвечать нечему (malformed payload), но не считаем это ошибкой. + return; + } + $this->telegram->answerCallback($callbackId, $text); + } } diff --git a/core/routes/web.php b/core/routes/web.php index 883c8f6..40cc23a 100644 --- a/core/routes/web.php +++ b/core/routes/web.php @@ -22,6 +22,6 @@ Route::middleware('remote.user')->group(function () { Route::post('/me/events/{event}/done', [MeEvents::class, 'done']); Route::get('/me/channels', [ChannelsController::class, 'index']); Route::post('/me/channels', [ChannelsController::class, 'store']); - Route::delete('/me/channels/{channel}', [ChannelsController::class, 'destroy']); + Route::delete('/me/channels/{channel}', [ChannelsController::class, 'destroy'])->whereNumber('channel'); Route::post('/me/channels/telegram/link', [ChannelsController::class, 'telegramLink']); }); diff --git a/core/tests/Feature/ChannelsApiTest.php b/core/tests/Feature/ChannelsApiTest.php index 233abc4..3e1353d 100644 --- a/core/tests/Feature/ChannelsApiTest.php +++ b/core/tests/Feature/ChannelsApiTest.php @@ -49,6 +49,8 @@ class ChannelsApiTest extends TestCase $this->getJson('/me', ['X-Remote-User' => 'other']); $foreign = Channel::create(['user_id' => User::where('login', 'other')->sole()->id, 'type' => ChannelType::Webhook, 'config' => ['deliver_url' => 'u', 'presence_url' => 'p'], 'enabled' => true]); $this->deleteJson('/me/channels/'.$foreign->id, [], $this->as)->assertNotFound(); + + $this->deleteJson('/me/channels/abc', [], $this->as)->assertNotFound(); } public function test_telegram_link_code(): void diff --git a/core/tests/Feature/TelegramWebhookTest.php b/core/tests/Feature/TelegramWebhookTest.php index f805320..7dc5f96 100644 --- a/core/tests/Feature/TelegramWebhookTest.php +++ b/core/tests/Feature/TelegramWebhookTest.php @@ -96,4 +96,12 @@ class TelegramWebhookTest extends TestCase { $this->postJson('/hooks/telegram', ['edited_message' => ['chat' => ['id' => 1]]], $this->secret())->assertNoContent(); } + + public function test_malformed_callback_is_ignored(): void + { + $this->postJson('/hooks/telegram', ['callback_query' => ['data' => 'ack:nope']], $this->secret())->assertNoContent(); + + $this->postJson('/hooks/telegram', ['callback_query' => ['id' => 'x', 'data' => 'garbage']], $this->secret())->assertNoContent(); + Http::assertSent(fn (Request $r) => str_ends_with($r->url(), '/answerCallbackQuery') && $r['text'] === 'Уже неактуально'); + } }