TelegramService.php

8.28 KB
08/07/2025 10:44
PHP
TelegramService.php
<?php

namespace App\Services;

// Load Telegram config if not already loaded by an autoloader or entry script
if (!defined('TELEGRAM_BOT_TOKEN')) {
    // Adjust path as necessary depending on how this service is included/loaded
    $telegramConfigPath = __DIR__.'/../../config/telegram_config.php';
    if (file_exists($telegramConfigPath)) {
        require_once $telegramConfigPath;
    } else {
        // Fallback or error if config is essential and not found
        // For now, we'll define a dummy one if not found to prevent immediate script halt
        // In a real app, this should be a fatal error or proper handling.
        if (!defined('TELEGRAM_BOT_TOKEN')) {
            define('TELEGRAM_BOT_TOKEN', 'YOUR_TELEGRAM_BOT_TOKEN_PLACEHOLDER');
        }

        error_log("Warning: telegram_config.php not found at ".$telegramConfigPath.". Using placeholder token.");
    }
}

class TelegramService
{
    private string $apiToken;
    private string $apiUrlBase;

    /**
     * @param string $apiToken
     */
    public function __construct(?string $apiToken = null)
    {
        $this->apiToken = $apiToken ?: (defined('TELEGRAM_BOT_TOKEN') ? TELEGRAM_BOT_TOKEN : '');
        if (empty($this->apiToken) || $this->apiToken === 'YOUR_TELEGRAM_BOT_TOKEN' || $this->apiToken === 'YOUR_TELEGRAM_BOT_TOKEN_PLACEHOLDER') {
            // Log or throw an exception if the token is missing or still a placeholder
            error_log("Telegram API Token is missing or is a placeholder. Please configure it in config/telegram_config.php");
            // Depending on strictness, you might throw an exception here:
            // throw new \Exception("Telegram API Token is not configured.");
        }
        $this->apiUrlBase = "https://api.telegram.org/bot{$this->apiToken}/";
    }

    /**
     * Sends a GET request to the Telegram API.
     * @param string $method Telegram API method name (e.g., "getMe").
     * @param array $params Optional parameters for the method.
     * @return array|false Decoded JSON response as an associative array, or false on failure.
     */
    private function apiRequest(string $method, array $params = [], string $requestType = 'POST')
    {
        if (empty($this->apiToken) || $this->apiToken === 'YOUR_TELEGRAM_BOT_TOKEN' || $this->apiToken === 'YOUR_TELEGRAM_BOT_TOKEN_PLACEHOLDER') {
            error_log("Cannot make API request: Telegram API Token is not properly configured.");
            return false;
        }

        $url = $this->apiUrlBase.$method;

        $ch = curl_init();

        if ($requestType === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            // Send data as JSON payload
            $jsonData = json_encode($params);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                'Content-Length: '.strlen($jsonData)
            ]);
        } else {
            // GET request
            if (!empty($params)) {
                $url .= "?".http_build_query($params);
            }
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10 seconds timeout
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // Should be true in production

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        if ($curlError) {
            error_log("Telegram API cURL Error for method {$method}: {$curlError}");
            return false;
        }

        if ($httpCode !== 200) {
            error_log("Telegram API HTTP Error for method {$method}: Code {$httpCode}. Response: {$response}");
            // Attempt to decode response anyway, it might contain error details from Telegram
            $decodedResponse = json_decode($response, true);
            return $decodedResponse ?: false; // Return decoded error or false
        }

        $decodedResponse = json_decode($response, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            error_log("Telegram API JSON Decode Error for method {$method}: ".json_last_error_msg().". Response: {$response}");
            return false;
        }

        return $decodedResponse;
    }

    /**
     * A simple method to test connection, like "getMe".
     * @return array|false Bot information or false on failure.
     */
    public function getMe()
    {
        return $this->apiRequest('getMe', [], 'GET');
    }

    /**
     * Sends a text message to a specific chat.
     * @param int $chatId Target chat_id.
     * @param string $text Text of the message to be sent.
     * @param ?array $replyMarkup Optional. Inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
     * @param ?string $parseMode Optional. Mode for parsing entities in the message text. See formatting options for more details. (e.g., MarkdownV2, HTML)
     * @return array|false API response or false on failure.
     */
    public function sendMessage(int $chatId, string $text, ?array $replyMarkup = null, ?string $parseMode = null)
    {
        $params = [
            'chat_id' => $chatId,
            'text' => $text
        ];
        if ($replyMarkup !== null) {
            $params['reply_markup'] = $replyMarkup;
        }
        if ($parseMode !== null) {
            $params['parse_mode'] = $parseMode;
        }
        return $this->apiRequest('sendMessage', $params);
    }

    /**
     * Edits text of a message sent by the bot.
     * @param int $chatId Target chat_id where the message to edit is located.
     * @param int $messageId Identifier of the message to edit.
     * @param string $text New text of the message.
     * @param ?array $replyMarkup Optional. Inline keyboard.
     * @param ?string $parseMode Optional. Mode for parsing entities in the message text (e.g., MarkdownV2, HTML).
     * @return array|false API response or false on failure.
     */
    public function editMessageText(int $chatId, int $messageId, string $text, ?array $replyMarkup = null, ?string $parseMode = null)
    {
        $params = [
            'chat_id' => $chatId,
            'message_id' => $messageId,
            'text' => $text
        ];
        if ($replyMarkup !== null) {
            $params['reply_markup'] = $replyMarkup;
        }
        if ($parseMode !== null) {
            $params['parse_mode'] = $parseMode;
        }
        return $this->apiRequest('editMessageText', $params);
    }

    /**
     * Sets the webhook for the bot.
     * @param string $webhookUrl HTTPS URL to send updates to.
     * @param ?array $options Optional parameters like certificate, allowed_updates etc.
     * @return array|false API response or false on failure.
     */
    public function setWebhook(string $webhookUrl, ?array $options = [])
    {
        $params = array_merge(['url' => $webhookUrl], $options);
        return $this->apiRequest('setWebhook', $params);
    }

    /**
     * Deletes the currently set webhook.
     * @return array|false API response or false on failure.
     */
    public function deleteWebhook()
    {
        return $this->apiRequest('deleteWebhook');
    }

    /**
     * Gets the current webhook information.
     * @return array|false API response or false on failure.
     */
    public function getWebhookInfo()
    {
        return $this->apiRequest('getWebhookInfo', [], 'GET');
    }

    /**
     * Answers callback query to remove the loading state from inline keyboard buttons.
     * @param string $callbackQueryId Unique identifier for the callback query to be answered.
     * @param ?string $text Optional. Text of the notification (max 200 characters).
     * @param ?bool $showAlert Optional. If true, an alert will be shown instead of a notification.
     * @return array|false API response or false on failure.
     */
    public function answerCallbackQuery(string $callbackQueryId, ?string $text = null, ?bool $showAlert = null)
    {
        $params = [
            'callback_query_id' => $callbackQueryId
        ];
        if ($text !== null) {
            $params['text'] = $text;
        }
        if ($showAlert !== null) {
            $params['show_alert'] = $showAlert;
        }
        return $this->apiRequest('answerCallbackQuery', $params);
    }
}