├── src ├── Handlers │ └── EmptyWebhookHandler.php ├── Contracts │ ├── Downloadable.php │ ├── Storable.php │ └── StorageDriver.php ├── Models │ └── Concerns │ │ └── HasCustomUrl.php ├── Enums │ ├── Emojis.php │ ├── ReplyButtonType.php │ ├── ChatPermissions.php │ ├── ChatActions.php │ └── ChatAdminPermissions.php ├── Exceptions │ ├── ChatThreadException.php │ ├── InvoiceException.php │ ├── InlineQueryException.php │ ├── KeyboardException.php │ ├── StorageException.php │ ├── ChatSettingsException.php │ ├── TelegramUpdatesException.php │ ├── TelegramWebhookException.php │ ├── BotCommandException.php │ ├── TelegraphException.php │ ├── TelegraphPollException.php │ └── FileException.php ├── Concerns │ ├── InteractWithUsers.php │ ├── InteractsWithPayments.php │ ├── BuildsFromTelegraphClass.php │ ├── CallTraitsMethods.php │ ├── HasStorage.php │ ├── CreatesScopedPayloads.php │ ├── SendsPolls.php │ ├── StoresFiles.php │ ├── InteractsWithCommands.php │ └── AnswersInlineQueries.php ├── Controllers │ └── WebhookController.php ├── ScopedPayloads │ ├── TelegraphPollPayload.php │ ├── SetChatMenuButtonPayload.php │ ├── TelegraphQuizPayload.php │ └── TelegraphEditMediaPayload.php ├── DTO │ ├── Attachment.php │ ├── InlineQueryResult.php │ ├── Location.php │ ├── Photo.php │ ├── ReactionType.php │ ├── Voice.php │ ├── PollOption.php │ ├── Contact.php │ ├── WriteAccessAllowed.php │ ├── OrderInfo.php │ ├── InlineQueryResultVoice.php │ ├── PollAnswer.php │ ├── Invoice.php │ ├── Document.php │ ├── VideoNote.php │ ├── InlineQueryResultAudio.php │ ├── InlineQuery.php │ ├── CallbackQuery.php │ ├── ShippingAddress.php │ ├── RefundedPayment.php │ ├── Audio.php │ ├── ChatJoinRequest.php │ ├── Venue.php │ ├── User.php │ ├── InlineQueryResultGif.php │ ├── InlineQueryResultPhoto.php │ ├── Animation.php │ ├── Entity.php │ ├── Video.php │ ├── PreCheckoutQuery.php │ ├── InlineQueryResultDocument.php │ ├── InlineQueryResultVideo.php │ ├── Chat.php │ ├── Game.php │ ├── Sticker.php │ ├── InlineQueryResultMpeg4Gif.php │ ├── InlineQueryResultContact.php │ ├── InlineQueryResultArticle.php │ ├── ChatMemberUpdate.php │ ├── InlineQueryResultVenue.php │ └── Poll.php ├── Support │ └── Testing │ │ └── Fakes │ │ ├── TelegraphEditMediaFake.php │ │ ├── TelegraphSetChatMenuButtonFake.php │ │ ├── TelegraphQuizFake.php │ │ └── TelegraphPollFake.php ├── Client │ └── TelegraphResponse.php ├── TelegraphServiceProvider.php ├── Proxies │ ├── KeyboardButtonProxy.php │ └── ReplyKeyboardButtonProxy.php ├── Commands │ ├── GetTelegramWebhookDebugInfoCommand.php │ ├── UnsetTelegramWebhookCommand.php │ ├── CreateNewChatCommand.php │ ├── SetTelegramWebhookCommand.php │ └── CreateNewBotCommand.php ├── Jobs │ └── SendRequestToTelegramJob.php ├── Storage │ ├── StorageDriver.php │ ├── FileStorageDriver.php │ └── CacheStorageDriver.php ├── Games │ └── TelegraphGamePayload.php └── Keyboard │ └── ReplyButton.php ├── resources └── lang │ ├── uz │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── ar │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── en │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── es │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── fa │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── it │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── nl │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── pt │ ├── misc.php │ ├── errors.php │ └── commands.php │ ├── ru │ ├── misc.php │ ├── errors.php │ └── commands.php │ └── uk │ ├── misc.php │ ├── errors.php │ └── commands.php ├── routes └── api.php ├── database ├── factories │ ├── TelegraphBotFactory.php │ └── TelegraphChatFactory.php └── migrations │ ├── create_telegraph_bots_table.php │ └── create_telegraph_chats_table.php ├── LICENSE.md └── composer.json /src/Handlers/EmptyWebhookHandler.php: -------------------------------------------------------------------------------- 1 | 'xa', 12 | 'no' => 'yo’q', 13 | ]; 14 | -------------------------------------------------------------------------------- /src/Enums/Emojis.php: -------------------------------------------------------------------------------- 1 | 'نعم', 12 | 'no' => 'لا', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/en/misc.php: -------------------------------------------------------------------------------- 1 | 'yes', 12 | 'no' => 'no', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/es/misc.php: -------------------------------------------------------------------------------- 1 | 'si', 12 | 'no' => 'no', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/fa/misc.php: -------------------------------------------------------------------------------- 1 | 'بله', 12 | 'no' => 'خیر', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/it/misc.php: -------------------------------------------------------------------------------- 1 | 'sì', 12 | 'no' => 'no', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/nl/misc.php: -------------------------------------------------------------------------------- 1 | 'ja', 12 | 'no' => 'nee', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/pt/misc.php: -------------------------------------------------------------------------------- 1 | 'sim', 12 | 'no' => 'não', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/ru/misc.php: -------------------------------------------------------------------------------- 1 | 'да', 12 | 'no' => 'нет', 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/lang/uk/misc.php: -------------------------------------------------------------------------------- 1 | 'так', 12 | 'no' => 'ні', 13 | ]; 14 | -------------------------------------------------------------------------------- /src/Enums/ReplyButtonType.php: -------------------------------------------------------------------------------- 1 | toJson()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | middleware(config('telegraph.webhook.middleware', [])) 10 | ->name('telegraph.webhook'); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/Exceptions/InlineQueryException.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_GET_USER_PROFILE_PHOTOS; 21 | $telegraph->data['user_id'] = $userId; 22 | 23 | return $telegraph; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/factories/TelegraphBotFactory.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class TelegraphBotFactory extends Factory 12 | { 13 | protected $model = TelegraphBot::class; 14 | 15 | /** 16 | * @inheritDoc 17 | */ 18 | public function definition(): array 19 | { 20 | return [ 21 | 'token' => $this->faker->uuid, 22 | 'name' => $this->faker->word, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/migrations/create_telegraph_bots_table.php: -------------------------------------------------------------------------------- 1 | id(); 12 | $table->string('token')->unique(); 13 | $table->string('name')->nullable(); 14 | 15 | $table->timestamps(); 16 | }); 17 | } 18 | 19 | public function down(): void 20 | { 21 | Schema::dropIfExists('telegraph_bots'); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/Concerns/InteractsWithPayments.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_REFUND_STAR_PAYMENT; 20 | $telegraph->data['user_id'] = $userId; 21 | $telegraph->data['telegram_payment_charge_id'] = $telegramPaymentChargeId; 22 | 23 | return $telegraph; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Exceptions/ChatSettingsException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class TelegraphChatFactory extends Factory 13 | { 14 | protected $model = TelegraphChat::class; 15 | 16 | /** 17 | * @inheritDoc 18 | */ 19 | public function definition(): array 20 | { 21 | return [ 22 | 'chat_id' => $this->faker->randomNumber(), 23 | 'name' => $this->faker->word, 24 | 'telegraph_bot_id' => fn () => TelegraphBot::factory()->create(), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Exceptions/TelegramUpdatesException.php: -------------------------------------------------------------------------------- 1 | name bot while a webhook is set. First, delete the webhook with [artisan telegraph:delete-webhook $bot->id] or programmatically calling [\$bot->deleteWebhook()]"); 12 | } 13 | 14 | public static function pollingError(TelegraphBot $bot, string $errorMessage): TelegramUpdatesException 15 | { 16 | return new self("Cannot retrieve updates for $bot->name bot: $errorMessage"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/lang/uk/errors.php: -------------------------------------------------------------------------------- 1 | 'Неприпустима дія', 12 | 'invalid_command' => 'Невідома команда', 13 | 'empty_token' => 'Токен не може бути пустим', 14 | 'empty_chat_id' => 'Ідентифіктор чата не може бути пусти', 15 | 'missing_bot_id' => 'Будь ласка, вкажіть ідентифікатор боту', 16 | 'failed_to_get_log_from_telegram' => 'Не вдалось отримати журнал логів з сервера Телеграм', 17 | 'failed_to_register_webhook' => 'Помилка реєстрації вебхука', 18 | 'failed_to_unregister_webhook' => 'Помилка видалення вебхука', 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/nl/errors.php: -------------------------------------------------------------------------------- 1 | 'Ongeldige actie', 14 | 'invalid_command' => 'Onbekend commando', 15 | 'empty_token' => 'Token mag niet leeg zijn', 16 | 'empty_chat_id' => 'Chat ID mag niet leeg zijn', 17 | 'missing_bot_id' => 'Geef een Bot ID', 18 | 'failed_to_get_log_from_telegram' => 'Ophalen log van Telegram server mislukt', 19 | 'failed_to_register_webhook' => 'Instellen webhook mislukt', 20 | 'failed_to_unregister_webhook' => 'Verwijderen webhook mislukt', 21 | ]; 22 | -------------------------------------------------------------------------------- /src/Concerns/BuildsFromTelegraphClass.php: -------------------------------------------------------------------------------- 1 | getProperties(); 16 | 17 | foreach ($properties as $property) { 18 | $propertyName = $property->name; 19 | $property->setAccessible(true); 20 | 21 | if ($property->isInitialized($telegraph)) { 22 | $newInstance->$propertyName = $property->getValue($telegraph); 23 | } 24 | } 25 | 26 | return $newInstance; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /resources/lang/ar/errors.php: -------------------------------------------------------------------------------- 1 | 'إجراء غير صالح', 14 | 'invalid_command' => 'أمر غير معروف', 15 | 'empty_token' => 'التوكن لا يمكن أن يكون فارغاً', 16 | 'empty_chat_id' => 'معرف المحادثة لا يمكن أن يكون فارغاً', 17 | 'missing_bot_id' => 'يرجى تحديد معرف الروبوت', 18 | 'failed_to_get_log_from_telegram' => 'فشل في الحصول على السجل من خادم تيليجرام', 19 | 'failed_to_register_webhook' => 'فشل في تسجيل الخطاف (Webhook)', 20 | 'failed_to_unregister_webhook' => 'فشل في إلغاء تسجيل الخطاف (Webhook)', 21 | ]; 22 | -------------------------------------------------------------------------------- /database/migrations/create_telegraph_chats_table.php: -------------------------------------------------------------------------------- 1 | id(); 12 | $table->string('chat_id'); 13 | $table->string('name')->nullable(); 14 | 15 | $table->foreignId('telegraph_bot_id')->constrained('telegraph_bots')->cascadeOnDelete(); 16 | $table->timestamps(); 17 | 18 | $table->unique(['chat_id', 'telegraph_bot_id']); 19 | }); 20 | } 21 | 22 | public function down(): void 23 | { 24 | Schema::dropIfExists('telegraph_chats'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /resources/lang/es/errors.php: -------------------------------------------------------------------------------- 1 | 'Acción no válida', 14 | 'invalid_command' => 'Comando desconocido', 15 | 'empty_token' => 'El token no puede estar vacío', 16 | 'empty_chat_id' => 'El ID de chat no puede estar vacío', 17 | 'missing_bot_id' => 'Especifique un ID de bot', 18 | 'failed_to_get_log_from_telegram' => 'No se pudo obtener el log del servidor de Telegram', 19 | 'failed_to_register_webhook' => 'No se pudo registrar el webhook', 20 | 'failed_to_unregister_webhook' => 'Error al anular el registro del webhook', 21 | ]; 22 | -------------------------------------------------------------------------------- /resources/lang/it/errors.php: -------------------------------------------------------------------------------- 1 | 'Azione non valida', 14 | 'invalid_command' => 'Comando non valido', 15 | 'empty_token' => 'Il token non può essere vuoto', 16 | 'empty_chat_id' => "l'ID della chat non può essere vuoto", 17 | 'missing_bot_id' => "Occorre inserire l'ID del bot", 18 | 'failed_to_get_log_from_telegram' => 'Impossibile recuperare i log dal server di Telegram', 19 | 'failed_to_register_webhook' => 'Registrazione del webhook fallita', 20 | 'failed_to_unregister_webhook' => 'Eliminazione del webhook fallita', 21 | ]; 22 | -------------------------------------------------------------------------------- /resources/lang/en/errors.php: -------------------------------------------------------------------------------- 1 | 'Invalid action', 14 | 'invalid_command' => 'Unknown command', 15 | 'empty_token' => 'Token cannot be empty', 16 | 'empty_chat_id' => 'Chat ID cannot be empty', 17 | 'missing_bot_id' => 'Please specify a Bot ID', 18 | 'failed_to_get_log_from_telegram' => 'Failed to get log from Telegram server', 19 | 'failed_to_register_webhook' => 'Failed to register webhook', 20 | 'failed_to_unregister_webhook' => 'Failed to unregister webhook', 21 | 'webhook_error_occurred' => 'Sorry, an error occurred', 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/lang/fa/errors.php: -------------------------------------------------------------------------------- 1 | 'عملیات نامعتبر', 14 | 'invalid_command' => 'دستور ناشناخته', 15 | 'empty_token' => 'توکن نمی‌تواند خالی باشد', 16 | 'empty_chat_id' => 'شناسه چت نمی‌تواند خالی باشد', 17 | 'missing_bot_id' => 'لطفاً شناسه ربات را مشخص کنید', 18 | 'failed_to_get_log_from_telegram' => 'دریافت گزارش از سرور تلگرام ناموفق بود', 19 | 'failed_to_register_webhook' => 'ثبت وب‌هوک ناموفق بود', 20 | 'failed_to_unregister_webhook' => 'حذف وب‌هوک ناموفق بود', 21 | 'webhook_error_occurred' => 'متأسفانه، خطایی رخ داد', 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/lang/pt/errors.php: -------------------------------------------------------------------------------- 1 | 'Ação inválida', 14 | 'invalid_command' => 'Comando desconhecido', 15 | 'empty_token' => 'Token não pode estar vazio', 16 | 'empty_chat_id' => 'O ID do chat não pode estar vazio', 17 | 'missing_bot_id' => 'Especifique um ID de bot', 18 | 'failed_to_get_log_from_telegram' => 'Falha ao obter o registo do servidor do Telegram', 19 | 'failed_to_register_webhook' => 'Falha ao registar o webhook', 20 | 'failed_to_unregister_webhook' => 'Falha ao cancelar o registo do webhook', 21 | 'webhook_error_occurred' => 'Desculpe, ocorreu um erro', 22 | ]; 23 | -------------------------------------------------------------------------------- /src/Concerns/CallTraitsMethods.php: -------------------------------------------------------------------------------- 1 | getTraitNames() as $trait) { 21 | $trait = class_basename($trait); 22 | 23 | $traitMethod = "$methodName$trait"; 24 | 25 | if (method_exists($this, $traitMethod)) { 26 | $pipeable = $this->$traitMethod($pipeable, ...$params); 27 | } 28 | } 29 | 30 | return $pipeable; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/lang/ru/errors.php: -------------------------------------------------------------------------------- 1 | 'Недопустимое действие', 14 | 'invalid_command' => 'Неизвестная команда', 15 | 'empty_token' => 'Токен не может быть пустым', 16 | 'empty_chat_id' => 'Идентификатор чата не может быть пустым', 17 | 'missing_bot_id' => 'Пожалуйста, укажите идентификатор бота', 18 | 'failed_to_get_log_from_telegram' => 'Не удалось получить журнал логов с сервера Телеграм', 19 | 'failed_to_register_webhook' => 'Ошибка регистрации вебхука', 20 | 'failed_to_unregister_webhook' => 'Ошибка удаления вебхука', 21 | 'webhook_error_occurred' => 'Извините, произошла ошибка', 22 | ]; 23 | -------------------------------------------------------------------------------- /src/Enums/ChatPermissions.php: -------------------------------------------------------------------------------- 1 | getConstants(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/TelegramWebhookException.php: -------------------------------------------------------------------------------- 1 | getConstants(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Enums/ChatAdminPermissions.php: -------------------------------------------------------------------------------- 1 | getConstants(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Concerns/HasStorage.php: -------------------------------------------------------------------------------- 1 | make($config['driver'], ['itemClass' => static::class, 'itemKey' => $this->storageKey(), 'configuration' => $config]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Exceptions/BotCommandException.php: -------------------------------------------------------------------------------- 1 | $botModel */ 17 | $botModel = config('telegraph.models.bot'); 18 | 19 | /** @var TelegraphBot $bot */ 20 | $bot = $botModel::fromToken($token); 21 | 22 | /** @var class-string $handler */ 23 | $handler = config('telegraph.webhook_handler', config('telegraph.webhook.handler')); 24 | 25 | /** @var WebhookHandler $handler */ 26 | $handler = app($handler); 27 | 28 | $handler->handle($request, $bot); 29 | 30 | return \response()->noContent(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/lang/uz/errors.php: -------------------------------------------------------------------------------- 1 | 'Ruxsat etilmagan amal', 14 | 'invalid_command' => 'Noma’lum buyruq', 15 | 'empty_token' => 'Token bo’sh bo’lishi mumkin emas', 16 | 'empty_chat_id' => 'Chat ID bo’sh bo’lishi mumkin emas', 17 | 'missing_bot_id' => 'Iltimos, Bot ID ni belgilang', 18 | 'failed_to_get_log_from_telegram' => 'Telegram serveridan jurnallardan loglarni olib bo‘lmadi', 19 | 'failed_to_register_webhook' => 'Webhookni ro’yxatdan o’tkazishdagi xatolik', 20 | 'failed_to_unregister_webhook' => 'Webhookni oʻchirishda xatolik yuz berdi', 21 | 'webhook_error_occurred' => 'Kechirasiz, xatolik yuz berdi', 22 | ]; 23 | -------------------------------------------------------------------------------- /src/ScopedPayloads/TelegraphPollPayload.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_SEND_POLL; 21 | $telegraph->data['chat_id'] = $telegraph->getChatId(); 22 | $telegraph->data['options'] = []; 23 | $telegraph->data['question'] = $question; 24 | 25 | return $telegraph; 26 | } 27 | 28 | public function allowMultipleAnswers(): static 29 | { 30 | $telegraph = clone $this; 31 | $telegraph->data['allows_multiple_answers'] = true; 32 | 33 | return $telegraph; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DTO/Attachment.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Attachment implements Arrayable 13 | { 14 | public function __construct( 15 | private string $path, 16 | private string|null $filename = null, 17 | ) { 18 | } 19 | 20 | public function path(): string 21 | { 22 | return $this->path; 23 | } 24 | 25 | public function local(): bool 26 | { 27 | return Str::of($this->path)->startsWith('/'); 28 | } 29 | 30 | public function contents(): string 31 | { 32 | return File::get($this->path); 33 | } 34 | 35 | public function filename(): string 36 | { 37 | return $this->filename ?? File::basename($this->path); 38 | } 39 | 40 | public function toArray(): array 41 | { 42 | return [ 43 | 'contents' => $this->contents(), 44 | 'filename' => $this->filename(), 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Support/Testing/Fakes/TelegraphEditMediaFake.php: -------------------------------------------------------------------------------- 1 | > $replies 21 | */ 22 | public function __construct(array $replies = []) 23 | { 24 | parent::__construct(); 25 | $this->replies = $replies; 26 | } 27 | 28 | public static function assertSentEditMedia(string $type, string $media): void 29 | { 30 | self::assertSentData(Telegraph::ENDPOINT_EDIT_MEDIA, [ 31 | "media" => json_encode([ 32 | 'type' => $type, 33 | 'media' => $media, 34 | ]), 35 | ], false); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) defstudio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Client/TelegraphResponse.php: -------------------------------------------------------------------------------- 1 | toPsrResponse()); 12 | } 13 | 14 | public function telegraphOk(): bool 15 | { 16 | return parent::successful() && $this->json('ok'); 17 | } 18 | 19 | public function telegraphError(): bool 20 | { 21 | return !$this->telegraphOk(); 22 | } 23 | 24 | public function telegraphMessageId(): int|null 25 | { 26 | if (!$this->telegraphOk()) { 27 | return null; 28 | } 29 | 30 | /* @phpstan-ignore-next-line */ 31 | return (int) $this->json('result.message_id'); 32 | } 33 | 34 | public function dump(mixed $key = null): static 35 | { 36 | dump($this->json($key)); 37 | 38 | return $this; 39 | } 40 | 41 | /** 42 | * @return never-returns 43 | */ 44 | public function dd(mixed $key = null): void 45 | { 46 | dd($this->json($key)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Support/Testing/Fakes/TelegraphSetChatMenuButtonFake.php: -------------------------------------------------------------------------------- 1 | > $replies 21 | */ 22 | public function __construct(array $replies = []) 23 | { 24 | parent::__construct(); 25 | $this->replies = $replies; 26 | } 27 | 28 | /** 29 | * @param array $options 30 | */ 31 | public static function assertChangedMenuButton(string $type, array $options = []): void 32 | { 33 | self::assertSentData(Telegraph::ENDPOINT_SET_CHAT_MENU_BUTTON, [ 34 | 'menu_button' => [ 35 | 'type' => $type, 36 | ] + $options, 37 | ]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Exceptions/TelegraphException.php: -------------------------------------------------------------------------------- 1 | data['menu_button'] = [ 17 | 'type' => 'default', 18 | ]; 19 | 20 | return $telegraph; 21 | } 22 | 23 | public function commands(): self 24 | { 25 | $telegraph = clone $this; 26 | 27 | $telegraph->data['menu_button'] = [ 28 | 'type' => 'commands', 29 | ]; 30 | 31 | return $telegraph; 32 | } 33 | 34 | public function webApp(string $text, string $url): self 35 | { 36 | $telegraph = clone $this; 37 | 38 | $telegraph->data['menu_button'] = [ 39 | 'type' => 'web_app', 40 | 'text' => $text, 41 | 'web_app' => [ 42 | 'url' => $url, 43 | ], 44 | ]; 45 | 46 | return $telegraph; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResult.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | abstract protected function data(): array; 23 | 24 | public function keyboard(Keyboard $keyboard): static 25 | { 26 | $this->keyboard = $keyboard; 27 | 28 | return $this; 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function toArray(): array 35 | { 36 | $data = array_filter($this->data(), fn ($value) => $value !== null) + [ 37 | 'id' => $this->id, 38 | 'type' => $this->type, 39 | ]; 40 | 41 | if ($this->keyboard !== null && $this->keyboard->isFilled()) { 42 | $data['reply_markup'] = [ 43 | 'inline_keyboard' => $this->keyboard->toArray(), 44 | ]; 45 | } 46 | 47 | return $data; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Concerns/CreatesScopedPayloads.php: -------------------------------------------------------------------------------- 1 | poll($question); 17 | } 18 | 19 | public function quiz(string $question): TelegraphQuizPayload 20 | { 21 | $quizPayload = TelegraphQuizPayload::makeFrom($this); 22 | 23 | return $quizPayload->quiz($question); 24 | } 25 | 26 | public function invoice(string $title): TelegraphInvoicePayload 27 | { 28 | $invoicePayload = TelegraphInvoicePayload::makeFrom($this); 29 | 30 | return $invoicePayload->invoice($title); 31 | } 32 | 33 | public function game(string $shortName): TelegraphGamePayload 34 | { 35 | $gamePayload = TelegraphGamePayload::makeFrom($this); 36 | 37 | return $gamePayload->game($shortName); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/TelegraphServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('telegraph') 19 | ->hasConfigFile() 20 | ->hasRoute('api') 21 | ->hasMigration('create_telegraph_bots_table') 22 | ->hasMigration('create_telegraph_chats_table') 23 | ->hasCommand(CreateNewBotCommand::class) 24 | ->hasCommand(CreateNewChatCommand::class) 25 | ->hasCommand(SetTelegramWebhookCommand::class) 26 | ->hasCommand(UnsetTelegramWebhookCommand::class) 27 | ->hasCommand(GetTelegramWebhookDebugInfoCommand::class) 28 | ->hasTranslations(); 29 | 30 | $this->app->bind('telegraph', fn () => new Telegraph()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Exceptions/TelegraphPollException.php: -------------------------------------------------------------------------------- 1 | > $replies 21 | */ 22 | public function __construct(array $replies = []) 23 | { 24 | parent::__construct(); 25 | $this->replies = $replies; 26 | } 27 | 28 | /** 29 | * @param array $options 30 | */ 31 | public static function assertSentQuiz(string $question, array $options = [], int $correct_index = null): void 32 | { 33 | $data = ['question' => $question, 'type' => 'quiz']; 34 | 35 | if (!empty($options)) { 36 | $data['options'] = $options; 37 | } 38 | 39 | if ($correct_index !== null) { 40 | $data['correct_option_id'] = $correct_index; 41 | } 42 | 43 | self::assertSentData(Telegraph::ENDPOINT_SEND_POLL, $data); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Support/Testing/Fakes/TelegraphPollFake.php: -------------------------------------------------------------------------------- 1 | > $replies 21 | */ 22 | public function __construct(array $replies = []) 23 | { 24 | parent::__construct(); 25 | $this->replies = $replies; 26 | } 27 | 28 | /** 29 | * @param array $options 30 | */ 31 | public static function assertSentPoll(string $question, array $options = []): void 32 | { 33 | if (empty($options)) { 34 | self::assertSentData(Telegraph::ENDPOINT_SEND_POLL, [ 35 | 'question' => $question, 36 | ], false); 37 | 38 | return; 39 | } 40 | 41 | self::assertSentData(Telegraph::ENDPOINT_SEND_POLL, [ 42 | 'question' => $question, 43 | 'options' => ['bar!', 'baz!'], 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Proxies/KeyboardButtonProxy.php: -------------------------------------------------------------------------------- 1 | rtl = $proxyed->rtl; 26 | $this->button = $button; 27 | $this->buttons = $proxyed->buttons; 28 | } 29 | 30 | /** 31 | * @param array $arguments 32 | */ 33 | public function __call(string $name, array $arguments): KeyboardButtonProxy 34 | { 35 | if (!method_exists($this->button, $name)) { 36 | throw KeyboardException::undefinedMethod($name); 37 | } 38 | 39 | $clone = $this->clone(); 40 | 41 | $clone->button->$name(...$arguments); 42 | 43 | return $clone; 44 | } 45 | 46 | protected function clone(): KeyboardButtonProxy 47 | { 48 | return new self(parent::clone(), $this->button); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /resources/lang/en/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'You are about to create a new Telegram Bot', 13 | 'enter_bot_token' => 'Please, enter the bot token', 14 | 'enter_bot_name' => 'Enter the bot name (optional)', 15 | 'ask_to_add_a_chat' => 'Do you want to add a chat to this bot?', 16 | 'ask_to_setup_webhook' => 'Do you want to setup a webhook for this bot?', 17 | 'bot_created' => 'New bot :bot_name has been created', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "You are about to create a new Telegram Chat for bot :bot_name", 22 | 'enter_chat_id' => 'Enter the chat ID - press [x] to abort', 23 | 'enter_chat_name' => 'Enter the chat name (optional)', 24 | 'chat_created' => 'New chat :chat_name has been create for bot :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Sending webhook setup request to: :api_url', 29 | 'webhook_updated' => 'Webhook updated' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Sending webhook unset request to: :api_url', 34 | 'webhook_deleted' => 'Webhook deleted' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/nl/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Nieuwe Telegram Bot maken', 13 | 'enter_bot_token' => 'Voer het bot token in', 14 | 'enter_bot_name' => 'Voer de naam van de bot in (optioneel)', 15 | 'ask_to_add_a_chat' => 'Wil je een chat toevoegen aan deze bot?', 16 | 'ask_to_setup_webhook' => 'Wil je een webhook instellen voor deze bot?', 17 | 'bot_created' => 'Nieuwe bot :bot_name is aangemaakt', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "Nieuwe Telegram Chat maken voor bot :bot_name", 22 | 'enter_chat_id' => 'Voer het chat ID in - druk [x] om te annuleren', 23 | 'enter_chat_name' => 'Voer de naam van de chat in (optioneel)', 24 | 'chat_created' => 'Nieuwe chat :chat_name is gemaakt voor :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Webhook setup verzoek versturen naar: :api_url', 29 | 'webhook_updated' => 'Webhook aangepast' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Webhook verwijder verzoek versturen naar: :api_url', 34 | 'webhook_deleted' => 'Webhook verwijderd' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/fa/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'شما در حال ایجاد یک ربات تلگرام جدید هستید', 13 | 'enter_bot_token' => 'لطفاً توکن ربات را وارد کنید', 14 | 'enter_bot_name' => 'نام ربات را وارد کنید (اختیاری)', 15 | 'ask_to_add_a_chat' => 'آیا می‌خواهید یک چت به این ربات اضافه کنید؟', 16 | 'ask_to_setup_webhook' => 'آیا می‌خواهید یک وب‌هوک برای این ربات تنظیم کنید؟', 17 | 'bot_created' => 'ربات جدید :bot_name ایجاد شد', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "شما در حال ایجاد یک چت تلگرام جدید برای ربات :bot_name هستید", 22 | 'enter_chat_id' => 'شناسه چت را وارد کنید - برای لغو [x] را فشار دهید', 23 | 'enter_chat_name' => 'نام چت را وارد کنید (اختیاری)', 24 | 'chat_created' => 'چت جدید :chat_name برای ربات :bot_name ایجاد شد', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'در حال ارسال درخواست تنظیم وب‌هوک به: :api_url', 29 | 'webhook_updated' => 'وب‌هوک به‌روزرسانی شد' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'در حال ارسال درخواست حذف وب‌هوک به: :api_url', 34 | 'webhook_deleted' => 'وب‌هوک حذف شد' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/ru/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Вы собираетесь создать нового Телеграм-бота', 13 | 'enter_bot_token' => 'Пожалуйста, введите токен бота', 14 | 'enter_bot_name' => 'Введите имя бота (не обязательно)', 15 | 'ask_to_add_a_chat' => 'Вы хотите добавить чат к этому боту?', 16 | 'ask_to_setup_webhook' => 'Вы хотите настроить вебхук для этого бота?', 17 | 'bot_created' => 'Бот :bot_name успешно создан', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => 'Вы собираетесь создать новый чат для Телеграм-бота :bot_name', 22 | 'enter_chat_id' => 'Введите идентификатор чата - нажмите [x] для отмены', 23 | 'enter_chat_name' => 'Введите название чата (не обязательно)', 24 | 'chat_created' => 'Чат :chat_name успешно создан и прикреплён к боту :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Отправка запроса для настройки вебхука: :api_url', 29 | 'webhook_updated' => 'Вебхук обновлён' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Отправка запроса для отключения вебхука: :api_url', 34 | 'webhook_deleted' => 'Вебхук удалён' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/ar/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'أنت على وشك إنشاء روبوت تيليجرام جديد', 13 | 'enter_bot_token' => 'من فضلك، قم بإدخال توكن الروبوت', 14 | 'enter_bot_name' => 'أدخل اسم الروبوت (اختياري)', 15 | 'ask_to_add_a_chat' => 'هل ترغب في إضافة محادثة لهذا الروبوت؟', 16 | 'ask_to_setup_webhook' => 'هل ترغب في إعداد خطاف (Webhook) لهذا الروبوت؟', 17 | 'bot_created' => 'تم إنشاء الروبوت الجديد :bot_name', 18 | ], 19 | 20 | 21 | 'new_chat' => [ 22 | 'starting_message' => 'أنت على وشك إنشاء محادثة تيليجرام جديدة للروبوت :bot_name', 23 | 'enter_chat_id' => 'أدخل معرف المحادثة - اضغط [x] للإلغاء', 24 | 'enter_chat_name' => 'أدخل اسم المحادثة (اختياري)', 25 | 'chat_created' => 'تم إنشاء محادثة جديدة :chat_name للروبوت :bot_name', 26 | ], 27 | 28 | 29 | 'set_webhook' => [ 30 | 'sending_setup_request' => 'جارٍ إرسال طلب إعداد الخطاف (Webhook) إلى: :api_url', 31 | 'webhook_updated' => 'تم تحديث الخطاف (Webhook)' 32 | ], 33 | 34 | 35 | 'unset_webhook' => [ 36 | 'sending_unset_request' => 'جارٍ إرسال طلب إلغاء الخطاف (Webhook) إلى: :api_url', 37 | 'webhook_deleted' => 'تم حذف الخطاف (Webhook)' 38 | ], 39 | 40 | ]; 41 | -------------------------------------------------------------------------------- /resources/lang/uz/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Siz yangi Telegram bot yaratmoqchimisiz?', 13 | 'enter_bot_token' => 'Iltimos, bot tokenini kiriting', 14 | 'enter_bot_name' => 'Bot nomini kiriting (ixtiyoriy)', 15 | 'ask_to_add_a_chat' => 'Siz botni chatga qo’shmoqchimisiz?', 16 | 'ask_to_setup_webhook' => 'Siz webhook sozlamalarini kiritmoqchimisz?', 17 | 'bot_created' => ':bot_name Bot muvaffaqiyatli yaratildi', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => 'Siz :bot_name Telegram-boti uchun yangi chat yaratmoqchimisiz', 22 | 'enter_chat_id' => 'Chat ID-ni kiriting - bekor qilish uchun [x] tugmasini bosing', 23 | 'enter_chat_name' => 'Chat nomini kiriting (ixtiyoriy)', 24 | 'chat_created' => ':chat_name chat muvaffaqiyatli yaratildi va :bot_name botga qo’shildi', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Webhookni o’rnatish uchun :api_url ga so’rov yuborilmoqda', 29 | 'webhook_updated' => 'Webhook yangilandi', 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Webhookni o’chirish uchun :api_url ga so’rov yuborilmoqda', 34 | 'webhook_deleted' => 'Webhook o’chirildi', 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /src/DTO/Location.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Location implements Arrayable 11 | { 12 | private float $latitude; 13 | private float $longitude; 14 | private ?float $accuracy = null; 15 | 16 | private function __construct() 17 | { 18 | } 19 | 20 | /** 21 | * @param array{ 22 | * longitude: float, 23 | * latitude: float, 24 | * horizontal_accuracy?: float 25 | * } $data 26 | */ 27 | public static function fromArray(array $data): Location 28 | { 29 | $location = new self(); 30 | 31 | $location->latitude = $data['latitude']; 32 | $location->longitude = $data['longitude']; 33 | $location->accuracy = $data['horizontal_accuracy'] ?? null; 34 | 35 | return $location; 36 | } 37 | 38 | public function latitude(): float 39 | { 40 | return $this->latitude; 41 | } 42 | 43 | public function longitude(): float 44 | { 45 | return $this->longitude; 46 | } 47 | 48 | public function accuracy(): ?float 49 | { 50 | return $this->accuracy; 51 | } 52 | 53 | public function toArray(): array 54 | { 55 | return array_filter([ 56 | 'latitude' => $this->latitude, 57 | 'longitude' => $this->longitude, 58 | 'accuracy' => $this->accuracy, 59 | ], fn ($value) => $value !== null); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /resources/lang/it/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Creazione di un nuovo Telegram Bot', 13 | 'enter_bot_token' => 'Inserisci il token del bot', 14 | 'enter_bot_name' => 'Inserisci il nome del bot (opzionale)', 15 | 'ask_to_add_a_chat' => 'Desideri configurare una chat per questo bot?', 16 | 'ask_to_setup_webhook' => 'Desideri configurare un webhook per questo bot?', 17 | 'bot_created' => 'Nuovo bot creato: :bot_name', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "Creazione di una nuova Telegram Chat per il bot :bot_name", 22 | 'enter_chat_id' => "Inserisci l'ID della chat - premi [x] per annullare", 23 | 'enter_chat_name' => 'Inserisci il nome della chat (opzionale)', 24 | 'chat_created' => 'Nuova chat :chat_name creata per il bot :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Invio della richiesta di setup del webook a: :api_url', 29 | 'webhook_updated' => 'Webhook aggiornato' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Invio della richiesta di cancellazione del webhook a: :api_url', 34 | 'webhook_deleted' => 'Webhook eliminato' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/pt/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Você está prestes a criar um novo bot do Telegram', 13 | 'enter_bot_token' => 'Por favor, introduza o token do bot', 14 | 'enter_bot_name' => 'Entre o nome do bot (opcional)', 15 | 'ask_to_add_a_chat' => 'Deseja adicionar um chat a este bot?', 16 | 'ask_to_setup_webhook' => 'Deseja configurar um webhook para este bot?', 17 | 'bot_created' => 'Foi criado um novo bot :bot_name', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "Você está prestes a criar um novo chat do Telegram para o :bot_name", 22 | 'enter_chat_id' => 'Insira o ID do chat - pressione [x] para cancelar', 23 | 'enter_chat_name' => 'Entre o nome do chat (opcional)', 24 | 'chat_created' => 'Novo chat :chat_name foi criado para o bot :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Enviando pedido de configuração do webhook para: :api_url', 29 | 'webhook_updated' => 'Webhook atualizado' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Enviando pedido de desativação do webhook para: :api_url', 34 | 'webhook_deleted' => 'Webhook apagado' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/uk/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Ви збираєтесь створити нового Телеграм-бота', 13 | 'enter_bot_token' => 'Будь ласка, введіть токен бота', 14 | 'enter_bot_name' => 'Введіть ім\'я бота (не обов\'язково)', 15 | 'ask_to_add_a_chat' => 'Ви бажаєте додати чат до цього бота?', 16 | 'ask_to_setup_webhook' => 'Ви бажаєте налаштувати вебхук для цього бота?', 17 | 'bot_created' => 'Бот :bot_name успішно створений', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => 'Ви збираєтесь створити новий чат для Телеграм-бота :bot_name', 22 | 'enter_chat_id' => 'Введіть ідентифікатор чату - натисніть [x] для скасування', 23 | 'enter_chat_name' => 'Введіть назву чату (не обов\'язково)', 24 | 'chat_created' => 'Чат :chat_name успішно створений и прикріплений до боту :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Відправка запиту для налаштування вебхука: :api_url', 29 | 'webhook_updated' => 'Вебхук оновлений' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Відправка запиту для відключення вебхука: :api_url', 34 | 'webhook_deleted' => 'Вебхук видалений' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /resources/lang/es/commands.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'starting_message' => 'Estás a punto de crear un nuevo Bot de Telegram', 13 | 'enter_bot_token' => 'Por favor, introduce el token del bot', 14 | 'enter_bot_name' => 'Introduce el nombre del bot (opcional)', 15 | 'ask_to_add_a_chat' => '¿Quieres agregar un chat a este bot?', 16 | 'ask_to_setup_webhook' => '¿Desea configurar un webhook para este bot?', 17 | 'bot_created' => 'Se ha creado un nuevo bot :bot_name', 18 | ], 19 | 20 | 'new_chat' => [ 21 | 'starting_message' => "Está a punto de crear un nuevo chat de Telegram para el bot :bot_name", 22 | 'enter_chat_id' => 'Ingrese el ID del chat - presione [x] para cancelar', 23 | 'enter_chat_name' => 'Introduce el nombre del chat (opcional)', 24 | 'chat_created' => 'Se ha creado un nuevo chat :chat_name para el bot :bot_name', 25 | ], 26 | 27 | 'set_webhook' => [ 28 | 'sending_setup_request' => 'Enviando solicitud de configuración de webhook a: :api_url', 29 | 'webhook_updated' => 'Webhook actualizado' 30 | ], 31 | 32 | 'unset_webhook' => [ 33 | 'sending_unset_request' => 'Enviando solicitud de anulación de webhook a: :api_url', 34 | 'webhook_deleted' => 'Webhook eliminado' 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /src/Proxies/ReplyKeyboardButtonProxy.php: -------------------------------------------------------------------------------- 1 | rtl = $proxyed->rtl; 28 | $this->resize = $proxyed->resize; 29 | $this->oneTime = $proxyed->oneTime; 30 | $this->selective = $proxyed->selective; 31 | $this->inputPlaceholder = $proxyed->inputPlaceholder; 32 | $this->button = $button; 33 | $this->buttons = $proxyed->buttons; 34 | } 35 | 36 | /** 37 | * @param array $arguments 38 | */ 39 | public function __call(string $name, array $arguments): ReplyKeyboardButtonProxy 40 | { 41 | if (!method_exists($this->button, $name)) { 42 | throw KeyboardException::undefinedMethod($name); 43 | } 44 | 45 | $clone = $this->clone(); 46 | 47 | $clone->button->$name(...$arguments); 48 | 49 | return $clone; 50 | } 51 | 52 | protected function clone(): ReplyKeyboardButtonProxy 53 | { 54 | return new self(parent::clone(), $this->button); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DTO/Photo.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Photo implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private int $width; 15 | private int $height; 16 | private ?int $filesize = null; 17 | 18 | private function __construct() 19 | { 20 | } 21 | 22 | /** 23 | * @param array{ 24 | * file_id: string, 25 | * width: int, 26 | * height: int, 27 | * file_size?: int 28 | * } $data 29 | */ 30 | public static function fromArray(array $data): Photo 31 | { 32 | $photo = new self(); 33 | 34 | $photo->id = $data['file_id']; 35 | $photo->width = $data['width']; 36 | $photo->height = $data['height']; 37 | $photo->filesize = $data['file_size'] ?? null; 38 | 39 | return $photo; 40 | } 41 | 42 | public function id(): string 43 | { 44 | return $this->id; 45 | } 46 | 47 | public function width(): int 48 | { 49 | return $this->width; 50 | } 51 | 52 | public function height(): int 53 | { 54 | return $this->height; 55 | } 56 | 57 | public function filesize(): ?int 58 | { 59 | return $this->filesize; 60 | } 61 | 62 | public function toArray(): array 63 | { 64 | return array_filter([ 65 | 'id' => $this->id, 66 | 'width' => $this->width, 67 | 'height' => $this->height, 68 | 'filesize' => $this->filesize, 69 | ], fn ($value) => $value !== null); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/DTO/ReactionType.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ReactionType implements Arrayable 13 | { 14 | public const TYPE_EMOJI = 'emoji'; 15 | public const TYPE_CUSTOM_EMOJI = 'custom_emoji'; 16 | public const TYPE_PAID_EMOJI = 'paid'; 17 | 18 | private string $type; 19 | private ?string $emoji = null; 20 | private ?string $customEmojiId = null; 21 | 22 | private function __construct() 23 | { 24 | } 25 | 26 | /** 27 | * @param array{ 28 | * type: string, 29 | * emoji?: string, 30 | * custom_emoji_id?: string 31 | * } $data 32 | */ 33 | public static function fromArray(array $data): ReactionType 34 | { 35 | $reaction = new self(); 36 | 37 | $reaction->type = $data['type']; 38 | $reaction->emoji = $data['emoji'] ?? null; 39 | $reaction->customEmojiId = $data['custom_emoji_id'] ?? null; 40 | 41 | return $reaction; 42 | } 43 | 44 | public function type(): string 45 | { 46 | return $this->type; 47 | } 48 | 49 | public function emoji(): ?string 50 | { 51 | return $this->emoji; 52 | } 53 | 54 | public function customEmojiId(): ?string 55 | { 56 | return $this->customEmojiId; 57 | } 58 | 59 | public function toArray(): array 60 | { 61 | return array_filter([ 62 | 'type' => $this->type, 63 | 'emoji' => $this->emoji, 64 | 'custom_emoji_id' => $this->customEmojiId, 65 | ], fn ($value) => $value !== null); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Concerns/SendsPolls.php: -------------------------------------------------------------------------------- 1 | data['options']) === 12) { 24 | throw TelegraphPollException::tooManyOptions(); 25 | } 26 | 27 | if (strlen($option) > 100) { 28 | throw TelegraphPollException::optionMaxLengthExceeded($option); 29 | } 30 | 31 | /** @phpstan-ignore-next-line */ 32 | $telegraph->data['options'][] = $option; 33 | 34 | return $telegraph; 35 | } 36 | 37 | public function disableAnonymous(): static 38 | { 39 | $telegraph = clone $this; 40 | $telegraph->data['is_anonymous'] = false; 41 | 42 | return $telegraph; 43 | } 44 | 45 | public function validUntil(CarbonInterface $endTime): self 46 | { 47 | if ($endTime->subSeconds(5)->isPast()) { 48 | throw TelegraphPollException::durationTooShort($endTime); 49 | } 50 | 51 | if ($endTime->subSeconds(600)->isFuture()) { 52 | throw TelegraphPollException::durationTooLong($endTime); 53 | } 54 | 55 | $telegraph = clone $this; 56 | $telegraph->data['close_date'] = $endTime->timestamp; 57 | 58 | return $telegraph; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/DTO/Voice.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Voice implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private int $duration; 15 | 16 | private ?string $mimeType = null; 17 | private ?int $filesize = null; 18 | 19 | private function __construct() 20 | { 21 | } 22 | 23 | /** 24 | * @param array{ 25 | * file_id: string, 26 | * duration: int, 27 | * mime_type?: string, 28 | * file_size?: int, 29 | * } $data 30 | */ 31 | public static function fromArray(array $data): Voice 32 | { 33 | $voice = new self(); 34 | 35 | $voice->id = $data['file_id']; 36 | $voice->duration = $data['duration']; 37 | 38 | $voice->mimeType = $data['mime_type'] ?? null; 39 | $voice->filesize = $data['file_size'] ?? null; 40 | 41 | return $voice; 42 | } 43 | 44 | public function id(): string 45 | { 46 | return $this->id; 47 | } 48 | 49 | public function duration(): int 50 | { 51 | return $this->duration; 52 | } 53 | 54 | public function mimeType(): ?string 55 | { 56 | return $this->mimeType; 57 | } 58 | 59 | public function filesize(): ?int 60 | { 61 | return $this->filesize; 62 | } 63 | 64 | public function toArray(): array 65 | { 66 | return array_filter([ 67 | 'id' => $this->id, 68 | 'duration' => $this->duration, 69 | 'mime_type' => $this->mimeType, 70 | 'filesize' => $this->filesize, 71 | ], fn ($value) => $value !== null); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/DTO/PollOption.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class PollOption implements Arrayable 12 | { 13 | private string $text; 14 | /** @var Collection|null */ 15 | private ?Collection $textEntities = null; 16 | 17 | private int $voterCount; 18 | 19 | private function __construct() 20 | { 21 | } 22 | 23 | /** 24 | * @param array{text:string, text_entities:array, voter_count:int} $data 25 | */ 26 | public static function fromArray(array $data): PollOption 27 | { 28 | $pollOption = new self(); 29 | 30 | $pollOption->text = $data['text']; 31 | 32 | if (!empty($data['text_entities'])) { 33 | /* @phpstan-ignore-next-line */ 34 | $pollOption->textEntities = collect($data['text_entities'])->map(fn (array $entity) => Entity::fromArray($entity)); 35 | } 36 | 37 | $pollOption->voterCount = $data['voter_count']; 38 | 39 | return $pollOption; 40 | } 41 | 42 | public function text(): string 43 | { 44 | return $this->text; 45 | } 46 | 47 | /** 48 | * @return Collection|null 49 | */ 50 | public function textEntities(): ?Collection 51 | { 52 | return $this->textEntities; 53 | } 54 | 55 | public function voterCount(): int 56 | { 57 | return $this->voterCount; 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | return array_filter([ 63 | 'text' => $this->text, 64 | 'text_entities' => $this->textEntities, 65 | 'voter_count' => $this->voterCount, 66 | 67 | ], fn ($value) => $value !== null); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Commands/GetTelegramWebhookDebugInfoCommand.php: -------------------------------------------------------------------------------- 1 | argument('bot'); 19 | 20 | /** @var class-string $botModel */ 21 | $botModel = config('telegraph.models.bot'); 22 | 23 | /** @var TelegraphBot|null $bot */ 24 | $bot = rescue(fn () => $botModel::fromId($bot_id), report: false); 25 | 26 | if (empty($bot)) { 27 | $this->error(__('telegraph::errors.missing_bot_id')); 28 | 29 | return self::FAILURE; 30 | } 31 | 32 | $response = $bot->getWebhookDebugInfo()->send(); 33 | 34 | if (!$response->json('ok')) { 35 | $this->error(__('telegraph::errors.failed_to_get_log_from_telegram')); 36 | $this->error($response->body()); 37 | 38 | return self::FAILURE; 39 | } 40 | 41 | /** @var array $result */ 42 | $result = $response->json('result'); 43 | 44 | foreach ($result as $key => $value) { 45 | if (is_bool($value)) { 46 | $value = $value ? __('telegraph::misc.yes') : __('telegraph::misc.no'); 47 | } 48 | 49 | /** @phpstan-ignore-next-line */ 50 | $this->line("$key: $value"); 51 | } 52 | 53 | return self::SUCCESS; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Concerns/StoresFiles.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_GET_FILE; 22 | $telegraph->data['file_id'] = $fileId; 23 | 24 | return $telegraph; 25 | } 26 | 27 | public function store(Downloadable|string $downloadable, string $path, ?string $filename = null): string 28 | { 29 | $fileId = is_string($downloadable) ? $downloadable : $downloadable->id(); 30 | 31 | $response = $this->getFileInfo($fileId)->send(); 32 | 33 | if ($response->telegraphError()) { 34 | throw FileException::failedToRetreiveFileInfo($fileId); 35 | } 36 | 37 | $filePath = $response->json('result.file_path'); 38 | 39 | assert(is_string($filePath)); 40 | 41 | $url = $this->getFilesUrl(); 42 | 43 | $url = Str::of($url) 44 | ->append('/', $filePath); 45 | 46 | /** @var Response $response */ 47 | $response = Http::get($url); 48 | 49 | if ($response->failed()) { 50 | throw FileException::unableToDownloadFile($fileId); 51 | } 52 | 53 | $filename ??= $url->afterLast('/')->before('?'); 54 | 55 | File::ensureDirectoryExists($path); 56 | File::put($path."/".$filename, $response->body()); 57 | 58 | return $path."/".$filename; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Concerns/InteractsWithCommands.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_GET_REGISTERED_BOT_COMMANDS; 22 | 23 | return $telegraph; 24 | } 25 | 26 | /** 27 | * @param array $commands 28 | */ 29 | public function registerBotCommands(array $commands): Telegraph 30 | { 31 | $telegraph = clone $this; 32 | 33 | $telegraph->endpoint = self::ENDPOINT_REGISTER_BOT_COMMANDS; 34 | 35 | if (count($commands) > 100) { 36 | throw BotCommandException::tooManyCommands(); 37 | } 38 | 39 | $telegraph->data['commands'] = collect($commands)->map(function (string $description, string $command) { 40 | if (strlen($command) > 32) { 41 | throw BotCommandException::longCommand($command); 42 | } 43 | 44 | if (!preg_match('/[a-z0-9_]+/', $command)) { 45 | throw BotCommandException::invalidCommand($command); 46 | } 47 | 48 | return [ 49 | 'command' => $command, 50 | 'description' => $description, 51 | ]; 52 | })->values()->toArray(); 53 | 54 | return $telegraph; 55 | } 56 | 57 | public function unregisterBotCommands(): Telegraph 58 | { 59 | $telegraph = clone $this; 60 | 61 | $telegraph->endpoint = self::ENDPOINT_UNREGISTER_BOT_COMMANDS; 62 | 63 | return $telegraph; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Commands/UnsetTelegramWebhookCommand.php: -------------------------------------------------------------------------------- 1 | argument('bot'); 20 | 21 | /** @var class-string $botModel */ 22 | $botModel = config('telegraph.models.bot'); 23 | 24 | /** @var TelegraphBot|null $bot */ 25 | $bot = rescue(fn () => $botModel::fromId($bot_id), report: false); 26 | 27 | if (empty($bot)) { 28 | $this->error(__('telegraph::errors.missing_bot_id')); 29 | 30 | return self::FAILURE; 31 | } 32 | 33 | $telegraph = $bot->unregisterWebhook($this->hasOption('drop-pending-updates')); 34 | 35 | $this->info(__('telegraph::commands.unset_webhook.sending_unset_request', ['api_url' => $telegraph->getApiUrl()])); 36 | 37 | $reponse = $telegraph->send(); 38 | 39 | $ok = (bool)$reponse->json('ok'); 40 | 41 | if (!$ok) { 42 | $this->error(__('telegraph::errors.failed_to_unregister_webhook')); 43 | $this->error($reponse->body()); 44 | 45 | return self::FAILURE; 46 | } 47 | 48 | $this->info(__('telegraph::commands.unset_webhook.webhook_deleted')); 49 | 50 | return self::SUCCESS; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Jobs/SendRequestToTelegramJob.php: -------------------------------------------------------------------------------- 1 | $data 22 | * @param Collection $files 23 | */ 24 | public function __construct(public string $url, public array $data, public Collection $files, public string|null $httpProxy = null) 25 | { 26 | } 27 | 28 | public function handle(): void 29 | { 30 | $asMultipart = $this->files->isNotEmpty(); 31 | 32 | $request = $asMultipart 33 | ? Http::asMultipart() 34 | : Http::asJson(); 35 | 36 | /** @var PendingRequest $request */ 37 | $request = $this->files->reduce( 38 | function ($request, Attachment $attachment, string $key) { 39 | //@phpstan-ignore-next-line 40 | return $request->attach($key, $attachment->contents(), $attachment->filename()); 41 | }, 42 | $request 43 | ); 44 | 45 | // Apply proxy configuration if set 46 | if ($proxy = $this->httpProxy ?? config('telegraph.http_proxy')) { 47 | $request->withOptions(['proxy' => $proxy]); 48 | } 49 | 50 | /** @phpstan-ignore-next-line */ 51 | $request->timeout(config('telegraph.http_timeout', 30))->connectTimeout(config('telegraph.http_connection_timeout', 10))->post($this->url, $this->data); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Commands/CreateNewChatCommand.php: -------------------------------------------------------------------------------- 1 | argument('bot'); 20 | 21 | /** @var class-string $botModel */ 22 | $botModel = config('telegraph.models.bot'); 23 | 24 | /** @var TelegraphBot|null $bot */ 25 | $bot = rescue(fn () => $botModel::fromId($bot_id), report: false); 26 | 27 | if (empty($bot)) { 28 | $this->error(__('telegraph::errors.missing_bot_id')); 29 | 30 | return self::FAILURE; 31 | } 32 | 33 | $this->info(__('telegraph::commands.new_chat.starting_message', ['bot_name' => $bot->name])); 34 | 35 | while (empty($chat_id = $this->ask(__('telegraph::commands.new_chat.enter_chat_id')))) { 36 | $this->error(__('telegraph::errors.empty_chat_id')); 37 | } 38 | 39 | if ($chat_id != 'x') { 40 | $chat_name = $this->ask(__('telegraph::commands.new_chat.enter_chat_name')); 41 | 42 | /** @var TelegraphChat $chat */ 43 | $chat = $bot->chats()->create([ 44 | 'chat_id' => $chat_id, 45 | 'name' => $chat_name, 46 | ]); 47 | 48 | $this->info(__('telegraph::commands.new_chat.chat_created', ['chat_name' => $chat->name, 'bot_name' => $bot->name])); 49 | } 50 | 51 | 52 | return self::SUCCESS; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DTO/Contact.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Contact implements Arrayable 11 | { 12 | private string $phone_number; 13 | private string $first_name; 14 | private ?string $last_name; 15 | private ?int $user_id = null; 16 | private ?string $vcard = null; 17 | 18 | private function __construct() 19 | { 20 | } 21 | 22 | /** 23 | * @param array{phone_number: string, first_name?: string, last_name?: string, user_id?: int, vcard?: string} $data 24 | */ 25 | public static function fromArray(array $data): Contact 26 | { 27 | $contact = new self(); 28 | 29 | $contact->phone_number = $data['phone_number']; 30 | $contact->first_name = $data['first_name'] ?? ''; 31 | $contact->last_name = $data['last_name'] ?? null; 32 | $contact->user_id = $data['user_id'] ?? null; 33 | $contact->vcard = $data['vcard'] ?? null; 34 | 35 | return $contact; 36 | } 37 | 38 | public function phoneNumber(): string 39 | { 40 | return $this->phone_number; 41 | } 42 | 43 | public function firstName(): string 44 | { 45 | return $this->first_name; 46 | } 47 | 48 | public function lastName(): ?string 49 | { 50 | return $this->last_name; 51 | } 52 | 53 | public function userId(): ?int 54 | { 55 | return $this->user_id; 56 | } 57 | 58 | public function vcard(): ?string 59 | { 60 | return $this->vcard; 61 | } 62 | 63 | public function toArray(): array 64 | { 65 | return array_filter([ 66 | 'phone_number' => $this->phone_number, 67 | 'first_name' => $this->first_name, 68 | 'last_name' => $this->last_name, 69 | 'user_id' => $this->user_id, 70 | 'vcard' => $this->vcard, 71 | ], fn ($value) => $value !== null); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/ScopedPayloads/TelegraphQuizPayload.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_SEND_POLL; 24 | $telegraph->data['chat_id'] = $telegraph->getChatId(); 25 | $telegraph->data['type'] = 'quiz'; 26 | $telegraph->data['options'] = []; 27 | $telegraph->data['question'] = $question; 28 | 29 | return $telegraph; 30 | } 31 | 32 | public function option(string $option, bool $correct = false): static 33 | { 34 | $telegraph = self::_createOption($option); 35 | 36 | if ($correct) { 37 | if (isset($telegraph->data['correct_option_id'])) { 38 | /** @phpstan-ignore-next-line */ 39 | throw TelegraphPollException::onlyOneCorrectAnswerAllowed($telegraph->data['options'][$telegraph->data['correct_option_id']]); 40 | } 41 | 42 | /** @phpstan-ignore-next-line */ 43 | $telegraph->data['correct_option_id'] = count($telegraph->data['options']) - 1; 44 | } 45 | 46 | return $telegraph; 47 | } 48 | 49 | public function explanation(string $text): static 50 | { 51 | if (strlen($text) > 200) { 52 | throw TelegraphPollException::explanationMaxLengthExceeded(); 53 | } 54 | 55 | $telegraph = clone $this; 56 | $telegraph->data['explanation'] = $text; 57 | 58 | return $telegraph; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/DTO/WriteAccessAllowed.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class WriteAccessAllowed implements Arrayable 11 | { 12 | private bool $fromRequest = false; 13 | private ?string $webAppName = null; 14 | private bool $fromAttachmentMenu = false; 15 | 16 | private function __construct() 17 | { 18 | } 19 | 20 | /** 21 | * @param array{ 22 | * from_request?: bool, 23 | * web_app_name?: string, 24 | * from_attachment_menu?: bool, 25 | * } $data 26 | */ 27 | public static function fromArray(array $data): WriteAccessAllowed 28 | { 29 | $writeAccessAllowed = new self(); 30 | 31 | $writeAccessAllowed->fromRequest = $data['from_request'] ?? false; 32 | $writeAccessAllowed->webAppName = $data['web_app_name'] ?? null; 33 | $writeAccessAllowed->fromAttachmentMenu = $data ['from_attachment_menu'] ?? false; 34 | 35 | return $writeAccessAllowed; 36 | } 37 | 38 | public function fromRequest(): bool 39 | { 40 | return $this->fromRequest; 41 | } 42 | 43 | public function fromWebApp(): bool 44 | { 45 | return $this->webAppName !== null; 46 | } 47 | 48 | public function webAppName(): ?string 49 | { 50 | return $this->webAppName; 51 | } 52 | 53 | public function fromAttachmentMenu(): bool 54 | { 55 | return $this->fromAttachmentMenu; 56 | } 57 | 58 | public function isAllowed(): bool 59 | { 60 | return $this->fromRequest() || $this->fromWebApp() || $this->fromAttachmentMenu(); 61 | } 62 | 63 | public function toArray(): array 64 | { 65 | return array_filter([ 66 | 'from_request' => $this->fromRequest, 67 | 'web_app_name' => $this->webAppName, 68 | 'from_attachment_menu' => $this->fromAttachmentMenu, 69 | ], fn ($value) => $value !== null); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/DTO/OrderInfo.php: -------------------------------------------------------------------------------- 1 | > 9 | */ 10 | class OrderInfo implements Arrayable 11 | { 12 | private ?string $name = null; 13 | private ?string $phoneNumber = null; 14 | private ?string $email = null; 15 | private ?ShippingAddress $shippingAddress = null; 16 | 17 | private function __construct() 18 | { 19 | } 20 | 21 | /** 22 | * @param array{ 23 | * name?: string, 24 | * phone_number?: string, 25 | * email?: string, 26 | * shipping_address?: array, 27 | * } $data 28 | */ 29 | public static function fromArray(array $data): OrderInfo 30 | { 31 | $orderInfo = new self(); 32 | 33 | $orderInfo->name = $data['name'] ?? null; 34 | $orderInfo->phoneNumber = $data['phone_number'] ?? null; 35 | $orderInfo->email = $data['email'] ?? null; 36 | 37 | if (isset($data['shipping_address'])) { 38 | $orderInfo->shippingAddress = ShippingAddress::fromArray($data['shipping_address']); 39 | } 40 | 41 | return $orderInfo; 42 | } 43 | 44 | public function name(): ?string 45 | { 46 | return $this->name; 47 | } 48 | 49 | public function phoneNumber(): ?string 50 | { 51 | return $this->phoneNumber; 52 | } 53 | 54 | public function email(): ?string 55 | { 56 | return $this->email; 57 | } 58 | 59 | public function shippingAddress(): ?ShippingAddress 60 | { 61 | return $this->shippingAddress; 62 | } 63 | 64 | public function toArray(): array 65 | { 66 | return array_filter([ 67 | 'name' => $this->name, 68 | 'phone_number' => $this->phoneNumber, 69 | 'email' => $this->email, 70 | 'shipping_address' => $this->shippingAddress?->toArray(), 71 | ], fn ($value) => $value !== null); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Concerns/AnswersInlineQueries.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_ANSWER_INLINE_QUERY; 24 | $telegraph->data = [ 25 | 'inline_query_id' => $inlineQueryID, 26 | 'results' => collect($results)->map(fn (InlineQueryResult $result) => $result->toArray())->toArray(), 27 | ]; 28 | 29 | return $telegraph; 30 | } 31 | 32 | public function cache(int $seconds): Telegraph 33 | { 34 | $telegraph = clone $this; 35 | $telegraph->data['cache_time'] = $seconds; 36 | 37 | return $telegraph; 38 | } 39 | 40 | public function personal(): Telegraph 41 | { 42 | $telegraph = clone $this; 43 | $telegraph->data['is_personal'] = true; 44 | 45 | return $telegraph; 46 | } 47 | 48 | public function nextOffset(string $offset): Telegraph 49 | { 50 | $telegraph = clone $this; 51 | $telegraph->data['next_offset'] = $offset; 52 | 53 | return $telegraph; 54 | } 55 | 56 | public function offertToSwitchToPrivateMessage(string $text, string $parameter): Telegraph 57 | { 58 | if (!preg_match("#^[a-zA-Z\d_-]+$#", $parameter)) { 59 | throw InlineQueryException::invalidSwitchToPmParameter($parameter); 60 | } 61 | 62 | $telegraph = clone $this; 63 | $telegraph->data['switch_pm_text'] = $text; 64 | $telegraph->data['switch_pm_parameter'] = $parameter; 65 | 66 | return $telegraph; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultVoice.php: -------------------------------------------------------------------------------- 1 | id = $id; 23 | $result->url = $url; 24 | $result->title = $title; 25 | 26 | return $result; 27 | } 28 | 29 | public function caption(string|null $caption): static 30 | { 31 | $this->caption = $caption; 32 | 33 | return $this; 34 | } 35 | 36 | public function duration(int|null $duration): static 37 | { 38 | $this->duration = $duration; 39 | 40 | return $this; 41 | } 42 | 43 | public function html(): static 44 | { 45 | $this->parseMode = Telegraph::PARSE_HTML; 46 | 47 | return $this; 48 | } 49 | 50 | public function markdown(): static 51 | { 52 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 53 | 54 | return $this; 55 | } 56 | 57 | public function markdownV2(): static 58 | { 59 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * @inheritDoc 66 | */ 67 | public function data(): array 68 | { 69 | return array_filter([ 70 | 'voice_url' => $this->url, 71 | 'title' => $this->title, 72 | 'caption' => $this->caption, 73 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 74 | 'voice_duration' => $this->duration, 75 | ], fn ($value) => $value !== null); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/DTO/PollAnswer.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class PollAnswer implements Arrayable 11 | { 12 | private string $pollId; 13 | private ?Chat $voterChat = null; 14 | private ?User $user = null; 15 | 16 | /** @var array */ 17 | private array $optionIds; 18 | 19 | private function __construct() 20 | { 21 | } 22 | 23 | /** 24 | * @param array{poll_id:string, voter_chat?:array, user?:array, option_ids:array} $data 25 | */ 26 | public static function fromArray(array $data): PollAnswer 27 | { 28 | $pollAnswer = new self(); 29 | 30 | $pollAnswer->pollId = $data['poll_id']; 31 | 32 | if (isset($data['user'])) { 33 | /* @phpstan-ignore-next-line */ 34 | $pollAnswer->user = User::fromArray($data['user']); 35 | } 36 | 37 | if (isset($data['voter_chat'])) { 38 | /* @phpstan-ignore-next-line */ 39 | $pollAnswer->voterChat = Chat::fromArray($data['voter_chat']); 40 | } 41 | 42 | $pollAnswer->optionIds = $data['option_ids']; 43 | 44 | 45 | return $pollAnswer; 46 | } 47 | 48 | public function pollId(): string 49 | { 50 | return $this->pollId; 51 | } 52 | 53 | public function user(): ?User 54 | { 55 | return $this->user; 56 | } 57 | 58 | public function voterChat(): ?Chat 59 | { 60 | return $this->voterChat; 61 | } 62 | 63 | /** 64 | * @return int[] 65 | */ 66 | public function optionIds(): array 67 | { 68 | return $this->optionIds; 69 | } 70 | 71 | public function toArray(): array 72 | { 73 | return array_filter([ 74 | 'poll_id' => $this->pollId, 75 | 'user' => $this->user?->toArray(), 76 | 'voter_chat' => $this->voterChat?->toArray(), 77 | 'option_ids' => $this->optionIds, 78 | 79 | ], fn ($value) => $value !== null); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/DTO/Invoice.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Invoice implements Arrayable 11 | { 12 | public string $title; 13 | 14 | public string $description; 15 | 16 | public string $startParameter; 17 | 18 | public string $currency; 19 | 20 | public int $totalAmount; 21 | 22 | public function __construct() 23 | { 24 | } 25 | 26 | /** 27 | * @param array{ 28 | * title: string, 29 | * description: string, 30 | * start_parameter: string, 31 | * currency: string, 32 | * total_amount: int 33 | * } $data 34 | */ 35 | public static function fromArray(array $data): Invoice 36 | { 37 | $invoice = new self(); 38 | 39 | $invoice->title = $data['title']; 40 | $invoice->description = $data['description']; 41 | $invoice->startParameter = $data['start_parameter']; 42 | $invoice->currency = $data['currency']; 43 | $invoice->totalAmount = $data['total_amount']; 44 | 45 | return $invoice; 46 | } 47 | 48 | public function title(): string 49 | { 50 | return $this->title; 51 | } 52 | 53 | public function description(): string 54 | { 55 | return $this->description; 56 | } 57 | 58 | public function startParameter(): string 59 | { 60 | return $this->startParameter; 61 | } 62 | 63 | public function currency(): string 64 | { 65 | return $this->currency; 66 | } 67 | 68 | public function totalAmount(): int 69 | { 70 | return $this->totalAmount; 71 | } 72 | 73 | public function toArray(): array 74 | { 75 | return array_filter([ 76 | 'title' => $this->title, 77 | 'description' => $this->description, 78 | 'start_parameter' => $this->startParameter, 79 | 'currency' => $this->currency, 80 | 'total_amount' => $this->totalAmount, 81 | ], fn ($value) => $value !== null); //@phpstan-ignore-line 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/DTO/Document.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Document implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | 15 | private ?string $filename = null; 16 | private ?string $mimeType = null; 17 | private ?int $filesize = null; 18 | 19 | private ?Photo $thumbnail = null; 20 | 21 | private function __construct() 22 | { 23 | } 24 | 25 | /** 26 | * @param array{ 27 | * file_id: string, 28 | * file_name?: string, 29 | * mime_type?: string, 30 | * file_size?: int, 31 | * thumb?: array, 32 | * } $data 33 | */ 34 | public static function fromArray(array $data): Document 35 | { 36 | $document = new self(); 37 | 38 | $document->id = $data['file_id']; 39 | $document->filename = $data['file_name'] ?? null; 40 | $document->mimeType = $data['mime_type'] ?? null; 41 | $document->filesize = $data['file_size'] ?? null; 42 | 43 | if (isset($data['thumb'])) { 44 | $document->thumbnail = Photo::fromArray($data['thumb']); 45 | } 46 | 47 | return $document; 48 | } 49 | 50 | public function id(): string 51 | { 52 | return $this->id; 53 | } 54 | 55 | public function filename(): ?string 56 | { 57 | return $this->filename; 58 | } 59 | 60 | public function mimeType(): ?string 61 | { 62 | return $this->mimeType; 63 | } 64 | 65 | public function filesize(): ?int 66 | { 67 | return $this->filesize; 68 | } 69 | 70 | public function toArray(): array 71 | { 72 | return array_filter([ 73 | 'id' => $this->id, 74 | 'filename' => $this->filename, 75 | 'mime_type' => $this->mimeType, 76 | 'filesize' => $this->filesize, 77 | 'thumbnail' => $this->thumbnail?->toArray(), 78 | ], fn ($value) => $value !== null); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/DTO/VideoNote.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class VideoNote implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private int $duration; 15 | private int $length; 16 | private ?int $filesize = null; 17 | 18 | private ?Photo $thumbnail = null; 19 | 20 | private function __construct() 21 | { 22 | } 23 | 24 | /** 25 | * @param array{ 26 | * file_id: string, 27 | * length: int, 28 | * duration: int, 29 | * file_size?: int, 30 | * thumb?: array, 31 | * } $data 32 | */ 33 | public static function fromArray(array $data): VideoNote 34 | { 35 | $video = new self(); 36 | 37 | $video->id = $data['file_id']; 38 | $video->length = $data['length']; 39 | $video->duration = $data['duration']; 40 | $video->filesize = $data['file_size'] ?? null; 41 | 42 | if (isset($data['thumb'])) { 43 | $video->thumbnail = Photo::fromArray($data['thumb']); 44 | } 45 | 46 | return $video; 47 | } 48 | 49 | public function id(): string 50 | { 51 | return $this->id; 52 | } 53 | 54 | public function length(): int 55 | { 56 | return $this->length; 57 | } 58 | 59 | public function duration(): int 60 | { 61 | return $this->duration; 62 | } 63 | 64 | public function filesize(): ?int 65 | { 66 | return $this->filesize; 67 | } 68 | 69 | public function thumbnail(): ?Photo 70 | { 71 | return $this->thumbnail; 72 | } 73 | 74 | public function toArray(): array 75 | { 76 | return array_filter([ 77 | 'id' => $this->id, 78 | 'length' => $this->length, 79 | 'duration' => $this->duration, 80 | 'filesize' => $this->filesize, 81 | 'thumbnail' => $this->thumbnail?->toArray(), 82 | ], fn ($value) => $value !== null); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Storage/StorageDriver.php: -------------------------------------------------------------------------------- 1 | storeData($key, $this->dehydrate($value)); 16 | 17 | return $this; 18 | } 19 | 20 | final public function get(string $key, mixed $default = null): mixed 21 | { 22 | return $this->hydrate($this->retrieveData($key, $default)); 23 | } 24 | 25 | abstract protected function storeData(string $key, mixed $value): void; 26 | 27 | abstract protected function retrieveData(string $key, mixed $default): mixed; 28 | 29 | private function dehydrate(mixed $value): mixed 30 | { 31 | if (is_iterable($value)) { 32 | /** @var iterable $value */ 33 | return Collection::make($value) 34 | ->map(fn ($item) => $this->dehydrate($item)) 35 | ->toArray(); 36 | } 37 | 38 | if ($value instanceof Model) { 39 | return [ 40 | self::MODEL_CLASS_KEY => $value::class, 41 | self::MODEL_ID_KEY => $value->getKey(), 42 | ]; 43 | } 44 | 45 | return $value; 46 | } 47 | 48 | private function hydrate(mixed $value): mixed 49 | { 50 | if (is_iterable($value)) { 51 | /** @var iterable $value */ 52 | $collection = Collection::make($value); 53 | if ($collection->has(self::MODEL_CLASS_KEY)) { 54 | /** @var class-string $modelClass */ 55 | $modelClass = $collection->get(self::MODEL_CLASS_KEY); 56 | 57 | return $modelClass::find($collection->get(self::MODEL_ID_KEY)); 58 | } 59 | 60 | return array_map(fn ($item) => $this->hydrate($item), $collection->toArray()); 61 | } 62 | 63 | return $value; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Storage/FileStorageDriver.php: -------------------------------------------------------------------------------- 1 | file = (string) Str::of($configuration['root']) 24 | ->append('/', class_basename($itemClass)) 25 | ->append("/", $itemKey, '.json'); 26 | 27 | $this->disk = Storage::disk($configuration['disk'] ?? null); 28 | } 29 | 30 | /** 31 | * @return array 32 | */ 33 | private function getDataFromJson(): array 34 | { 35 | try { 36 | $json = $this->disk->get($this->file); 37 | 38 | /** @phpstan-ignore-next-line */ 39 | return rescue(fn () => json_decode($json, true, flags: JSON_THROW_ON_ERROR), []); 40 | } catch (JsonException|FileNotFoundException) { 41 | return []; 42 | } 43 | } 44 | 45 | /** 46 | * @param array $data 47 | */ 48 | private function storeDataInJson(array $data): void 49 | { 50 | $json = json_encode($data); 51 | 52 | /** @phpstan-ignore-next-line */ 53 | $this->disk->put($this->file, $json); 54 | } 55 | 56 | public function storeData(string $key, mixed $value): void 57 | { 58 | $data = $this->getDataFromJson(); 59 | Arr::set($data, $key, $value); 60 | $this->storeDataInJson($data); 61 | } 62 | 63 | public function retrieveData(string $key, mixed $default = null): mixed 64 | { 65 | $data = $this->getDataFromJson(); 66 | 67 | return Arr::get($data, $key, $default); 68 | } 69 | 70 | public function forget(string $key): static 71 | { 72 | $data = $this->getDataFromJson(); 73 | Arr::forget($data, $key); 74 | $this->storeDataInJson($data); 75 | 76 | return $this; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Commands/SetTelegramWebhookCommand.php: -------------------------------------------------------------------------------- 1 | argument('bot'); 23 | 24 | /** @var class-string $botModel */ 25 | $botModel = config('telegraph.models.bot'); 26 | 27 | /** @var TelegraphBot|null $bot */ 28 | $bot = rescue(fn () => $botModel::fromId($bot_id), report: false); 29 | 30 | if (empty($bot)) { 31 | $this->error(__('telegraph::errors.missing_bot_id')); 32 | 33 | return self::FAILURE; 34 | } 35 | 36 | $dropPendingUpdates = $this->option('drop-pending-updates'); 37 | $maxConnections = $this->option('max-connections'); 38 | $secret = $this->option('secret'); 39 | 40 | /* @phpstan-ignore-next-line */ 41 | $telegraph = $bot->registerWebhook($dropPendingUpdates, $maxConnections, $secret); 42 | 43 | $this->info(__('telegraph::commands.set_webhook.sending_setup_request', ['api_url' => $telegraph->getApiUrl()])); 44 | 45 | $reponse = $telegraph->send(); 46 | 47 | $ok = (bool) $reponse->json('ok'); 48 | 49 | if (!$ok) { 50 | $this->error(__('telegraph::errors.failed_to_register_webhook')); 51 | $this->error($reponse->body()); 52 | 53 | return self::FAILURE; 54 | } 55 | 56 | $this->info(__('telegraph::commands.set_webhook.webhook_updated')); 57 | 58 | return self::SUCCESS; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultAudio.php: -------------------------------------------------------------------------------- 1 | id = $id; 24 | $result->url = $url; 25 | $result->title = $title; 26 | 27 | return $result; 28 | } 29 | 30 | public function caption(string|null $caption): static 31 | { 32 | $this->caption = $caption; 33 | 34 | return $this; 35 | } 36 | 37 | public function performer(string|null $performer): static 38 | { 39 | $this->performer = $performer; 40 | 41 | return $this; 42 | } 43 | 44 | public function duration(int|null $duration): static 45 | { 46 | $this->duration = $duration; 47 | 48 | return $this; 49 | } 50 | 51 | public function html(): static 52 | { 53 | $this->parseMode = Telegraph::PARSE_HTML; 54 | 55 | return $this; 56 | } 57 | 58 | public function markdown(): static 59 | { 60 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 61 | 62 | return $this; 63 | } 64 | 65 | public function markdownV2(): static 66 | { 67 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * @inheritDoc 74 | */ 75 | public function data(): array 76 | { 77 | return array_filter([ 78 | 'audio_url' => $this->url, 79 | 'title' => $this->title, 80 | 'caption' => $this->caption, 81 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 82 | 'performer' => $this->performer, 83 | 'audio_duration' => $this->duration, 84 | ], fn ($value) => $value !== null); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/DTO/InlineQuery.php: -------------------------------------------------------------------------------- 1 | > 9 | */ 10 | class InlineQuery implements Arrayable 11 | { 12 | private string $id; 13 | private string $query; 14 | private User $from; 15 | private string $offset; 16 | private string $chatType; 17 | private ?Location $location = null; 18 | 19 | private function __construct() 20 | { 21 | } 22 | 23 | /** 24 | * @param array{ 25 | * id: string, 26 | * from: array, 27 | * query: string|null, 28 | * offset: string|null, 29 | * chat_type: string, 30 | * location?: array 31 | * } $data 32 | */ 33 | public static function fromArray(array $data): InlineQuery 34 | { 35 | $inlineQuery = new self(); 36 | 37 | $inlineQuery->id = $data['id']; 38 | 39 | $inlineQuery->from = User::fromArray($data['from']); 40 | 41 | $inlineQuery->query = $data['query'] ?? ''; 42 | $inlineQuery->offset = $data['offset'] ?? ''; 43 | $inlineQuery->chatType = $data['chat_type']; 44 | 45 | if (isset($data['location'])) { 46 | $inlineQuery->location = Location::fromArray($data['location']); 47 | } 48 | 49 | return $inlineQuery; 50 | } 51 | 52 | public function id(): string 53 | { 54 | return $this->id; 55 | } 56 | 57 | public function query(): string 58 | { 59 | return $this->query; 60 | } 61 | 62 | public function from(): User 63 | { 64 | return $this->from; 65 | } 66 | 67 | public function offset(): string 68 | { 69 | return $this->offset; 70 | } 71 | 72 | public function chatType(): string 73 | { 74 | return $this->chatType; 75 | } 76 | 77 | public function location(): ?Location 78 | { 79 | return $this->location; 80 | } 81 | 82 | public function toArray(): array 83 | { 84 | return array_filter([ 85 | 'id' => $this->id, 86 | 'query' => $this->query, 87 | 'from' => $this->from, 88 | 'offset' => $this->offset, 89 | 'chat_type' => $this->chatType, 90 | 'location' => $this->location, 91 | ], fn ($value) => $value !== null); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/DTO/CallbackQuery.php: -------------------------------------------------------------------------------- 1 | > 13 | */ 14 | class CallbackQuery implements Arrayable 15 | { 16 | private int $id; 17 | 18 | private User $from; 19 | 20 | private Message|null $message = null; 21 | 22 | /** 23 | * @var Collection 24 | */ 25 | private Collection $data; 26 | 27 | private function __construct() 28 | { 29 | } 30 | 31 | /** 32 | * @param array{id:int, from:array, message?:array, data?:string} $data 33 | */ 34 | public static function fromArray(array $data): CallbackQuery 35 | { 36 | $callbackQuery = new self(); 37 | 38 | $callbackQuery->id = $data['id']; 39 | 40 | $callbackQuery->from = User::fromArray($data['from']); 41 | 42 | if (isset($data['message'])) { 43 | $callbackQuery->message = Message::fromArray($data['message']); 44 | } 45 | 46 | $callbackQuery->data = Str::of($data['data'] ?? '') 47 | ->explode(';') 48 | ->filter() 49 | ->mapWithKeys(function (string $entity) { 50 | $entity = explode(':', $entity); 51 | $key = $entity[0]; 52 | $value = $entity[1]; 53 | 54 | return [$key => $value]; 55 | }); 56 | 57 | return $callbackQuery; 58 | } 59 | 60 | public function id(): int 61 | { 62 | return $this->id; 63 | } 64 | 65 | public function from(): User 66 | { 67 | return $this->from; 68 | } 69 | 70 | public function message(): Message|null 71 | { 72 | return $this->message; 73 | } 74 | 75 | /** 76 | * @return Collection 77 | */ 78 | public function data(): Collection 79 | { 80 | return $this->data; 81 | } 82 | 83 | public function toArray(): array 84 | { 85 | return array_filter([ 86 | 'id' => $this->id, 87 | 'from' => $this->from->toArray(), 88 | 'message' => $this->message?->toArray(), 89 | 'data' => $this->data->toArray(), 90 | ], fn ($value) => $value !== null); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/DTO/ShippingAddress.php: -------------------------------------------------------------------------------- 1 | > 9 | */ 10 | class ShippingAddress implements Arrayable 11 | { 12 | private string $countryCode; 13 | private string $state; 14 | private string $city; 15 | private string $streetLine1; 16 | private string $streetLine2; 17 | private string $postCode; 18 | 19 | private function __construct() 20 | { 21 | } 22 | 23 | /** 24 | * @param array{ 25 | * country_code: string, 26 | * state: string, 27 | * city: string, 28 | * street_line1: string, 29 | * street_line2: string, 30 | * post_code: string, 31 | * } $data 32 | */ 33 | public static function fromArray(array $data): ShippingAddress 34 | { 35 | $shippingAddress = new self(); 36 | 37 | $shippingAddress->countryCode = $data['country_code']; 38 | $shippingAddress->state = $data['state']; 39 | $shippingAddress->city = $data['city']; 40 | $shippingAddress->streetLine1 = $data['street_line1']; 41 | $shippingAddress->streetLine2 = $data['street_line2']; 42 | $shippingAddress->postCode = $data['post_code']; 43 | 44 | return $shippingAddress; 45 | } 46 | 47 | public function countryCode(): string 48 | { 49 | return $this->countryCode; 50 | } 51 | 52 | public function state(): string 53 | { 54 | return $this->state; 55 | } 56 | 57 | public function city(): string 58 | { 59 | return $this->city; 60 | } 61 | 62 | public function streetLine1(): string 63 | { 64 | return $this->streetLine1; 65 | } 66 | 67 | public function streetLine2(): string 68 | { 69 | return $this->streetLine1; 70 | } 71 | 72 | public function postCode(): string 73 | { 74 | return $this->postCode; 75 | } 76 | 77 | public function toArray(): array 78 | { 79 | return array_filter([ 80 | 'country_code' => $this->countryCode, 81 | 'state' => $this->state, 82 | 'city' => $this->city, 83 | 'street_line1' => $this->streetLine1, 84 | 'street_line2' => $this->streetLine2, 85 | 'post_code' => $this->postCode, 86 | ], fn ($value) => $value !== null); //@phpstan-ignore-line 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Storage/CacheStorageDriver.php: -------------------------------------------------------------------------------- 1 | key = (string) Str::of($configuration['key_prefix']) 23 | ->append("_", class_basename($itemClass)) 24 | ->append("_", $itemKey); 25 | 26 | $this->cache = Cache::store($configuration['store'] ?? null); 27 | } 28 | 29 | public function storeData(string $key, mixed $value): void 30 | { 31 | if (!Str::of($key)->contains('.')) { 32 | $this->cache->set("{$this->key}_$key", $value); 33 | 34 | return; 35 | } 36 | 37 | $mainKey = (string)Str::of($key)->before('.'); 38 | $mainValue = $this->retrieveData($mainKey, []); 39 | 40 | $otherKeys = (string)Str::of($key)->after('.'); 41 | data_set($mainValue, $otherKeys, $value); 42 | 43 | $this->cache->set("{$this->key}_".$mainKey, $mainValue); 44 | } 45 | 46 | public function retrieveData(string $key, mixed $default = null): mixed 47 | { 48 | if (!Str::of($key)->contains('.')) { 49 | return $this->cache->get("{$this->key}_$key", $default); 50 | } 51 | 52 | $mainKey = (string) Str::of($key)->before('.'); 53 | $mainValue = $this->retrieveData($mainKey, []); 54 | 55 | $otherKeys = (string) Str::of($key)->after('.'); 56 | 57 | return data_get($mainValue, $otherKeys, $default); 58 | } 59 | 60 | public function forget(string $key): static 61 | { 62 | if (!Str::of($key)->contains('.')) { 63 | $this->cache->forget("{$this->key}_$key"); 64 | } 65 | 66 | $mainKey = (string) Str::of($key)->before('.'); 67 | 68 | /** @var mixed[] $mainValue */ 69 | $mainValue = $this->retrieveData($mainKey, []); 70 | 71 | $otherKeys = (string) Str::of($key)->after('.'); 72 | 73 | Arr::forget($mainValue, $otherKeys); 74 | 75 | $this->storeData($mainKey, $mainValue); 76 | 77 | return $this; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/DTO/RefundedPayment.php: -------------------------------------------------------------------------------- 1 | > 11 | */ 12 | class RefundedPayment implements Arrayable 13 | { 14 | private string $currency; 15 | private int $totalAmount; 16 | private string $invoicePayload; 17 | private string $telegramPaymentChargeId; 18 | private ?string $providerPaymentChargeId; 19 | 20 | private function __construct() 21 | { 22 | } 23 | 24 | /** 25 | * @param array{ 26 | * currency:string, 27 | * total_amount:int, 28 | * invoice_payload:string, 29 | * telegram_payment_charge_id:string, 30 | * provider_payment_charge_id?:string, 31 | * } $data 32 | */ 33 | public static function fromArray(array $data): RefundedPayment 34 | { 35 | 36 | $refundedPayment = new self(); 37 | 38 | $refundedPayment->currency = $data['currency']; 39 | $refundedPayment->totalAmount = $data['total_amount']; 40 | $refundedPayment->invoicePayload = $data['invoice_payload']; 41 | $refundedPayment->telegramPaymentChargeId = $data['telegram_payment_charge_id']; 42 | $refundedPayment->providerPaymentChargeId = $data['provider_payment_charge_id'] ?? null; 43 | 44 | return $refundedPayment; 45 | } 46 | 47 | public function currency(): string 48 | { 49 | return $this->currency; 50 | } 51 | 52 | public function totalAmount(): int 53 | { 54 | return $this->totalAmount; 55 | } 56 | 57 | public function invoicePayload(): string 58 | { 59 | return $this->invoicePayload; 60 | } 61 | 62 | public function telegramPaymentChargeId(): string 63 | { 64 | return $this->telegramPaymentChargeId; 65 | } 66 | 67 | public function providerPaymentChargeId(): ?string 68 | { 69 | return $this->providerPaymentChargeId; 70 | } 71 | 72 | public function toArray(): array 73 | { 74 | return array_filter([ 75 | 'currency' => $this->currency, 76 | 'total_amount' => $this->totalAmount, 77 | 'invoice_payload' => $this->invoicePayload, 78 | 'telegram_payment_charge_id' => $this->telegramPaymentChargeId, 79 | 'provider_payment_charge_id' => $this->providerPaymentChargeId, 80 | ], fn ($value) => $value !== null); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/DTO/Audio.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Audio implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private int $duration; 15 | 16 | private ?string $title = null; 17 | private ?string $filename = null; 18 | private ?string $mimeType = null; 19 | private ?int $filesize = null; 20 | 21 | private ?Photo $thumbnail = null; 22 | 23 | private function __construct() 24 | { 25 | } 26 | 27 | /** 28 | * @param array{ 29 | * file_id: string, 30 | * duration: int, 31 | * title?: string, 32 | * file_name?: string, 33 | * mime_type?: string, 34 | * file_size?: int, 35 | * thumb?: array, 36 | * } $data 37 | */ 38 | public static function fromArray(array $data): Audio 39 | { 40 | $audio = new self(); 41 | 42 | $audio->id = $data['file_id']; 43 | $audio->duration = $data['duration']; 44 | 45 | $audio->title = $data['title'] ?? null; 46 | $audio->filename = $data['file_name'] ?? null; 47 | $audio->mimeType = $data['mime_type'] ?? null; 48 | $audio->filesize = $data['file_size'] ?? null; 49 | 50 | if (isset($data['thumb'])) { 51 | $audio->thumbnail = Photo::fromArray($data['thumb']); 52 | } 53 | 54 | return $audio; 55 | } 56 | 57 | public function id(): string 58 | { 59 | return $this->id; 60 | } 61 | 62 | public function duration(): int 63 | { 64 | return $this->duration; 65 | } 66 | 67 | public function title(): ?string 68 | { 69 | return $this->title; 70 | } 71 | 72 | public function filename(): ?string 73 | { 74 | return $this->filename; 75 | } 76 | 77 | public function mimeType(): ?string 78 | { 79 | return $this->mimeType; 80 | } 81 | 82 | public function filesize(): ?int 83 | { 84 | return $this->filesize; 85 | } 86 | 87 | public function toArray(): array 88 | { 89 | return array_filter([ 90 | 'id' => $this->id, 91 | 'duration' => $this->duration, 92 | 'title' => $this->title, 93 | 'filename' => $this->filename, 94 | 'mime_type' => $this->mimeType, 95 | 'filesize' => $this->filesize, 96 | 'thumbnail' => $this->thumbnail?->toArray(), 97 | ], fn ($value) => $value !== null); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Commands/CreateNewBotCommand.php: -------------------------------------------------------------------------------- 1 | info(__('telegraph::commands.new_bot.starting_message')); 22 | 23 | $token = $this->ask(__('telegraph::commands.new_bot.enter_bot_token')); 24 | if (empty($token)) { 25 | $this->error(__('telegraph::errors.empty_token')); 26 | 27 | return self::FAILURE; 28 | } 29 | 30 | $name = $this->ask(__('telegraph::commands.new_bot.enter_bot_name')); 31 | 32 | /** @var class-string $botModel */ 33 | $botModel = config('telegraph.models.bot'); 34 | 35 | /** @var TelegraphBot $bot */ 36 | $bot = $botModel::create([ 37 | 'token' => $token, 38 | 'name' => $name, 39 | ]); 40 | 41 | if ($this->confirm(__('telegraph::commands.new_bot.ask_to_add_a_chat'))) { 42 | while (empty($chat_id = $this->ask(__('telegraph::commands.new_chat.enter_chat_id')))) { 43 | $this->error(__('telegraph::errors.empty_chat_id')); 44 | } 45 | 46 | if ($chat_id != 'x') { 47 | $chat_name = $this->ask(__('telegraph::commands.new_chat.enter_chat_name')); 48 | $bot->chats()->create([ 49 | 'chat_id' => $chat_id, 50 | 'name' => $chat_name, 51 | ]); 52 | } 53 | } 54 | 55 | 56 | if ($this->confirm(__('telegraph::commands.new_bot.ask_to_setup_webhook'))) { 57 | Artisan::call('telegraph:set-webhook', [ 58 | 'bot' => $bot->id, 59 | '--drop-pending-updates' => $this->option('drop-pending-updates'), 60 | '--max-connections' => $this->option('max-connections'), 61 | '--secret' => $this->option('secret'), 62 | ]); 63 | } 64 | 65 | $this->info(__('telegraph::commands.new_bot.bot_created', ['bot_name' => $bot->name])); 66 | 67 | return self::SUCCESS; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/DTO/ChatJoinRequest.php: -------------------------------------------------------------------------------- 1 | > 11 | */ 12 | class ChatJoinRequest implements Arrayable 13 | { 14 | private int $userChatId; 15 | private CarbonInterface $date; 16 | private ?string $bio = null; 17 | private ?ChatInviteLink $inviteLink = null; 18 | private Chat $chat; 19 | private User $from; 20 | 21 | private function __construct() 22 | { 23 | } 24 | 25 | /** 26 | * @param array{ 27 | * user_chat_id: int, 28 | * date: int, 29 | * bio?: string, 30 | * invite_link?: array, 31 | * chat: array, 32 | * from: array, 33 | * } $data 34 | */ 35 | public static function fromArray(array $data): ChatJoinRequest 36 | { 37 | $request = new self(); 38 | 39 | $request->userChatId = $data['user_chat_id']; 40 | 41 | $request->date = Carbon::createFromTimestamp($data['date']); 42 | 43 | if (isset($data['bio'])) { 44 | $request->bio = $data['bio']; 45 | } 46 | 47 | if (isset($data['invite_link'])) { 48 | $request->inviteLink = ChatInviteLink::fromArray($data['invite_link']); 49 | } 50 | 51 | $request->chat = Chat::fromArray($data['chat']); 52 | 53 | $request->from = User::fromArray($data['from']); 54 | 55 | return $request; 56 | } 57 | 58 | public function userChatId(): int 59 | { 60 | return $this->userChatId; 61 | } 62 | 63 | public function date(): CarbonInterface 64 | { 65 | return $this->date; 66 | } 67 | 68 | public function bio(): ?string 69 | { 70 | return $this->bio; 71 | } 72 | 73 | public function inviteLink(): ?ChatInviteLink 74 | { 75 | return $this->inviteLink; 76 | } 77 | 78 | public function chat(): Chat 79 | { 80 | return $this->chat; 81 | } 82 | 83 | public function from(): User 84 | { 85 | return $this->from; 86 | } 87 | 88 | public function toArray(): array 89 | { 90 | return array_filter([ 91 | 'user_chat_id' => $this->userChatId, 92 | 'date' => $this->date->timestamp, 93 | 'bio' => $this->bio, 94 | 'invite_link' => $this->inviteLink?->toArray(), 95 | 'chat' => $this->chat->toArray(), 96 | 'from' => $this->from->toArray(), 97 | ], fn ($value) => $value !== null); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Exceptions/FileException.php: -------------------------------------------------------------------------------- 1 | join(', ', ' and '))); 35 | } 36 | 37 | public static function fileNotFound(string $fileType, string $path): FileException 38 | { 39 | return new self("$fileType [$path] not found"); 40 | } 41 | 42 | public static function photoSizeExceeded(float $sizeMb, float $maxSizeMb): FileException 43 | { 44 | return new self(sprintf("Photo size (%f Mb) exceeds max allowed size of %f MB", $sizeMb, $maxSizeMb)); 45 | } 46 | 47 | public static function invalidPhotoSize(int $totalLength, int $maxTotalLength): FileException 48 | { 49 | return new self(sprintf("Photo's sum of width and height (%dpx) exceed allowed %dpx", $totalLength, $maxTotalLength)); 50 | } 51 | 52 | public static function invalidPhotoRatio(float $ratio, float $maxRatio): FileException 53 | { 54 | $relativeRatio = $ratio < $maxRatio ? 1 / $ratio : $ratio; 55 | 56 | return new self(sprintf("Ratio of height and width (%f) exceeds max allowed ratio of %f", $relativeRatio, $maxRatio)); 57 | } 58 | 59 | public static function failedToRetreiveFileInfo(string $fileId): FileException 60 | { 61 | return new self("Failed to retreive info for file [$fileId]"); 62 | } 63 | 64 | public static function unableToDownloadFile(string $fileId): FileException 65 | { 66 | return new self("An error occourred while trying to download file [$fileId]"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/DTO/Venue.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Venue implements Arrayable 11 | { 12 | private Location $location; 13 | private string $title; 14 | private string $address; 15 | private ?string $foursquareId = null; 16 | private ?string $foursquareType = null; 17 | private ?string $googlePlaceId = null; 18 | private ?string $googlePlaceType = null; 19 | 20 | private function __construct() 21 | { 22 | } 23 | 24 | /** 25 | * @param array{ 26 | * location: array{longitude: float, latitude: float, horizontal_accuracy?: float}, 27 | * title: string, 28 | * address: string, 29 | * foursquare_id?: string, 30 | * foursquare_type?: string, 31 | * google_place_id?: string, 32 | * google_place_type?: string 33 | * } $data 34 | */ 35 | public static function fromArray(array $data): Venue 36 | { 37 | $venue = new self(); 38 | 39 | $venue->location = Location::fromArray($data['location']); 40 | $venue->title = $data['title']; 41 | $venue->address = $data['address']; 42 | $venue->foursquareId = $data['foursquare_id'] ?? null; 43 | $venue->foursquareType = $data['foursquare_type'] ?? null; 44 | $venue->googlePlaceId = $data['google_place_id'] ?? null; 45 | $venue->googlePlaceType = $data['google_place_type'] ?? null; 46 | 47 | return $venue; 48 | } 49 | 50 | public function location(): Location 51 | { 52 | return $this->location; 53 | } 54 | 55 | public function title(): string 56 | { 57 | return $this->title; 58 | } 59 | 60 | public function address(): string 61 | { 62 | return $this->address; 63 | } 64 | 65 | public function foursquareId(): ?string 66 | { 67 | return $this->foursquareId; 68 | } 69 | 70 | public function foursquareType(): ?string 71 | { 72 | return $this->foursquareType; 73 | } 74 | 75 | public function googlePlaceId(): ?string 76 | { 77 | return $this->googlePlaceId; 78 | } 79 | 80 | public function googlePlaceType(): ?string 81 | { 82 | return $this->googlePlaceType; 83 | } 84 | 85 | public function toArray(): array 86 | { 87 | return array_filter([ 88 | 'location' => $this->location, 89 | 'title' => $this->title, 90 | 'address' => $this->address, 91 | 'foursquare_id' => $this->foursquareId, 92 | 'foursquare_type' => $this->foursquareType, 93 | 'google_place_id' => $this->googlePlaceId, 94 | 'google_place_type' => $this->googlePlaceType, 95 | ], fn ($value) => $value !== null); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/DTO/User.php: -------------------------------------------------------------------------------- 1 | > 13 | */ 14 | class User implements Arrayable, Storable 15 | { 16 | use HasStorage; 17 | 18 | private int $id; 19 | private bool $isBot; 20 | private string $firstName; 21 | private string $lastName; 22 | private string $username; 23 | private string $languageCode; 24 | private bool $isPremium; 25 | 26 | private function __construct() 27 | { 28 | } 29 | 30 | /** 31 | * @param array{id:int, is_bot?:bool, first_name?:string, last_name?:string, username?:string, language_code?:string, is_premium?:bool} $data 32 | */ 33 | public static function fromArray(array $data): User 34 | { 35 | $user = new self(); 36 | 37 | $user->id = $data['id']; 38 | $user->isBot = $data['is_bot'] ?? false; 39 | 40 | $user->firstName = $data['first_name'] ?? ''; 41 | $user->lastName = $data['last_name'] ?? ''; 42 | $user->username = $data['username'] ?? ''; 43 | $user->languageCode = $data['language_code'] ?? ''; 44 | $user->isPremium = $data['is_premium'] ?? false; 45 | 46 | return $user; 47 | } 48 | 49 | public function storageKey(): string|int 50 | { 51 | return $this->id; 52 | } 53 | 54 | public function id(): int 55 | { 56 | return $this->id; 57 | } 58 | 59 | public function isBot(): bool 60 | { 61 | return $this->isBot; 62 | } 63 | 64 | public function firstName(): string 65 | { 66 | return $this->firstName; 67 | } 68 | 69 | public function lastName(): string 70 | { 71 | return $this->lastName; 72 | } 73 | 74 | public function username(): string 75 | { 76 | return $this->username; 77 | } 78 | 79 | public function languageCode(): string 80 | { 81 | return $this->languageCode; 82 | } 83 | 84 | public function isPremium(): bool 85 | { 86 | return $this->isPremium; 87 | } 88 | 89 | public function toArray(): array 90 | { 91 | return array_filter([ 92 | 'id' => $this->id, 93 | 'is_bot' => $this->isBot, 94 | 'first_name' => $this->firstName, 95 | 'last_name' => $this->lastName, 96 | 'username' => $this->username, 97 | 'language_code' => $this->languageCode, 98 | 'is_premium' => $this->isPremium, 99 | ], fn ($value) => $value !== null); //@phpstan-ignore-line 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultGif.php: -------------------------------------------------------------------------------- 1 | id = $id; 26 | $result->url = $url; 27 | $result->thumbUrl = $thumbUrl; 28 | 29 | return $result; 30 | } 31 | 32 | public function width(int|null $width): static 33 | { 34 | $this->width = $width; 35 | 36 | return $this; 37 | } 38 | 39 | public function height(int|null $height): static 40 | { 41 | $this->height = $height; 42 | 43 | return $this; 44 | } 45 | 46 | public function duration(int|null $duration): static 47 | { 48 | $this->duration = $duration; 49 | 50 | return $this; 51 | } 52 | 53 | public function title(string|null $title): static 54 | { 55 | $this->title = $title; 56 | 57 | return $this; 58 | } 59 | 60 | public function caption(string|null $caption): static 61 | { 62 | $this->caption = $caption; 63 | 64 | return $this; 65 | } 66 | 67 | public function html(): static 68 | { 69 | $this->parseMode = Telegraph::PARSE_HTML; 70 | 71 | return $this; 72 | } 73 | 74 | public function markdown(): static 75 | { 76 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 77 | 78 | return $this; 79 | } 80 | 81 | public function markdownV2(): static 82 | { 83 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * @inheritDoc 90 | */ 91 | public function data(): array 92 | { 93 | return array_filter([ 94 | 'gif_url' => $this->url, 95 | 'thumb_url' => $this->thumbUrl, 96 | 'gif_width' => $this->width, 97 | 'gif_height' => $this->height, 98 | 'gif_duration' => $this->duration, 99 | 'title' => $this->title, 100 | 'caption' => $this->caption, 101 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 102 | ], fn ($value) => $value !== null); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultPhoto.php: -------------------------------------------------------------------------------- 1 | id = $id; 26 | $result->url = $url; 27 | $result->thumbUrl = $thumbUrl; 28 | 29 | return $result; 30 | } 31 | 32 | public function width(int|null $width): static 33 | { 34 | $this->width = $width; 35 | 36 | return $this; 37 | } 38 | 39 | public function height(int|null $height): static 40 | { 41 | $this->height = $height; 42 | 43 | return $this; 44 | } 45 | 46 | public function title(string|null $title): static 47 | { 48 | $this->title = $title; 49 | 50 | return $this; 51 | } 52 | 53 | public function caption(string|null $caption): static 54 | { 55 | $this->caption = $caption; 56 | 57 | return $this; 58 | } 59 | 60 | public function description(string|null $description): static 61 | { 62 | $this->description = $description; 63 | 64 | return $this; 65 | } 66 | 67 | public function html(): static 68 | { 69 | $this->parseMode = Telegraph::PARSE_HTML; 70 | 71 | return $this; 72 | } 73 | 74 | public function markdown(): static 75 | { 76 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 77 | 78 | return $this; 79 | } 80 | 81 | public function markdownV2(): static 82 | { 83 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * @inheritDoc 90 | */ 91 | public function data(): array 92 | { 93 | return array_filter([ 94 | 'photo_url' => $this->url, 95 | 'thumb_url' => $this->thumbUrl, 96 | 'photo_width' => $this->width, 97 | 'photo_height' => $this->height, 98 | 'title' => $this->title, 99 | 'caption' => $this->caption, 100 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 101 | 'description' => $this->description, 102 | ], fn ($value) => $value !== null); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/DTO/Animation.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Animation implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | 15 | private int $width; 16 | private int $height; 17 | private ?int $duration = null; 18 | private ?int $filesize = null; 19 | 20 | private ?string $filename = null; 21 | private ?string $mimeType = null; 22 | 23 | private ?Photo $thumbnail = null; 24 | 25 | private function __construct() 26 | { 27 | } 28 | 29 | /** 30 | * @param array{ 31 | * file_id: string, 32 | * width: int, 33 | * height: int, 34 | * duration: int, 35 | * file_name?: string, 36 | * mime_type?: string, 37 | * file_size?: int, 38 | * thumb?: array, 39 | * } $data 40 | */ 41 | public static function fromArray(array $data): Animation 42 | { 43 | $animation = new self(); 44 | 45 | $animation->id = $data['file_id']; 46 | $animation->width = $data['width']; 47 | $animation->height = $data['height']; 48 | $animation->duration = $data['duration']; 49 | 50 | $animation->filesize = $data['file_size'] ?? null; 51 | $animation->filename = $data['file_name'] ?? null; 52 | $animation->mimeType = $data['mime_type'] ?? null; 53 | 54 | if (isset($data['thumb'])) { 55 | $animation->thumbnail = Photo::fromArray($data['thumb']); 56 | } 57 | 58 | return $animation; 59 | } 60 | 61 | public function id(): string 62 | { 63 | return $this->id; 64 | } 65 | 66 | public function width(): int 67 | { 68 | return $this->width; 69 | } 70 | 71 | public function height(): int 72 | { 73 | return $this->height; 74 | } 75 | 76 | public function duration(): ?int 77 | { 78 | return $this->duration; 79 | } 80 | 81 | public function filesize(): ?int 82 | { 83 | return $this->filesize; 84 | } 85 | 86 | public function filename(): ?string 87 | { 88 | return $this->filename; 89 | } 90 | 91 | public function mimeType(): ?string 92 | { 93 | return $this->mimeType; 94 | } 95 | 96 | public function toArray(): array 97 | { 98 | return array_filter([ 99 | 'id' => $this->id, 100 | 'width' => $this->width, 101 | 'height' => $this->height, 102 | 'duration' => $this->duration, 103 | 'filename' => $this->filename, 104 | 'mime_type' => $this->mimeType, 105 | 'filesize' => $this->filesize, 106 | 'thumbnail' => $this->thumbnail?->toArray(), 107 | ], fn ($value) => $value !== null); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/DTO/Entity.php: -------------------------------------------------------------------------------- 1 | > 11 | */ 12 | class Entity implements Arrayable 13 | { 14 | private string $type; 15 | 16 | private int $offset; 17 | 18 | private int $length; 19 | 20 | private ?string $url = null; 21 | 22 | private ?User $user = null; 23 | 24 | private ?string $language = null; 25 | 26 | private ?string $customEmojiId = null; 27 | 28 | private function __construct() 29 | { 30 | } 31 | 32 | /** 33 | * @param array{ 34 | * type: string, 35 | * offset: int, 36 | * length: int, 37 | * url?: string, 38 | * user?: array, 39 | * language?: string, 40 | * custom_emoji_id?: string 41 | * } $data 42 | * 43 | * @return \DefStudio\Telegraph\DTO\Entity 44 | */ 45 | public static function fromArray(array $data): Entity 46 | { 47 | $entity = new self(); 48 | 49 | $entity->type = $data['type']; 50 | $entity->offset = $data['offset']; 51 | $entity->length = $data['length']; 52 | 53 | if (isset($data['url'])) { 54 | $entity->url = $data['url']; 55 | } 56 | 57 | if (isset($data['user'])) { 58 | $entity->user = User::fromArray($data['user']); 59 | } 60 | 61 | if (isset($data['language'])) { 62 | $entity->language = $data['language']; 63 | } 64 | 65 | if (isset($data['custom_emoji_id'])) { 66 | $entity->customEmojiId = (string) $data['custom_emoji_id']; 67 | } 68 | 69 | return $entity; 70 | } 71 | 72 | public function type(): string 73 | { 74 | return $this->type; 75 | } 76 | 77 | public function offset(): int 78 | { 79 | return $this->offset; 80 | } 81 | 82 | public function length(): int 83 | { 84 | return $this->length; 85 | } 86 | 87 | public function url(): ?string 88 | { 89 | return $this->url; 90 | } 91 | 92 | public function user(): ?User 93 | { 94 | return $this->user; 95 | } 96 | 97 | public function language(): ?string 98 | { 99 | return $this->language; 100 | } 101 | 102 | public function customEmojiId(): ?string 103 | { 104 | return $this->customEmojiId; 105 | } 106 | 107 | public function toArray(): array 108 | { 109 | return array_filter([ 110 | 'type' => $this->type, 111 | 'offset' => $this->offset, 112 | 'length' => $this->length, 113 | 'url' => $this->url, 114 | 'user' => $this->user()?->toArray(), 115 | 'language' => $this->language, 116 | 'custom_emoji_id' => $this->customEmojiId, 117 | ], fn ($value) => $value !== null); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/DTO/Video.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Video implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private int $width; 15 | private int $height; 16 | private int $duration; 17 | 18 | private ?string $filename = null; 19 | private ?string $mimeType = null; 20 | private ?int $filesize = null; 21 | 22 | private ?Photo $thumbnail = null; 23 | 24 | private function __construct() 25 | { 26 | } 27 | 28 | /** 29 | * @param array{ 30 | * file_id: string, 31 | * width: int, 32 | * height: int, 33 | * duration: int, 34 | * file_name?: string, 35 | * mime_type?: string, 36 | * file_size?: int, 37 | * thumb?: array, 38 | * } $data 39 | */ 40 | public static function fromArray(array $data): Video 41 | { 42 | $video = new self(); 43 | 44 | $video->id = $data['file_id']; 45 | $video->width = $data['width']; 46 | $video->height = $data['height']; 47 | $video->duration = $data['duration']; 48 | $video->filename = $data['file_name'] ?? null; 49 | $video->mimeType = $data['mime_type'] ?? null; 50 | $video->filesize = $data['file_size'] ?? null; 51 | 52 | if (isset($data['thumb'])) { 53 | $video->thumbnail = Photo::fromArray($data['thumb']); 54 | } 55 | 56 | return $video; 57 | } 58 | 59 | public function id(): string 60 | { 61 | return $this->id; 62 | } 63 | 64 | public function width(): int 65 | { 66 | return $this->width; 67 | } 68 | 69 | public function height(): int 70 | { 71 | return $this->height; 72 | } 73 | 74 | public function duration(): int 75 | { 76 | return $this->duration; 77 | } 78 | 79 | public function filename(): ?string 80 | { 81 | return $this->filename; 82 | } 83 | 84 | public function mimeType(): ?string 85 | { 86 | return $this->mimeType; 87 | } 88 | 89 | public function filesize(): ?int 90 | { 91 | return $this->filesize; 92 | } 93 | 94 | public function thumbnail(): ?Photo 95 | { 96 | return $this->thumbnail; 97 | } 98 | 99 | public function toArray(): array 100 | { 101 | return array_filter([ 102 | 'id' => $this->id, 103 | 'width' => $this->width, 104 | 'height' => $this->height, 105 | 'duration' => $this->duration, 106 | 'filename' => $this->filename, 107 | 'mime_type' => $this->mimeType, 108 | 'filesize' => $this->filesize, 109 | 'thumbnail' => $this->thumbnail?->toArray(), 110 | ], fn ($value) => $value !== null); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/DTO/PreCheckoutQuery.php: -------------------------------------------------------------------------------- 1 | > 11 | */ 12 | class PreCheckoutQuery implements Arrayable 13 | { 14 | private int $id; 15 | private User $from; 16 | private string $currency; 17 | private int $totalAmount; 18 | private string $invoicePayload; 19 | private ?string $shippingOptionId = null; 20 | private ?OrderInfo $orderInfo = null; 21 | 22 | private function __construct() 23 | { 24 | } 25 | 26 | /** 27 | * @param array{ 28 | * id:int, 29 | * from:array, 30 | * currency:string, 31 | * total_amount:int, 32 | * invoice_payload:string, 33 | * shipping_option_id?:string, 34 | * order_info?:array 35 | * } $data 36 | */ 37 | public static function fromArray(array $data): PreCheckoutQuery 38 | { 39 | $preCheckoutQuery = new self(); 40 | 41 | $preCheckoutQuery->id = $data['id']; 42 | 43 | $preCheckoutQuery->from = User::fromArray($data['from']); 44 | 45 | $preCheckoutQuery->currency = $data['currency']; 46 | $preCheckoutQuery->totalAmount = $data['total_amount']; 47 | $preCheckoutQuery->invoicePayload = $data['invoice_payload']; 48 | $preCheckoutQuery->shippingOptionId = $data['shipping_option_id'] ?? null; 49 | 50 | 51 | if (isset($data['order_info'])) { 52 | $preCheckoutQuery->orderInfo = OrderInfo::fromArray($data['order_info']); 53 | } 54 | 55 | return $preCheckoutQuery; 56 | } 57 | 58 | public function id(): int 59 | { 60 | return $this->id; 61 | } 62 | 63 | public function from(): User 64 | { 65 | return $this->from; 66 | } 67 | 68 | public function currency(): string 69 | { 70 | return $this->currency; 71 | } 72 | 73 | public function totalAmount(): int 74 | { 75 | return $this->totalAmount; 76 | } 77 | 78 | public function invoicePayload(): string 79 | { 80 | return $this->invoicePayload; 81 | } 82 | 83 | public function shippingOptionId(): ?string 84 | { 85 | return $this->shippingOptionId; 86 | } 87 | 88 | public function orderInfo(): ?OrderInfo 89 | { 90 | return $this->orderInfo; 91 | } 92 | 93 | public function toArray(): array 94 | { 95 | return array_filter([ 96 | 'id' => $this->id, 97 | 'from' => $this->from->toArray(), 98 | 'currency' => $this->currency, 99 | 'total_amount' => $this->totalAmount, 100 | 'invoice_payload' => $this->invoicePayload, 101 | 'shipping_option_id' => $this->shippingOptionId, 102 | 'order_info' => $this->orderInfo?->toArray(), 103 | ], fn ($value) => $value !== null); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultDocument.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $result->title = $title; 28 | $result->url = $url; 29 | $result->mimeType = $mimeType; 30 | 31 | return $result; 32 | } 33 | 34 | public function caption(string|null $caption): static 35 | { 36 | $this->caption = $caption; 37 | 38 | return $this; 39 | } 40 | 41 | public function description(string|null $description): static 42 | { 43 | $this->description = $description; 44 | 45 | return $this; 46 | } 47 | 48 | public function thumbUrl(string|null $thumbUrl): static 49 | { 50 | $this->thumbUrl = $thumbUrl; 51 | 52 | return $this; 53 | } 54 | 55 | public function thumbWidth(int|null $thumbWidth): static 56 | { 57 | $this->thumbWidth = $thumbWidth; 58 | 59 | return $this; 60 | } 61 | 62 | public function thumbHeight(int|null $thumbHeight): static 63 | { 64 | $this->thumbHeight = $thumbHeight; 65 | 66 | return $this; 67 | } 68 | 69 | public function html(): static 70 | { 71 | $this->parseMode = Telegraph::PARSE_HTML; 72 | 73 | return $this; 74 | } 75 | 76 | public function markdown(): static 77 | { 78 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 79 | 80 | return $this; 81 | } 82 | 83 | public function markdownV2(): static 84 | { 85 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * @inheritDoc 92 | */ 93 | public function data(): array 94 | { 95 | return array_filter([ 96 | 'title' => $this->title, 97 | 'caption' => $this->caption, 98 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 99 | 'document_url' => $this->url, 100 | 'mime_type' => $this->mimeType, 101 | 'description' => $this->description, 102 | 'thumb_url' => $this->thumbUrl, 103 | 'thumb_width' => $this->thumbWidth, 104 | 'thumb_height' => $this->thumbHeight, 105 | ], fn ($value) => $value !== null); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Games/TelegraphGamePayload.php: -------------------------------------------------------------------------------- 1 | endpoint = self::ENDPOINT_SEND_GAME; 21 | 22 | $telegraph->data['game_short_name'] = $shortName; 23 | 24 | return $telegraph; 25 | } 26 | 27 | public function businessConnectionId(string $id): static 28 | { 29 | $telegraph = clone $this; 30 | 31 | $telegraph->data['business_connection_id'] = $id; 32 | 33 | return $telegraph; 34 | } 35 | 36 | public function messageThreadId(string $id): static 37 | { 38 | $telegraph = clone $this; 39 | 40 | $telegraph->data['message_thread_id'] = $id; 41 | 42 | return $telegraph; 43 | } 44 | 45 | public function messageEffectId(string $id): static 46 | { 47 | $telegraph = clone $this; 48 | 49 | $telegraph->data['message_effect_id'] = $id; 50 | 51 | return $telegraph; 52 | } 53 | 54 | public function disableNotification(bool $disable = true): static 55 | { 56 | $telegraph = clone $this; 57 | 58 | $telegraph->data['disable_notification'] = $disable; 59 | 60 | return $telegraph; 61 | } 62 | 63 | public function protectContent(bool $protect = true): static 64 | { 65 | $telegraph = clone $this; 66 | 67 | $telegraph->data['protect_content'] = $protect; 68 | 69 | return $telegraph; 70 | } 71 | 72 | public function allowPaidBroadcast(bool $allow = true): static 73 | { 74 | $telegraph = clone $this; 75 | 76 | $telegraph->data['allow_paid_broadcast'] = $allow; 77 | 78 | return $telegraph; 79 | } 80 | 81 | protected function prepareData(): array 82 | { 83 | $data = parent::prepareData(); 84 | 85 | if (empty($data['chat_id']) && $this->endpoint === self::ENDPOINT_SEND_GAME) { 86 | $data['chat_id'] = $this->getChatId(); 87 | } 88 | 89 | $validator = Validator::make($data, [ 90 | 'business_connection_id' => ['nullable', 'string'], 91 | 'message_thread_id' => ['nullable', 'string'], 92 | 'game_short_name' => ['required', 'string'], 93 | 'disable_notification' => ['nullable', 'boolean'], 94 | 'protect_content' => ['nullable', 'boolean'], 95 | 'allow_paid_broadcast' => ['nullable', 'boolean'], 96 | 'message_effect_id' => ['nullable', 'string'], 97 | ]); 98 | 99 | if ($validator->fails()) { 100 | throw InvoiceException::validationError($validator->messages()); 101 | } 102 | 103 | return $data; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultVideo.php: -------------------------------------------------------------------------------- 1 | id = $id; 28 | $result->url = $url; 29 | $result->mimeType = $mimeType; 30 | $result->thumbUrl = $thumbUrl; 31 | $result->title = $title; 32 | 33 | return $result; 34 | } 35 | 36 | public function caption(string|null $caption): static 37 | { 38 | $this->caption = $caption; 39 | 40 | return $this; 41 | } 42 | 43 | public function description(string|null $description): static 44 | { 45 | $this->description = $description; 46 | 47 | return $this; 48 | } 49 | 50 | public function width(int|null $width): static 51 | { 52 | $this->width = $width; 53 | 54 | return $this; 55 | } 56 | 57 | public function height(int|null $height): static 58 | { 59 | $this->height = $height; 60 | 61 | return $this; 62 | } 63 | 64 | public function duration(int|null $duration): static 65 | { 66 | $this->duration = $duration; 67 | 68 | return $this; 69 | } 70 | 71 | public function html(): static 72 | { 73 | $this->parseMode = Telegraph::PARSE_HTML; 74 | 75 | return $this; 76 | } 77 | 78 | public function markdown(): static 79 | { 80 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 81 | 82 | return $this; 83 | } 84 | 85 | public function markdownV2(): static 86 | { 87 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * @inheritDoc 94 | */ 95 | public function data(): array 96 | { 97 | return array_filter([ 98 | 'video_url' => $this->url, 99 | 'mime_type' => $this->mimeType, 100 | 'thumb_url' => $this->thumbUrl, 101 | 'title' => $this->title, 102 | 'caption' => $this->caption, 103 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 104 | 'video_width' => $this->width, 105 | 'video_height' => $this->height, 106 | 'video_duration' => $this->duration, 107 | 'description' => $this->description, 108 | ], fn ($value) => $value !== null); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/DTO/Chat.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Chat implements Arrayable 13 | { 14 | public const TYPE_SENDER = 'sender'; 15 | public const TYPE_PRIVATE = 'private'; 16 | public const TYPE_GROUP = 'group'; 17 | public const TYPE_SUPERGROUP = 'supergroup'; 18 | public const TYPE_CHANNEL = 'channel'; 19 | 20 | private string $id; 21 | private string $type; 22 | private ?string $title; 23 | private ?string $username; 24 | private ?string $firstName; 25 | private ?string $lastName; 26 | private bool $isForum = false; 27 | private bool $isDirectMessages = false; 28 | 29 | private function __construct() 30 | { 31 | } 32 | 33 | /** 34 | * @param array{ 35 | * id:string, 36 | * type:string, 37 | * title?:string, 38 | * username?:string, 39 | * first_name?:string, 40 | * last_name?:string, 41 | * is_forum?:bool, 42 | * is_direct_messages?:bool, 43 | * } $data 44 | */ 45 | public static function fromArray(array $data): Chat 46 | { 47 | $chat = new self(); 48 | 49 | $chat->id = $data['id']; 50 | $chat->type = $data['type']; 51 | $chat->title = $data['title'] ?? null; 52 | $chat->username = $data['username'] ?? null; 53 | $chat->firstName = $data['first_name'] ?? null; 54 | $chat->lastName = $data['last_name'] ?? null; 55 | $chat->isForum = $data['is_forum'] ?? false; 56 | $chat->isDirectMessages = $data['is_direct_messages'] ?? false; 57 | 58 | return $chat; 59 | } 60 | 61 | public function id(): string 62 | { 63 | return $this->id; 64 | } 65 | 66 | public function type(): string 67 | { 68 | return $this->type; 69 | } 70 | 71 | public function title(): ?string 72 | { 73 | return $this->title; 74 | } 75 | 76 | public function username(): ?string 77 | { 78 | return $this->username; 79 | } 80 | 81 | public function firstName(): ?string 82 | { 83 | return $this->firstName; 84 | } 85 | 86 | public function lastName(): ?string 87 | { 88 | return $this->lastName; 89 | } 90 | 91 | public function isForum(): bool 92 | { 93 | return $this->isForum; 94 | } 95 | 96 | public function isDirectMessages(): bool 97 | { 98 | return $this->isDirectMessages; 99 | } 100 | 101 | public function toArray(): array 102 | { 103 | return array_filter([ 104 | 'id' => $this->id, 105 | 'type' => $this->type, 106 | 'title' => $this->title, 107 | 'username' => $this->username, 108 | 'first_name' => $this->firstName, 109 | 'last_name' => $this->lastName, 110 | 'is_forum' => $this->isForum, 111 | 'is_direct_messages' => $this->isDirectMessages, 112 | ], fn ($value) => $value !== null); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Keyboard/ReplyButton.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | private array $pollType; 19 | 20 | private int $width = 0; 21 | 22 | private function __construct( 23 | private string $label, 24 | ) { 25 | } 26 | 27 | public static function make(string $label): ReplyButton 28 | { 29 | return new self($label); 30 | } 31 | 32 | public function width(float $percentage): ReplyButton 33 | { 34 | $width = (int)($percentage * 100); 35 | 36 | if ($width > 100) { 37 | $width = 100; 38 | } 39 | 40 | $this->width = $width; 41 | 42 | return $this; 43 | } 44 | 45 | public function webApp(string $url): static 46 | { 47 | $this->type = ReplyButtonType::WEB_APP; 48 | $this->webAppUrl = $url; 49 | 50 | return $this; 51 | } 52 | 53 | public function requestContact(): static 54 | { 55 | $this->type = ReplyButtonType::REQUEST_CONTACT; 56 | 57 | return $this; 58 | } 59 | 60 | public function requestLocation(): static 61 | { 62 | $this->type = ReplyButtonType::REQUEST_LOCATION; 63 | 64 | return $this; 65 | } 66 | 67 | public function requestPoll(): static 68 | { 69 | $this->type = ReplyButtonType::REQUEST_POLL; 70 | $this->pollType = ['type' => 'regular']; 71 | 72 | return $this; 73 | } 74 | 75 | public function requestQuiz(): static 76 | { 77 | $this->type = ReplyButtonType::REQUEST_POLL; 78 | $this->pollType = ['type' => 'quiz']; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @return array 85 | */ 86 | public function toArray(): array 87 | { 88 | $data = ['text' => $this->label]; 89 | 90 | if ($this->type === ReplyButtonType::WEB_APP) { 91 | $data['web_app'] = [ 92 | 'url' => $this->webAppUrl, 93 | ]; 94 | } 95 | 96 | if ($this->type === ReplyButtonType::REQUEST_CONTACT) { 97 | $data['request_contact'] = true; 98 | } 99 | 100 | if ($this->type === ReplyButtonType::REQUEST_LOCATION) { 101 | $data['request_location'] = true; 102 | } 103 | 104 | if ($this->type === ReplyButtonType::REQUEST_POLL) { 105 | $data['request_poll'] = $this->pollType; 106 | } 107 | 108 | return $data; 109 | } 110 | 111 | public function label(): string 112 | { 113 | return $this->label; 114 | } 115 | 116 | public function get_width(): float 117 | { 118 | if ($this->width === 0) { 119 | return 1; 120 | } 121 | 122 | return $this->width / 100; 123 | } 124 | 125 | public function has_width(): bool 126 | { 127 | return $this->width > 0; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/DTO/Game.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Game implements Arrayable 12 | { 13 | private string $title; 14 | private string $description; 15 | /** @var Collection */ 16 | private Collection $photos; 17 | /** @var Collection */ 18 | private Collection $entities; 19 | private ?Animation $animation = null; 20 | private ?string $text = null; 21 | 22 | private function __construct() 23 | { 24 | $this->photos = Collection::empty(); 25 | $this->entities = Collection::empty(); 26 | 27 | } 28 | 29 | /** 30 | * @param array{ 31 | * title?: string, 32 | * description?: string, 33 | * entities?: array, 34 | * photo?: array, 35 | * animation?:array, 36 | * text?:string 37 | * } $data 38 | */ 39 | public static function fromArray(array $data): Game 40 | { 41 | $game = new self(); 42 | 43 | $game->title = $data['title'] ?? ''; 44 | $game->description = $data['description'] ?? ''; 45 | $game->text = $data['text'] ?? ''; 46 | 47 | /* @phpstan-ignore-next-line */ 48 | $game->photos = collect($data['photo'] ?? [])->map(fn (array $photoData) => Photo::fromArray($photoData)); 49 | 50 | if (isset($data['animation'])) { 51 | $game->animation = Animation::fromArray($data['animation']); 52 | } 53 | 54 | if (isset($data['entities']) && $data['entities']) { 55 | /* @phpstan-ignore-next-line */ 56 | $game->entities = collect($data['entities'])->map(fn (array $entity) => Entity::fromArray($entity)); 57 | } 58 | 59 | return $game; 60 | } 61 | 62 | public function title(): string 63 | { 64 | return $this->title; 65 | } 66 | 67 | public function description(): string 68 | { 69 | return $this->description; 70 | } 71 | 72 | public function text(): ?string 73 | { 74 | return $this->text; 75 | } 76 | 77 | public function animation(): ?Animation 78 | { 79 | return $this->animation; 80 | } 81 | 82 | /** 83 | * @return Collection 84 | */ 85 | public function photos(): Collection 86 | { 87 | return $this->photos; 88 | } 89 | 90 | /** 91 | * @return Collection 92 | */ 93 | public function entities(): Collection 94 | { 95 | return $this->entities; 96 | } 97 | 98 | public function toArray(): array 99 | { 100 | return array_filter([ 101 | 'title' => $this->title, 102 | 'description' => $this->description, 103 | 'text' => $this->text, 104 | 'photos' => $this->photos->toArray(), 105 | 'animation' => $this->animation?->toArray(), 106 | 'entities' => $this->entities->toArray(), 107 | ], fn ($value) => $value !== null); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/DTO/Sticker.php: -------------------------------------------------------------------------------- 1 | > 10 | */ 11 | class Sticker implements Arrayable, Downloadable 12 | { 13 | private string $id; 14 | private string $type; 15 | private int $width; 16 | private int $height; 17 | private bool $isAnimated = false; 18 | private bool $isVideo = false; 19 | private ?string $emoji = null ; 20 | private ?int $filesize = null; 21 | private ?Photo $thumbnail = null; 22 | 23 | private function __construct() 24 | { 25 | } 26 | 27 | /** 28 | * @param array{ 29 | * file_id: string, 30 | * width: int, 31 | * height: int, 32 | * type: string, 33 | * is_animated: bool, 34 | * is_video: bool, 35 | * file_size?: int, 36 | * emoji?: string, 37 | * thumb?: array, 38 | * } $data 39 | */ 40 | public static function fromArray(array $data): Sticker 41 | { 42 | $sticker = new self(); 43 | 44 | $sticker->id = $data['file_id']; 45 | $sticker->width = $data['width']; 46 | $sticker->height = $data['height']; 47 | $sticker->type = $data['type']; 48 | $sticker->isAnimated = $data['is_animated']; 49 | $sticker->isVideo = $data['is_video']; 50 | $sticker->emoji = $data['emoji'] ?? null; 51 | $sticker->filesize = $data['file_size'] ?? null; 52 | 53 | if (isset($data['thumb'])) { 54 | $sticker->thumbnail = Photo::fromArray($data['thumb']); 55 | } 56 | 57 | return $sticker; 58 | } 59 | 60 | public function id(): string 61 | { 62 | return $this->id; 63 | } 64 | 65 | public function width(): int 66 | { 67 | return $this->width; 68 | } 69 | 70 | public function height(): int 71 | { 72 | return $this->height; 73 | } 74 | 75 | public function type(): string 76 | { 77 | return $this->type; 78 | } 79 | 80 | public function isAnimated(): bool 81 | { 82 | return $this->isAnimated; 83 | } 84 | 85 | public function isVideo(): bool 86 | { 87 | return $this->isVideo; 88 | } 89 | 90 | public function emoji(): ?string 91 | { 92 | return $this->emoji; 93 | } 94 | 95 | public function filesize(): ?int 96 | { 97 | return $this->filesize; 98 | } 99 | 100 | public function thumbnail(): ?Photo 101 | { 102 | return $this->thumbnail; 103 | } 104 | 105 | public function toArray(): array 106 | { 107 | return array_filter([ 108 | 'id' => $this->id, 109 | 'width' => $this->width, 110 | 'height' => $this->height, 111 | 'type' => $this->type, 112 | 'is_animated' => $this->isAnimated, 113 | 'is_video' => $this->isVideo, 114 | 'filesize' => $this->filesize, 115 | 'emoji' => $this->emoji, 116 | 'thumbnail' => $this->thumbnail?->toArray(), 117 | ], fn ($value) => $value !== null); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultMpeg4Gif.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $result->mpeg4Url = $mpeg4Url; 28 | $result->thumbUrl = $thumbUrl; 29 | 30 | return $result; 31 | } 32 | 33 | public function mpeg4Width(int|null $mpeg4Width): static 34 | { 35 | $this->mpeg4Width = $mpeg4Width; 36 | 37 | return $this; 38 | } 39 | 40 | public function mpeg4Height(int|null $mpeg4Height): static 41 | { 42 | $this->mpeg4Height = $mpeg4Height; 43 | 44 | return $this; 45 | } 46 | 47 | public function mpeg4Duration(int|null $mpeg4Duration): static 48 | { 49 | $this->mpeg4Duration = $mpeg4Duration; 50 | 51 | return $this; 52 | } 53 | 54 | public function thumbMimeType(string|null $thumbMimeType): static 55 | { 56 | $this->thumbMimeType = $thumbMimeType; 57 | 58 | return $this; 59 | } 60 | 61 | public function title(string|null $title): static 62 | { 63 | $this->title = $title; 64 | 65 | return $this; 66 | } 67 | 68 | public function caption(string|null $caption): static 69 | { 70 | $this->caption = $caption; 71 | 72 | return $this; 73 | } 74 | 75 | public function html(): static 76 | { 77 | $this->parseMode = Telegraph::PARSE_HTML; 78 | 79 | return $this; 80 | } 81 | 82 | public function markdown(): static 83 | { 84 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 85 | 86 | return $this; 87 | } 88 | 89 | public function markdownV2(): static 90 | { 91 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * @inheritDoc 98 | */ 99 | public function data(): array 100 | { 101 | return array_filter([ 102 | 'mpeg4_url' => $this->mpeg4Url, 103 | 'mpeg4_width' => $this->mpeg4Width, 104 | 'mpeg4_height' => $this->mpeg4Height, 105 | 'mpeg4_duration' => $this->mpeg4Duration, 106 | 'thumb_url' => $this->thumbUrl, 107 | 'thumb_mime_type' => $this->thumbMimeType, 108 | 'title' => $this->title, 109 | 'caption' => $this->caption, 110 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 111 | ], fn ($value) => $value !== null); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultContact.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $result->phoneNumber = $phoneNumber; 28 | $result->firstName = $firstName; 29 | $result->message = $message; 30 | 31 | return $result; 32 | } 33 | 34 | public function thumbWidth(int|null $thumbWidth): static 35 | { 36 | $this->thumbWidth = $thumbWidth; 37 | 38 | return $this; 39 | } 40 | 41 | public function thumbHeight(int|null $thumbHeight): static 42 | { 43 | $this->thumbHeight = $thumbHeight; 44 | 45 | return $this; 46 | } 47 | 48 | public function lastName(string|null $lastName): static 49 | { 50 | $this->lastName = $lastName; 51 | 52 | return $this; 53 | } 54 | 55 | public function vcard(string|null $vcard): static 56 | { 57 | $this->vcard = $vcard; 58 | 59 | return $this; 60 | } 61 | 62 | public function thumbUrl(string|null $thumbUrl): static 63 | { 64 | $this->thumbUrl = $thumbUrl; 65 | 66 | return $this; 67 | } 68 | 69 | public function html(): static 70 | { 71 | $this->parseMode = Telegraph::PARSE_HTML; 72 | 73 | return $this; 74 | } 75 | 76 | public function markdown(): static 77 | { 78 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 79 | 80 | return $this; 81 | } 82 | 83 | public function markdownV2(): static 84 | { 85 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * @inheritDoc 92 | */ 93 | public function data(): array 94 | { 95 | $data = [ 96 | 'phone_number' => $this->phoneNumber, 97 | 'first_name' => $this->firstName, 98 | 'last_name' => $this->lastName, 99 | 'vcard' => $this->vcard, 100 | 'thumb_url' => $this->thumbUrl, 101 | 'thumb_width' => $this->thumbWidth, 102 | 'thumb_height' => $this->thumbHeight, 103 | ]; 104 | 105 | if ($this->message !== null) { 106 | $data['input_message_content'] = [ 107 | 'message_text' => $this->message, 108 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 109 | ]; 110 | } 111 | 112 | return array_filter($data, fn ($value) => $value !== null); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "defstudio/telegraph", 3 | "description": "A laravel facade to interact with Telegram Bots", 4 | "keywords": [ 5 | "defstudio", 6 | "laravel", 7 | "telegraph" 8 | ], 9 | "homepage": "https://github.com/defstudio/telegraph", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Fabio Ivona", 14 | "email": "fabio.ivona@defstudio.it", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "illuminate/support": "^10.0 | ^11.0 | ^12.0", 21 | "illuminate/contracts": "^10.0 | ^11.0 | ^12.0", 22 | "illuminate/http": "^10.0 | ^11.0 | ^12.0", 23 | "spatie/laravel-package-tools": "^1.12.1" 24 | }, 25 | "require-dev": { 26 | "ext-sqlite3": "*", 27 | "defstudio/pest-plugin-laravel-expectations": "^2.1.2", 28 | "friendsofphp/php-cs-fixer": "^3.13.0", 29 | "guzzlehttp/guzzle": "^7.5.0", 30 | "nunomaduro/collision": "^7.10.0 | ^8.1.1", 31 | "larastan/larastan": "^1.0.4 | ^2.4.0 | ^3.1.0", 32 | "orchestra/testbench": "^8.0 | ^9.0 | ^10.0", 33 | "pestphp/pest": "^2.34.2 | ^3.7.4", 34 | "pestphp/pest-plugin-laravel": "^2.3.0 | ^3.1.0", 35 | "phpstan/extension-installer": "^1.2.0", 36 | "phpstan/phpstan-deprecation-rules": "^1.0.0 | ^2.0.1", 37 | "phpstan/phpstan-phpunit": "^1.2.2 | ^2.0.4", 38 | "spatie/laravel-ray": "^1.31.0", 39 | "spatie/pest-plugin-snapshots": "^2.1.0", 40 | "spatie/pest-plugin-test-time": "^2.1.0", 41 | "spatie/x-ray": "dev-main" 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "DefStudio\\Telegraph\\": "src", 46 | "DefStudio\\Telegraph\\Database\\Factories\\": "database/factories" 47 | } 48 | }, 49 | "autoload-dev": { 50 | "psr-4": { 51 | "DefStudio\\Telegraph\\Tests\\": "tests" 52 | } 53 | }, 54 | "scripts": { 55 | "x-ray": "vendor/bin/x-ray .", 56 | "lint": "set PHP_CS_FIXER_IGNORE_ENV=1 && php vendor/bin/php-cs-fixer fix -v", 57 | "test:lint": "set PHP_CS_FIXER_IGNORE_ENV=1 && php vendor/bin/php-cs-fixer fix -v --dry-run", 58 | "test:types": "vendor/bin/phpstan analyse --ansi --memory-limit=-1", 59 | "test:unit": "vendor/bin/pest --colors=always --exclude-group=sandbox", 60 | "test:sandbox": "vendor/bin/pest --colors=always --group=sandbox", 61 | "test": [ 62 | "@test:lint", 63 | "@test:types", 64 | "@test:unit", 65 | "@test:sandbox", 66 | "@x-ray" 67 | ], 68 | "update:snapshots": "vendor/bin/pest --colors=always -d --update-snapshots", 69 | "coverage": "@test:unit --coverage" 70 | }, 71 | "config": { 72 | "sort-packages": true, 73 | "allow-plugins": { 74 | "pestphp/pest-plugin": true, 75 | "phpstan/extension-installer": true 76 | } 77 | }, 78 | "extra": { 79 | "laravel": { 80 | "providers": [ 81 | "DefStudio\\Telegraph\\TelegraphServiceProvider" 82 | ], 83 | "aliases": { 84 | "Telegraph": "DefStudio\\Telegraph\\Facades\\Telegraph" 85 | } 86 | } 87 | }, 88 | "minimum-stability": "dev", 89 | "prefer-stable": true 90 | } 91 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultArticle.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $result->title = $title; 28 | $result->message = $message; 29 | 30 | return $result; 31 | } 32 | 33 | public function url(string|null $url): static 34 | { 35 | $this->url = $url; 36 | 37 | return $this; 38 | } 39 | 40 | public function description(string|null $description): static 41 | { 42 | $this->description = $description; 43 | 44 | return $this; 45 | } 46 | 47 | public function thumbUrl(string|null $thumbUrl): static 48 | { 49 | $this->thumbUrl = $thumbUrl; 50 | 51 | return $this; 52 | } 53 | 54 | public function thumbWidth(int|null $thumbWidth): static 55 | { 56 | $this->thumbWidth = $thumbWidth; 57 | 58 | return $this; 59 | } 60 | 61 | public function thumbHeight(int|null $thumbHeight): static 62 | { 63 | $this->thumbHeight = $thumbHeight; 64 | 65 | return $this; 66 | } 67 | 68 | public function hideUrl(bool|null $hideUrl): static 69 | { 70 | $this->hideUrl = $hideUrl; 71 | 72 | return $this; 73 | } 74 | 75 | public function html(): static 76 | { 77 | $this->parseMode = Telegraph::PARSE_HTML; 78 | 79 | return $this; 80 | } 81 | 82 | public function markdown(): static 83 | { 84 | $this->parseMode = Telegraph::PARSE_MARKDOWN; 85 | 86 | return $this; 87 | } 88 | 89 | public function markdownV2(): static 90 | { 91 | $this->parseMode = Telegraph::PARSE_MARKDOWNV2; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * @inheritDoc 98 | */ 99 | public function data(): array 100 | { 101 | $data = [ 102 | 'title' => $this->title, 103 | 'url' => $this->url, 104 | 'hide_url' => $this->hideUrl, 105 | 'description' => $this->description, 106 | 'thumb_url' => $this->thumbUrl, 107 | 'thumb_width' => $this->thumbWidth, 108 | 'thumb_height' => $this->thumbHeight, 109 | ]; 110 | 111 | if ($this->message !== null) { 112 | $data['input_message_content'] = [ 113 | 'message_text' => $this->message, 114 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 115 | ]; 116 | } 117 | 118 | return array_filter($data, fn ($value) => $value !== null); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/DTO/ChatMemberUpdate.php: -------------------------------------------------------------------------------- 1 | > 13 | */ 14 | class ChatMemberUpdate implements Arrayable 15 | { 16 | private CarbonInterface $date; 17 | 18 | private Chat $chat; 19 | private User $from; 20 | private ChatMember $previous; 21 | private ChatMember $new; 22 | private ?ChatInviteLink $inviteLink = null; 23 | private ?bool $viaJoinRequest = null; 24 | private ?bool $viaChatFolderInviteLink = null; 25 | 26 | private function __construct() 27 | { 28 | } 29 | 30 | /** 31 | * @param array{ 32 | * date:int, 33 | * chat:array, 34 | * from:array, 35 | * old_chat_member:array, 36 | * new_chat_member:array, 37 | * invite_link?:array, 38 | * via_join_request?:bool, 39 | * via_chat_folder_invite_link?:bool 40 | * } $data 41 | */ 42 | public static function fromArray(array $data): ChatMemberUpdate 43 | { 44 | $chatMemberUpdate = new self(); 45 | 46 | $chatMemberUpdate->date = Carbon::createFromTimestamp($data['date']); 47 | $chatMemberUpdate->chat = Chat::fromArray($data['chat']); 48 | $chatMemberUpdate->from = User::fromArray($data['from']); 49 | $chatMemberUpdate->previous = ChatMember::fromArray($data['old_chat_member']); 50 | $chatMemberUpdate->new = ChatMember::fromArray($data['new_chat_member']); 51 | 52 | if (isset($data['invite_link'])) { 53 | $chatMemberUpdate->inviteLink = ChatInviteLink::fromArray($data['invite_link']); 54 | } 55 | 56 | $chatMemberUpdate->viaJoinRequest = $data['via_join_request'] ?? null; 57 | $chatMemberUpdate->viaChatFolderInviteLink = $data['via_chat_folder_invite_link'] ?? null; 58 | 59 | return $chatMemberUpdate; 60 | } 61 | 62 | public function date(): CarbonInterface 63 | { 64 | return $this->date; 65 | } 66 | 67 | public function chat(): Chat 68 | { 69 | return $this->chat; 70 | } 71 | 72 | public function from(): User 73 | { 74 | return $this->from; 75 | } 76 | 77 | public function previous(): ChatMember 78 | { 79 | return $this->previous; 80 | } 81 | 82 | public function new(): ChatMember 83 | { 84 | return $this->new; 85 | } 86 | 87 | public function inviteLink(): ?ChatInviteLink 88 | { 89 | return $this->inviteLink; 90 | } 91 | 92 | public function viaJoinRequest(): ?bool 93 | { 94 | return $this->viaJoinRequest; 95 | } 96 | 97 | public function viaChatFolderInviteLink(): ?bool 98 | { 99 | return $this->viaChatFolderInviteLink; 100 | } 101 | 102 | public function toArray(): array 103 | { 104 | return array_filter([ 105 | 'chat' => $this->chat->toArray(), 106 | 'from' => $this->from->toArray(), 107 | 'date' => $this->date->toISOString(), 108 | 'previous' => $this->previous->toArray(), 109 | 'new' => $this->new->toArray(), 110 | 'invite_link' => $this->inviteLink?->toArray(), 111 | 'via_join_request' => $this->viaJoinRequest, 112 | 'via_chat_folder_invite_link' => $this->viaChatFolderInviteLink, 113 | ], fn ($value) => $value !== null); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/ScopedPayloads/TelegraphEditMediaPayload.php: -------------------------------------------------------------------------------- 1 | attachPhoto($telegraph, $path, $filename); 18 | 19 | $data = [ 20 | 'type' => 'photo', 21 | 'media' => $telegraph->files->has('photo') 22 | ? "attach://photo" 23 | : $telegraph->data['photo'], 24 | ]; 25 | 26 | 27 | $telegraph->data['media'] = json_encode($data); 28 | 29 | return $telegraph; 30 | } 31 | 32 | public function document(string $path, string|null $filename = null): self 33 | { 34 | $telegraph = clone $this; 35 | 36 | $this->attachDocument($telegraph, $path, $filename); 37 | 38 | $data = [ 39 | 'type' => 'document', 40 | 'media' => $telegraph->files->has('document') 41 | ? "attach://document" 42 | : $telegraph->data['document'], 43 | ]; 44 | 45 | 46 | $telegraph->data['media'] = json_encode($data); 47 | 48 | return $telegraph; 49 | } 50 | 51 | public function animation(string $path, string|null $filename = null): self 52 | { 53 | $telegraph = clone $this; 54 | 55 | $this->attachAnimation($telegraph, $path, $filename); 56 | 57 | $data = [ 58 | 'type' => 'animation', 59 | 'media' => $telegraph->files->has('animation') 60 | ? "attach://animation" 61 | : $telegraph->data['animation'], 62 | ]; 63 | 64 | 65 | $telegraph->data['media'] = json_encode($data); 66 | 67 | return $telegraph; 68 | } 69 | 70 | public function video(string $path, string|null $filename = null): self 71 | { 72 | $telegraph = clone $this; 73 | 74 | $this->attachVideo($telegraph, $path, $filename); 75 | 76 | $data = [ 77 | 'type' => 'video', 78 | 'media' => $telegraph->files->has('video') 79 | ? "attach://video" 80 | : $telegraph->data['video'], 81 | ]; 82 | 83 | 84 | $telegraph->data['media'] = json_encode($data); 85 | 86 | return $telegraph; 87 | } 88 | 89 | public function videoNote(string $path, string|null $filename = null): self 90 | { 91 | $telegraph = clone $this; 92 | 93 | $this->attachVideoNote($telegraph, $path, $filename); 94 | 95 | $data = [ 96 | 'type' => 'video_note', 97 | 'media' => $telegraph->files->has('video_note') 98 | ? "attach://video_note" 99 | : $telegraph->data['video_note'], 100 | ]; 101 | 102 | 103 | $telegraph->data['media'] = json_encode($data); 104 | 105 | return $telegraph; 106 | } 107 | 108 | public function audio(string $path, string|null $filename = null): self 109 | { 110 | $telegraph = clone $this; 111 | 112 | $this->attachAudio($telegraph, $path, $filename); 113 | 114 | $data = [ 115 | 'type' => 'audio', 116 | 'media' => $telegraph->files->has('audio') 117 | ? "attach://audio" 118 | : $telegraph->data['audio'], 119 | ]; 120 | 121 | 122 | $telegraph->data['media'] = json_encode($data); 123 | 124 | return $telegraph; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/DTO/InlineQueryResultVenue.php: -------------------------------------------------------------------------------- 1 | id = $id; 30 | $result->title = $title; 31 | $result->latitude = $latitude; 32 | $result->longitude = $longitude; 33 | $result->address = $address; 34 | $result->message = $message; 35 | 36 | return $result; 37 | } 38 | 39 | public function thumbUrl(string|null $thumbUrl): static 40 | { 41 | $this->thumbUrl = $thumbUrl; 42 | 43 | return $this; 44 | } 45 | 46 | public function thumbWidth(int|null $thumbWidth): static 47 | { 48 | $this->thumbWidth = $thumbWidth; 49 | 50 | return $this; 51 | } 52 | 53 | public function thumbHeight(int|null $thumbHeight): static 54 | { 55 | $this->thumbHeight = $thumbHeight; 56 | 57 | return $this; 58 | } 59 | 60 | public function foursquareId(string|null $foursquareId): static 61 | { 62 | $this->foursquareId = $foursquareId; 63 | 64 | return $this; 65 | } 66 | 67 | public function foursquareType(string|null $foursquareType): static 68 | { 69 | $this->foursquareType = $foursquareType; 70 | 71 | return $this; 72 | } 73 | 74 | public function googlePlaceId(string|null $googlePlaceId): static 75 | { 76 | $this->googlePlaceId = $googlePlaceId; 77 | 78 | return $this; 79 | } 80 | 81 | public function googlePlaceType(string|null $googlePlaceType): static 82 | { 83 | $this->googlePlaceType = $googlePlaceType; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * @inheritDoc 90 | */ 91 | public function data(): array 92 | { 93 | $data = [ 94 | 'title' => $this->title, 95 | 'latitude' => $this->latitude, 96 | 'longitude' => $this->longitude, 97 | 'address' => $this->address, 98 | 'foursquare_id' => $this->foursquareId, 99 | 'foursquare_type' => $this->foursquareType, 100 | 'google_place_id' => $this->googlePlaceId, 101 | 'google_place_type' => $this->googlePlaceType, 102 | 'thumb_url' => $this->thumbUrl, 103 | 'thumb_width' => $this->thumbWidth, 104 | 'thumb_height' => $this->thumbHeight, 105 | ]; 106 | 107 | if ($this->message !== null) { 108 | $data['input_message_content'] = [ 109 | 'message_text' => $this->message, 110 | 'parse_mode' => $this->parseMode ?? config('telegraph.default_parse_mode', Telegraph::PARSE_HTML), 111 | ]; 112 | } 113 | 114 | return array_filter($data, fn ($value) => $value !== null); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/DTO/Poll.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Poll implements Arrayable 12 | { 13 | private string $id; 14 | private string $question; 15 | /** @var Collection|null */ 16 | private ?Collection $questionEntities = null; 17 | /** @var Collection */ 18 | private Collection $options; 19 | private int $totalVoterCount; 20 | private bool $isClosed; 21 | private bool $isAnonymous; 22 | private string $type; 23 | private bool $allowsMultipleAnswers; 24 | 25 | private function __construct() 26 | { 27 | } 28 | 29 | /** 30 | * @param array{id:string, question:string,question_entities?: array , options:array, total_voter_count:int, is_closed:bool, is_anonymous:bool, type:string, allows_multiple_answers:bool} $data 31 | */ 32 | public static function fromArray(array $data): Poll 33 | { 34 | $poll = new self(); 35 | 36 | $poll->id = $data['id']; 37 | $poll->question = $data['question']; 38 | 39 | if (!empty($data['question_entities'])) { 40 | /* @phpstan-ignore-next-line */ 41 | $poll->questionEntities = collect($data['question_entities'])->map(fn (array $entity) => Entity::fromArray($entity)); 42 | } 43 | 44 | /* @phpstan-ignore-next-line */ 45 | $poll->options = collect($data['options'])->map(fn (array $option) => PollOption::fromArray($option)); 46 | 47 | $poll->totalVoterCount = $data['total_voter_count']; 48 | $poll->isClosed = $data['is_closed']; 49 | $poll->isAnonymous = $data['is_anonymous']; 50 | $poll->type = $data['type']; 51 | $poll->allowsMultipleAnswers = $data['allows_multiple_answers']; 52 | 53 | return $poll; 54 | } 55 | 56 | public function id(): string 57 | { 58 | return $this->id; 59 | } 60 | 61 | public function question(): string 62 | { 63 | return $this->question; 64 | } 65 | 66 | /** 67 | * @return Collection|null 68 | */ 69 | public function questionEntities(): ?Collection 70 | { 71 | return $this->questionEntities; 72 | } 73 | 74 | /** 75 | * @return Collection 76 | */ 77 | public function options(): Collection 78 | { 79 | return $this->options; 80 | } 81 | 82 | public function totalVoterCount(): int 83 | { 84 | return $this->totalVoterCount; 85 | } 86 | 87 | public function isClosed(): bool 88 | { 89 | return $this->isClosed; 90 | } 91 | 92 | public function isAnonymous(): bool 93 | { 94 | return $this->isAnonymous; 95 | } 96 | 97 | public function type(): string 98 | { 99 | return $this->type; 100 | } 101 | 102 | public function allowsMultipleAnswers(): bool 103 | { 104 | return $this->allowsMultipleAnswers; 105 | } 106 | 107 | public function toArray(): array 108 | { 109 | return array_filter([ 110 | 'id' => $this->id, 111 | 'question' => $this->question, 112 | 'question_entities' => $this->questionEntities?->toArray(), 113 | 'options' => $this->options->toArray(), 114 | 'total_voter_count' => $this->totalVoterCount, 115 | 'is_closed' => $this->isClosed, 116 | 'is_anonymous' => $this->isAnonymous, 117 | 'type' => $this->type, 118 | 'allows_multiple_answers' => $this->allowsMultipleAnswers, 119 | 120 | ], fn ($value) => $value !== null); 121 | } 122 | } 123 | --------------------------------------------------------------------------------