diff --git a/src/TgBotLib/Abstracts/Method.php b/src/TgBotLib/Abstracts/Method.php index d8a78c1..5040739 100644 --- a/src/TgBotLib/Abstracts/Method.php +++ b/src/TgBotLib/Abstracts/Method.php @@ -86,6 +86,39 @@ return $curl; } + /** + * Builds a cURL handle for a multi-part file upload request to a bot API. + * + * @param Bot $bot The bot instance to configure the request for. + * @param string $method The API method to call. + * @param array $files An associative array of parameter names and file paths to upload. + * @param array $parameters An associative array of additional parameters for the request. + * @return CurlHandle The configured cURL handle. + */ + protected static function buildMultiUpload(Bot $bot, string $method, array $files, array $parameters): CurlHandle + { + $curl = curl_init(sprintf('%s/bot%s/%s', $bot->getEndpoint(), $bot->getToken(), $method)); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data']); + + $postFields = []; + + foreach ($files as $param => $file) + { + $postFields[$param] = new CURLFile($file); + } + + foreach ($parameters as $key => $value) + { + $postFields[$key] = $value; + } + + curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields); + + return $curl; + } + /** * Executes a cURL request and processes the response. * diff --git a/src/TgBotLib/Bot.php b/src/TgBotLib/Bot.php index fd22d13..855d745 100644 --- a/src/TgBotLib/Bot.php +++ b/src/TgBotLib/Bot.php @@ -36,6 +36,7 @@ * @method MessageId copyMessage(string|int $chat_id, string|int $from_chat_id, int $message_id, ?int $message_thread_id=null, ?string $caption=null, ?string|ParseMode $parse_mode=null, ?MessageEntity[] $caption_entities=null, ?bool $show_caption_above_media=null, ?bool $disable_notification=null, ?bool $protect_content=null, ?ReplyParameters $reply_parameters=null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $reply_markup=null) Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessage, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success. * @method MessageId[] copyMessages(string|int $chat_id, string|int $from_chat_id, int[] $message_ids, ?int $message_thread_id=null, ?bool $disable_notification=null, ?bool $protect_content=null, ?bool $remove_caption=null) Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessages, but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. On success, an array of MessageId of the sent messages is returned. * @method Message sendPhoto(string|int $chat_id, string $photo, ?string $business_connection_id=null, ?int $message_thread_id=null, ?string $caption=null, ?string|ParseMode $parse_mode=null, ?MessageEntity[] $caption_entities=null, ?bool $show_caption_above_media=null, ?bool $disable_notification=null, ?bool $protect_content=null, ?string $message_effect_id=null, ?ReplyParameters $reply_parameters=null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $reply_markup=null) Use this method to send photos. On success, the sent Message is returned. + * @method Message sendAudio(string|int $chat_id, string $audio, ?string $business_connection_id=null, ?int $message_thread_id=null, ?string $caption=null, ?string|ParseMode $parse_mode=null, ?MessageEntity[] $caption_entities=null, ?int $duration=null, ?string $performer=null, ?string $title=null, ?string $thumb=null, ?bool $disable_notification=null, ?bool $protect_content=null, ?string $message_effect_id=null, ?ReplyParameters $reply_parameters=null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $reply_markup=null) Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future. */ class Bot { diff --git a/src/TgBotLib/Enums/Methods.php b/src/TgBotLib/Enums/Methods.php index 1122cad..6c26495 100644 --- a/src/TgBotLib/Enums/Methods.php +++ b/src/TgBotLib/Enums/Methods.php @@ -12,6 +12,7 @@ use TgBotLib\Methods\ForwardMessages; use TgBotLib\Methods\GetMe; use TgBotLib\Methods\Logout; + use TgBotLib\Methods\SendAudio; use TgBotLib\Methods\SendMessage; use TgBotLib\Methods\SendPhoto; @@ -26,6 +27,7 @@ case COPY_MESSAGE = 'copyMessage'; case COPY_MESSAGES = 'copyMessages'; case SEND_PHOTO = 'sendPhoto'; + case SEND_AUDIO = 'sendAudio'; /** * Executes a command on the provided bot with the given parameters. @@ -48,6 +50,7 @@ self::COPY_MESSAGE => CopyMessage::execute($bot, $parameters), self::COPY_MESSAGES => CopyMessages::execute($bot, $parameters), self::SEND_PHOTO => SendPhoto::execute($bot, $parameters), + self::SEND_AUDIO => SendAudio::execute($bot, $parameters), }; } } diff --git a/src/TgBotLib/Methods/SendAudio.php b/src/TgBotLib/Methods/SendAudio.php new file mode 100644 index 0000000..0bd77d4 --- /dev/null +++ b/src/TgBotLib/Methods/SendAudio.php @@ -0,0 +1,136 @@ +value; + } + + if(isset($parameters['caption_entities']) && is_array($parameters['caption_entities'])) + { + $entities = []; + foreach($parameters['caption_entities'] as $entity) + { + if($entity instanceof MessageEntity) + { + $entities[] = $entity->toArray(); + } + else + { + $entities[] = $entity; + } + } + $parameters['caption_entities'] = $entities; + } + + if(isset($parameters['reply_parameters']) && $parameters['reply_parameters'] instanceof ReplyParameters) + { + $parameters['reply_parameters'] = $parameters['reply_parameters']->toArray(); + } + + if(isset($parameters['reply_markup']) && $parameters['reply_markup'] instanceof ObjectTypeInterface) + { + $parameters['reply_markup'] = $parameters['reply_markup']->toArray(); + } + + // Handle file uploads + $hasLocalAudio = isset($parameters['audio']) && is_string($parameters['audio']) && file_exists($parameters['audio']) && is_file($parameters['audio']); + $hasLocalThumb = isset($parameters['thumbnail']) && is_string($parameters['thumbnail']) && file_exists($parameters['thumbnail']) && is_file($parameters['thumbnail']); + + if ($hasLocalAudio || $hasLocalThumb) + { + $files = []; + + if ($hasLocalAudio) + { + $files['audio'] = $parameters['audio']; + unset($parameters['audio']); + } + + if ($hasLocalThumb) + { + $files['thumbnail'] = $parameters['thumbnail']; + unset($parameters['thumbnail']); + } + + if (count($files) > 1) + { + // Multiple files to upload + $curl = self::buildMultiUpload($bot, Methods::SEND_AUDIO->value, $files, $parameters); + } + else + { + // Single file to upload + $fileParam = array_key_first($files); + $curl = self::buildUpload($bot, Methods::SEND_AUDIO->value, $fileParam, $files[$fileParam], $parameters); + } + + return Message::fromArray(self::executeCurl($curl)); + } + + // If no local files to upload, use regular POST method + return Message::fromArray(self::executeCurl(self::buildPost($bot, Methods::SEND_AUDIO->value, $parameters))); + } + + /** + * @inheritDoc + */ + public static function getRequiredParameters(): ?array + { + return [ + 'chat_id', + 'audio' + ]; + } + + /** + * @inheritDoc + */ + public static function getOptionalParameters(): ?array + { + return [ + 'business_connection_id', + 'message_thread_id', + 'caption', + 'parse_mode', + 'caption_entities', + 'duration', + 'performer', + 'title', + 'thumbnail', + 'disable_notification', + 'protect_content', + 'message_effect_id', + 'reply_parameters', + 'reply_markup' + ]; + } + } \ No newline at end of file diff --git a/tests/TgBotLib/Methods/SendAudioTest.php b/tests/TgBotLib/Methods/SendAudioTest.php new file mode 100644 index 0000000..c8f8d1b --- /dev/null +++ b/tests/TgBotLib/Methods/SendAudioTest.php @@ -0,0 +1,41 @@ +sendAudio( + chat_id: TEST_CHAT_ID, + audio: self::TEST_AUDIO_PATH, + caption: 'Test Unit: testSendAudio', + ); + + $this->assertInstanceOf(Message::class, $result); + $this->assertEquals('Test Unit: testSendAudio', $result->getCaption()); + } + +} diff --git a/tests/TgBotLib/Methods/sample/ketchup.mp3 b/tests/TgBotLib/Methods/sample/ketchup.mp3 new file mode 100644 index 0000000..19a5f3d Binary files /dev/null and b/tests/TgBotLib/Methods/sample/ketchup.mp3 differ