diff --git a/app/Jobs/SendMessageToTelegramJob.php b/app/Jobs/SendMessageToTelegramJob.php index 87d9625eed..cf7d5c25c3 100644 --- a/app/Jobs/SendMessageToTelegramJob.php +++ b/app/Jobs/SendMessageToTelegramJob.php @@ -53,8 +53,8 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue { $response = $this->sendMessage(); - // 401: invalid bot token, 403: the bot was blocked or removed from the chat, 400 "chat not found": wrong chat ID. - $isConfigurationError = in_array($response->status(), [401, 403], true) + // 401: invalid bot token, 404: malformed bot token, 403: the bot was blocked or removed from the chat, 400 "chat not found": wrong chat ID. + $isConfigurationError = in_array($response->status(), [401, 403, 404], true) || ($response->status() === 400 && Str::contains((string) $response->json('description'), 'chat not found', true)); $this->handleNotificationResponse($response, 'Telegram', $isConfigurationError); } @@ -64,7 +64,8 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue */ public function sendMessage(): Response { - $url = 'https://api.telegram.org/bot'.$this->token.'/sendMessage'; + // Pasted values often keep a space or new line. In the URL it gives a malformed token, so Telegram answers 404. + $url = 'https://api.telegram.org/bot'.trim($this->token).'/sendMessage'; $inlineButtons = []; if (! empty($this->buttons)) { foreach ($this->buttons as $button) { @@ -86,7 +87,7 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue [...$inlineButtons], ], ]), - 'chat_id' => $this->chatId, + 'chat_id' => trim($this->chatId), 'text' => $this->text, ]; if ($this->threadId) { diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index f45bc9045c..5992aac048 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -318,7 +318,9 @@ class Telegram extends Component ))->sendMessage(); if ($response->failed()) { - $reason = $response->json('description') ?? "HTTP {$response->status()}"; + $reason = in_array($response->status(), [401, 404], true) + ? 'The Bot API token is not valid. Copy it again from @BotFather.' + : ($response->json('description') ?? "HTTP {$response->status()}"); $this->dispatch('error', 'Telegram rejected the test notification.', e($reason)); return; diff --git a/tests/Feature/NotificationDeliveryFailureTest.php b/tests/Feature/NotificationDeliveryFailureTest.php index 64e31385c9..e0bffa0064 100644 --- a/tests/Feature/NotificationDeliveryFailureTest.php +++ b/tests/Feature/NotificationDeliveryFailureTest.php @@ -66,6 +66,7 @@ dataset('configuration errors', [ 'discord deleted webhook' => ['discord', 404, ['message' => 'Unknown Webhook', 'code' => 10015]], 'slack archived channel' => ['slack', 410, 'channel_is_archived'], 'telegram invalid token' => ['telegram', 401, ['ok' => false, 'description' => 'Unauthorized']], + 'telegram malformed token' => ['telegram', 404, ['ok' => false, 'error_code' => 404, 'description' => 'Not Found']], 'telegram wrong chat' => ['telegram', 400, ['ok' => false, 'description' => 'Bad Request: chat not found']], 'pushover invalid token' => ['pushover', 400, ['token' => 'invalid', 'errors' => ['application token is invalid'], 'status' => 0]], 'webhook not found' => ['webhook', 404, 'Not Found'], diff --git a/tests/Feature/TelegramTestNotificationTest.php b/tests/Feature/TelegramTestNotificationTest.php index ce9640406a..9716aabfb0 100644 --- a/tests/Feature/TelegramTestNotificationTest.php +++ b/tests/Feature/TelegramTestNotificationTest.php @@ -50,3 +50,27 @@ it('sends the test notification synchronously and reports success', function () && $request['chat_id'] === '123456'); Queue::assertNothingPushed(); }); + +it('ignores whitespace around the stored bot token and chat ID', function () { + $this->team->telegramNotificationSettings->update([ + 'telegram_token' => " bot-token \n", + 'telegram_chat_id' => ' 123456 ', + ]); + Http::fake(['api.telegram.org/*' => Http::response(['ok' => true, 'result' => []])]); + + Livewire::test(TelegramNotification::class) + ->call('sendTestNotification') + ->assertDispatched('success', 'Test notification sent.'); + + Http::assertSent(fn ($request) => $request->url() === 'https://api.telegram.org/botbot-token/sendMessage' + && $request['chat_id'] === '123456'); +}); + +it('tells the user to check the bot token when Telegram does not find the bot', function () { + Http::fake(['api.telegram.org/*' => Http::response(['ok' => false, 'error_code' => 404, 'description' => 'Not Found'], 404)]); + + Livewire::test(TelegramNotification::class) + ->call('sendTestNotification') + ->assertDispatched('error', 'Telegram rejected the test notification.', 'The Bot API token is not valid. Copy it again from @BotFather.') + ->assertNotDispatched('success'); +});