botToken = $botToken; } /** * Set default channels for posting * * @param array $channels Array of channel usernames or IDs */ public function setDefaultChannels($channels) { $this->defaultChannels = $channels; } /** * Make API request to Telegram * * @param string $method API method * @param array $params Parameters * @return array Response */ private function request($method, $params = []) { $url = $this->baseUrl . $this->botToken . '/' . $method; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $params, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60, CURLOPT_SSL_VERIFYPEER => true, ]); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { throw new RuntimeException("Telegram API error: {$error}"); } $data = json_decode($response, true); if (!$data['ok']) { throw new RuntimeException("Telegram API error: " . (isset($data['description']) ? $data['description'] : 'Unknown error')); } return isset($data['result']) ? $data['result'] : []; } /** * Send a text message * * @param string $chatId Chat/Channel ID or username * @param string $text Message text * @param string $parseMode Parse mode (HTML, Markdown, MarkdownV2) * @param bool $disablePreview Disable link preview * @return array Message info */ public function sendMessage($chatId, $text, $parseMode = 'HTML', $disablePreview = false) { return $this->request('sendMessage', [ 'chat_id' => $chatId, 'text' => $text, 'parse_mode' => $parseMode, 'disable_web_page_preview' => $disablePreview, ]); } /** * Send a single photo * * @param string $chatId Chat/Channel ID * @param string $photo Photo URL or file_id * @param string $caption Photo caption * @param string $parseMode Parse mode for caption * @return array Message info */ public function sendPhoto($chatId, $photo, $caption = '', $parseMode = 'HTML') { $params = [ 'chat_id' => $chatId, 'photo' => $photo, ]; if ($caption) { $params['caption'] = $caption; $params['parse_mode'] = $parseMode; } return $this->request('sendPhoto', $params); } /** * Send multiple photos as media group (album) * * @param string $chatId Chat/Channel ID * @param array $photos Array of photo URLs * @param string $caption Caption for first photo * @param string $parseMode Parse mode * @return array Messages info */ public function sendMediaGroup($chatId, $photos, $caption = '', $parseMode = 'HTML') { $media = []; foreach ($photos as $index => $photo) { $item = [ 'type' => 'photo', 'media' => $photo, ]; // Caption only on first photo if ($index === 0 && $caption) { $item['caption'] = $caption; $item['parse_mode'] = $parseMode; } $media[] = $item; } return $this->request('sendMediaGroup', [ 'chat_id' => $chatId, 'media' => json_encode($media), ]); } /** * Post photos with text to a channel * Smart method that chooses best approach based on content * * @param string $chatId Chat/Channel ID * @param array $photos Array of photo URLs * @param string $text Post text * @param string $parseMode Parse mode * @return array Result info */ public function post($chatId, $photos, $text = '', $parseMode = 'HTML') { $photoCount = count($photos); // Text only if ($photoCount === 0) { return ['type' => 'text', 'message' => $this->sendMessage($chatId, $text, $parseMode)]; } // Single photo if ($photoCount === 1) { return ['type' => 'photo', 'message' => $this->sendPhoto($chatId, $photos[0], $text, $parseMode)]; } // Multiple photos (2-10) - use media group if ($photoCount <= 10) { return ['type' => 'album', 'messages' => $this->sendMediaGroup($chatId, $photos, $text, $parseMode)]; } // More than 10 photos - split into multiple albums $results = []; $chunks = array_chunk($photos, 10); foreach ($chunks as $index => $chunk) { $caption = ($index === 0) ? $text : ''; $results[] = $this->sendMediaGroup($chatId, $chunk, $caption, $parseMode); } return ['type' => 'multiple_albums', 'messages' => $results]; } /** * Post to multiple channels at once * * @param array $chatIds Array of Chat/Channel IDs * @param array $photos Array of photo URLs * @param string $text Post text * @param string $parseMode Parse mode * @return array Results for each channel */ public function postToMultiple($chatIds, $photos, $text = '', $parseMode = 'HTML') { $results = []; foreach ($chatIds as $chatId) { try { $results[$chatId] = [ 'success' => true, 'result' => $this->post($chatId, $photos, $text, $parseMode), ]; } catch (Exception $e) { $results[$chatId] = [ 'success' => false, 'error' => $e->getMessage(), ]; } } return $results; } /** * Get bot info * * @return array Bot info */ public function getMe() { return $this->request('getMe'); } /** * Get chat info * * @param string $chatId Chat/Channel ID * @return array Chat info */ public function getChat($chatId) { return $this->request('getChat', ['chat_id' => $chatId]); } /** * Validate that bot has access to a channel * * @param string $chatId Channel ID or username * @return array Validation result */ public function validateChannel($chatId) { try { $chat = $this->getChat($chatId); return [ 'valid' => true, 'title' => isset($chat['title']) ? $chat['title'] : (isset($chat['username']) ? $chat['username'] : $chatId), 'type' => $chat['type'], ]; } catch (Exception $e) { return [ 'valid' => false, 'error' => $e->getMessage(), ]; } } /** * Format text for Telegram HTML mode * * @param string $text Plain text * @return string Escaped HTML */ public static function escapeHtml($text) { return htmlspecialchars($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); } /** * Format text for Telegram MarkdownV2 mode * * @param string $text Plain text * @return string Escaped Markdown */ public static function escapeMarkdown($text) { $chars = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!']; foreach ($chars as $char) { $text = str_replace($char, '\\' . $char, $text); } return $text; } }