Initial commit - Last War messaging system
This commit is contained in:
338
telegram/TelegramSender.php
Executable file
338
telegram/TelegramSender.php
Executable file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
namespace Telegram;
|
||||
|
||||
class TelegramSender
|
||||
{
|
||||
private string $token;
|
||||
private string $baseUrl = 'https://api.telegram.org/bot';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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 <img>
|
||||
$pattern = '/<img[^>]+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 <br>, <p>, etc. con saltos de línea
|
||||
$text = preg_replace('/<br\s*\/?>/i', "\n", $html);
|
||||
$text = preg_replace('/<\/p>/i', "\n", $text);
|
||||
$text = preg_replace('/<p[^>]*>/i', '', $text);
|
||||
$text = preg_replace('/<div[^>]*>/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('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $html, $matches);
|
||||
return $matches[1] ?? [];
|
||||
}
|
||||
|
||||
public function removeImages(string $html): string
|
||||
{
|
||||
return preg_replace('/<img[^>]+>/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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user