token = $_ENV['TELEGRAM_BOT_TOKEN'] ?? getenv('TELEGRAM_BOT_TOKEN'); } public function sendMessage(int $chatId, string $text, ?array $keyboard = null, ?string $parseMode = 'HTML'): array { $data = [ 'chat_id' => $chatId, 'text' => $text, 'parse_mode' => $parseMode ]; if ($keyboard) { $data['reply_markup'] = json_encode($keyboard); } return $this->request('sendMessage', $data); } public function editMessageText(int $chatId, int $messageId, string $text, ?array $keyboard = null, ?string $parseMode = 'HTML'): array { $data = [ 'chat_id' => $chatId, 'message_id' => $messageId, 'text' => $text, 'parse_mode' => $parseMode ]; if ($keyboard) { $data['reply_markup'] = json_encode($keyboard); } return $this->request('editMessageText', $data); } public function sendPhoto(int $chatId, string $photo, ?string $caption = null, ?array $keyboard = null): array { $data = [ 'chat_id' => $chatId, 'photo' => $photo ]; if ($caption) { $data['caption'] = $caption; $data['parse_mode'] = 'HTML'; } if ($keyboard) { $data['reply_markup'] = json_encode($keyboard); } return $this->request('sendPhoto', $data); } public function sendMediaGroup(int $chatId, array $photos, ?string $caption = null): array { $media = []; foreach ($photos as $index => $photo) { $media[] = [ 'type' => 'photo', 'media' => $photo, 'caption' => $index === 0 ? $caption : null, 'parse_mode' => 'HTML' ]; } $data = [ 'chat_id' => $chatId, 'media' => json_encode($media) ]; return $this->request('sendMediaGroup', $data); } public function deleteMessage(int $chatId, int $messageId): array { return $this->request('deleteMessage', [ 'chat_id' => $chatId, 'message_id' => $messageId ]); } public function answerCallbackQuery(string $callbackQueryId, string $text, ?bool $showAlert = false) { return $this->request('answerCallbackQuery', [ 'callback_query_id' => $callbackQueryId, 'text' => $text, 'show_alert' => $showAlert ]); } public function getChat(int $chatId): array { return $this->request('getChat', ['chat_id' => $chatId]); } public function getChatAdministrators(int $chatId): array { return $this->request('getChatAdministrators', ['chat_id' => $chatId]); } public function leaveChat(int $chatId): array { return $this->request('leaveChat', ['chat_id' => $chatId]); } public function setChatMenuButton(?int $chatId = null, ?array $menuButton = null): array { $data = []; if ($chatId !== null) { $data['chat_id'] = $chatId; } if ($menuButton !== null) { $data['menu_button'] = json_encode($menuButton); } return $this->request('setChatMenuButton', $data); } public function createInlineKeyboard(array $buttons): array { $keyboard = ['inline_keyboard' => []]; foreach ($buttons as $button) { $buttonData = ['text' => $button['text']]; if (isset($button['callback_data'])) { $buttonData['callback_data'] = $button['callback_data']; } elseif (isset($button['url'])) { $buttonData['url'] = $button['url']; } $keyboard['inline_keyboard'][] = [$buttonData]; } return $keyboard; } public function createReplyKeyboard(array $buttons, ?bool $resize = true, ?bool $oneTime = false): array { $keyboard = [ 'keyboard' => [], 'resize_keyboard' => $resize, 'one_time_keyboard' => $oneTime ]; foreach ($buttons as $row) { $rowButtons = []; foreach ($row as $button) { $rowButtons[] = [ 'text' => $button['text'], 'request_contact' => $button['request_contact'] ?? false, 'request_location' => $button['request_location'] ?? false ]; } $keyboard['keyboard'][] = $rowButtons; } return $keyboard; } public function removeKeyboard(): array { return ['remove_keyboard' => true]; } private function request(string $method, array $data) { $url = $this->baseUrl . $this->token . '/' . $method; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // answerCallbackQuery returns true (boolean) on success if ($response === 'true') { return true; } $result = json_decode($response, true); if (!$result || !($result['ok'] ?? false)) { throw new \Exception("Telegram API Error: " . ($result['description'] ?? 'Unknown error')); } return $result['result']; } /** * Parsear HTML y dividirlo en segmentos manteniendo el orden * Retorna array de ['type' => 'text|image', 'content' => '...', 'src' => '...'] */ public function parseContent(string $html): array { $segments = []; // Usar regex para encontrar todas las etiquetas $pattern = '/]+src=["\']([^"\']+)["\'][^>]*>/i'; $parts = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE); // El array parts alterna entre: [texto, src_imagen, texto, src_imagen, texto...] for ($i = 0; $i < count($parts); $i++) { if ($i % 2 === 0) { // Es texto $text = $this->htmlToPlainText($parts[$i]); if (!empty(trim($text))) { $segments[] = [ 'type' => 'text', 'content' => $text ]; } } else { // Es una imagen (el src capturado) $segments[] = [ 'type' => 'image', 'src' => $parts[$i], 'content' => '' ]; } } return $segments; } /** * Enviar contenido con texto e imágenes en el orden correcto */ public function sendContentWithOrderedImages(int $chatId, array $segments): void { foreach ($segments as $segment) { if ($segment['type'] === 'text') { // Enviar texto if (!empty(trim($segment['content']))) { $this->sendMessage($chatId, $segment['content']); } } elseif ($segment['type'] === 'image') { $imagePath = $segment['src']; if (file_exists($imagePath)) { // Es un archivo local $this->sendPhoto($chatId, $imagePath); } elseif (strpos($imagePath, 'http') === 0) { // Es una URL remota $this->sendPhoto($chatId, $imagePath); } } } } /** * Convertir HTML a texto plano manteniendo saltos de línea */ private function htmlToPlainText(string $html): string { // Reemplazar
,

, etc. con saltos de línea $text = preg_replace('//i', "\n", $html); $text = preg_replace('/<\/p>/i', "\n", $text); $text = preg_replace('/]*>/i', '', $text); $text = preg_replace('/]*>/i', '', $text); $text = preg_replace('/<\/div>/i', "\n", $text); // Eliminar otras etiquetas HTML $text = strip_tags($text); // Decodificar entidades HTML $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); // Limpiar espacios múltiples y saltos de línea $text = preg_replace('/\n{3,}/', "\n\n", $text); $text = preg_replace('/[ \t]+/', ' ', $text); return trim($text); } public function extractImages(string $html): array { preg_match_all('/]+src=["\']([^"\']+)["\'][^>]*>/i', $html, $matches); return $matches[1] ?? []; } public function removeImages(string $html): string { return preg_replace('/]+>/i', '', $html); } public function splitMessage(string $content, int $maxLength = 4096): array { if (strlen($content) <= $maxLength) { return [$content]; } $parts = []; $lines = explode("\n", $content); $currentPart = ''; foreach ($lines as $line) { if (strlen($currentPart . "\n" . $line) > $maxLength) { if (!empty($currentPart)) { $parts[] = $currentPart; $currentPart = ''; } if (strlen($line) > $maxLength) { $chunks = str_split($line, $maxLength); $parts = array_merge($parts, array_slice($chunks, 0, -1)); $currentPart = end($chunks); } else { $currentPart = $line; } } else { $currentPart .= (empty($currentPart) ? '' : "\n") . $line; } } if (!empty($currentPart)) { $parts[] = $currentPart; } return $parts; } }