fix(telegram): trim bot token and chat id before sending

Closes #12323

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-26 12:58:02 +01:00
co-authored by Claude Opus 5.5
parent d6c4e773b9
commit 4e7fdd4ac6
4 changed files with 33 additions and 5 deletions
+5 -4
View File
@@ -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) {
+3 -1
View File
@@ -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;
@@ -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'],
@@ -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');
});