diff --git a/app/Actions/SocialAccount/ConnectTelegramChannel.php b/app/Actions/SocialAccount/ConnectTelegramChannel.php index 45743922..68d945be 100644 --- a/app/Actions/SocialAccount/ConnectTelegramChannel.php +++ b/app/Actions/SocialAccount/ConnectTelegramChannel.php @@ -10,6 +10,7 @@ use App\Features\SocialAccountLimit; use App\Models\SocialAccount; use App\Models\Workspace; +use App\Services\Social\TelegramApi; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Laravel\Pennant\Feature; @@ -79,27 +80,24 @@ public static function execute(Workspace $workspace, array $chat, string $nonce) */ private static function fetchChannelAvatar(string $chatId): ?string { - $token = (string) config('trypost.platforms.telegram.bot_token'); - $api = rtrim((string) config('trypost.platforms.telegram.api'), '/'); - - if ($token === '') { + if (TelegramApi::token() === '') { return null; } try { - $fileId = data_get(Http::get("{$api}/bot{$token}/getChat", ['chat_id' => $chatId])->json(), 'result.photo.big_file_id'); + $fileId = data_get(Http::get(TelegramApi::endpoint('getChat'), ['chat_id' => $chatId])->json(), 'result.photo.big_file_id'); if (! is_string($fileId)) { return null; } - $filePath = data_get(Http::get("{$api}/bot{$token}/getFile", ['file_id' => $fileId])->json(), 'result.file_path'); + $filePath = data_get(Http::get(TelegramApi::endpoint('getFile'), ['file_id' => $fileId])->json(), 'result.file_path'); if (! is_string($filePath)) { return null; } - return uploadFromUrl("{$api}/file/bot{$token}/{$filePath}"); + return uploadFromUrl(TelegramApi::fileUrl($filePath)); } catch (Throwable) { return null; } diff --git a/app/Actions/SocialAccount/RegisterTelegramWebhook.php b/app/Actions/SocialAccount/RegisterTelegramWebhook.php index 58f42dda..fbbd6206 100644 --- a/app/Actions/SocialAccount/RegisterTelegramWebhook.php +++ b/app/Actions/SocialAccount/RegisterTelegramWebhook.php @@ -4,6 +4,7 @@ namespace App\Actions\SocialAccount; +use App\Services\Social\TelegramApi; use Illuminate\Support\Facades\Http; use InvalidArgumentException; use RuntimeException; @@ -20,17 +21,15 @@ class RegisterTelegramWebhook */ public static function execute(): string { - $token = (string) config('trypost.platforms.telegram.bot_token'); - $api = rtrim((string) config('trypost.platforms.telegram.api'), '/'); $secret = (string) config('trypost.platforms.telegram.webhook_secret'); - if ($token === '' || $secret === '') { + if (TelegramApi::token() === '' || $secret === '') { throw new InvalidArgumentException('TELEGRAM_BOT_TOKEN and TELEGRAM_WEBHOOK_SECRET must both be set.'); } $url = route('telegram.webhook'); - $response = Http::post("{$api}/bot{$token}/setWebhook", [ + $response = Http::post(TelegramApi::endpoint('setWebhook'), [ 'url' => $url, 'secret_token' => $secret, 'allowed_updates' => ['message', 'channel_post', 'message_reaction_count'], diff --git a/app/Services/Social/ConnectionVerifier.php b/app/Services/Social/ConnectionVerifier.php index 4fb3da56..400d079e 100644 --- a/app/Services/Social/ConnectionVerifier.php +++ b/app/Services/Social/ConnectionVerifier.php @@ -507,11 +507,8 @@ private function verifyBluesky(SocialAccount $account): bool private function verifyTelegram(SocialAccount $account): bool { - $token = (string) config('trypost.platforms.telegram.bot_token'); - $api = rtrim((string) config('trypost.platforms.telegram.api'), '/'); - // getChat succeeds only while the bot can still reach the chat. - $response = Http::get("{$api}/bot{$token}/getChat", [ + $response = Http::get(TelegramApi::endpoint('getChat'), [ 'chat_id' => data_get($account->meta, 'chat_id'), ]); diff --git a/app/Services/Social/TelegramAnalytics.php b/app/Services/Social/TelegramAnalytics.php index 196809e0..35d5a0d4 100644 --- a/app/Services/Social/TelegramAnalytics.php +++ b/app/Services/Social/TelegramAnalytics.php @@ -18,17 +18,15 @@ class TelegramAnalytics */ public function getMetrics(SocialAccount $account): array { - $token = (string) config('trypost.platforms.telegram.bot_token'); - $api = rtrim((string) config('trypost.platforms.telegram.api'), '/'); $chatId = data_get($account->meta, 'chat_id'); - if ($token === '' || $chatId === null) { + if (TelegramApi::token() === '' || $chatId === null) { return []; } try { $count = data_get( - Http::get("{$api}/bot{$token}/getChatMemberCount", ['chat_id' => $chatId])->json(), + Http::get(TelegramApi::endpoint('getChatMemberCount'), ['chat_id' => $chatId])->json(), 'result', ); } catch (Throwable) { diff --git a/app/Services/Social/TelegramApi.php b/app/Services/Social/TelegramApi.php new file mode 100644 index 00000000..1d9d0dd5 --- /dev/null +++ b/app/Services/Social/TelegramApi.php @@ -0,0 +1,44 @@ +/` shape and config keys live in one place. + */ +class TelegramApi +{ + public static function token(): string + { + return (string) config('trypost.platforms.telegram.bot_token'); + } + + /** + * Endpoint for a Bot API method, e.g. `https://api.telegram.org/bot/sendMessage`. + */ + public static function endpoint(string $method): string + { + $base = self::baseUrl(); + $token = self::token(); + + return "{$base}/bot{$token}/{$method}"; + } + + /** + * Download URL for a file path returned by `getFile`. + */ + public static function fileUrl(string $path): string + { + $base = self::baseUrl(); + $token = self::token(); + + return "{$base}/file/bot{$token}/{$path}"; + } + + private static function baseUrl(): string + { + return rtrim((string) config('trypost.platforms.telegram.api'), '/'); + } +} diff --git a/app/Services/Social/TelegramPublisher.php b/app/Services/Social/TelegramPublisher.php index 8772fda5..073471f4 100644 --- a/app/Services/Social/TelegramPublisher.php +++ b/app/Services/Social/TelegramPublisher.php @@ -141,10 +141,7 @@ private function telegramMedia(MediaItem $media): array private function call(string $method, array $payload): Response { - $token = (string) config('trypost.platforms.telegram.bot_token'); - $api = rtrim((string) config('trypost.platforms.telegram.api'), '/'); - - $response = $this->socialHttp()->post("{$api}/bot{$token}/{$method}", $payload); + $response = $this->socialHttp()->post(TelegramApi::endpoint($method), $payload); if ($response->failed() || data_get($response->json(), 'ok') !== true) { $this->handleApiError($response); diff --git a/tests/Unit/Services/Social/TelegramApiTest.php b/tests/Unit/Services/Social/TelegramApiTest.php new file mode 100644 index 00000000..bcab8ef5 --- /dev/null +++ b/tests/Unit/Services/Social/TelegramApiTest.php @@ -0,0 +1,29 @@ + 'TESTTOKEN', + 'trypost.platforms.telegram.api' => 'https://api.telegram.org', + ]); +}); + +it('builds a method endpoint from the token and host', function () { + expect(TelegramApi::endpoint('sendMessage')) + ->toBe('https://api.telegram.org/botTESTTOKEN/sendMessage'); +}); + +it('builds a file download url', function () { + expect(TelegramApi::fileUrl('photos/file_1.jpg')) + ->toBe('https://api.telegram.org/file/botTESTTOKEN/photos/file_1.jpg'); +}); + +it('trims a trailing slash from the configured host', function () { + config(['trypost.platforms.telegram.api' => 'https://api.telegram.org/']); + + expect(TelegramApi::endpoint('getChat')) + ->toBe('https://api.telegram.org/botTESTTOKEN/getChat'); +});