pdo = $connection->getPdo(); $this->loadSettings(); } /** * Load settings from database settings table. */ public function loadSettings(): void { $this->botToken = $this->getSetting('telegram_bot_token'); $this->chatId = $this->getSetting('telegram_chat_id'); } /** * Helper to retrieve setting by key. */ private function getSetting(string $key): ?string { try { $stmt = $this->pdo->prepare("SELECT `value` FROM settings WHERE `key` = ?"); $stmt->execute([$key]); $val = $stmt->fetchColumn(); return $val !== false ? $val : null; } catch (Throwable $e) { return null; } } /** * Configure manually (for testing unsaved parameters). */ public function configure(string $botToken, string $chatId): void { $this->botToken = $botToken; $this->chatId = $chatId; } /** * Retrieve the last HTTP or API error message. */ public function getLastError(): ?string { return $this->lastError; } /** * Send a notification to Telegram. */ public function send(string $message, string $type = 'info'): bool { $this->lastError = null; if (!$this->botToken || !$this->chatId) { $this->lastError = "Telegram token or chat ID is missing."; return false; } $icons = [ 'info' => "\xF0\x9F\x93\xA1", // 📡 'success' => "\xE2\x9C\x85", // ✅ 'warning' => "\xE2\x9A\xA0\xEF\xB8\x8F", // ⚠️ 'error' => "\xE2\x9D\x8C", // ❌ 'opportunity' => "\xF0\x9F\x92\xA1", // 💡 'funding' => "\xF0\x9F\x92\xB0", // 💰 ]; $icon = $icons[$type] ?? $icons['info']; $fullMessage = "{$icon} *ScoutIQ*\n\n{$message}"; try { $url = "https://api.telegram.org/bot{$this->botToken}/sendMessage"; $payload = json_encode([ 'chat_id' => $this->chatId, 'text' => $fullMessage, 'parse_mode' => 'Markdown', 'disable_web_page_preview' => true, ]); if (function_exists('curl_init')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response = curl_exec($ch); if ($response === false) { $this->lastError = "Connection error: " . curl_error($ch); curl_close($ch); return false; } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { $resDecoded = json_decode($response, true); $this->lastError = $resDecoded['description'] ?? "API error HTTP {$httpCode}"; error_log("Telegram API returned code {$httpCode}: {$response}"); return false; } return true; } else { $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\n", 'content' => $payload, 'timeout' => 10, ], 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], ]); $response = @file_get_contents($url, false, $context); if ($response === false) { $error = error_get_last(); $this->lastError = $error['message'] ?? "Connection timed out."; return false; } $resDecoded = json_decode($response, true); if (($resDecoded['ok'] ?? false) === false) { $this->lastError = $resDecoded['description'] ?? "API error"; return false; } return true; } } catch (Throwable $e) { $this->lastError = $e->getMessage(); return false; } } /** * Notify about a new opportunity. */ public function notifyNewOpportunity(array $opportunity): void { $title = $opportunity['title'] ?? 'Untitled'; $fitScore = $opportunity['fit_score'] ?? 0; $url = $opportunity['url'] ?? ''; $orgName = $opportunity['org_name'] ?? ''; $whyItMatters = $opportunity['why_it_matters'] ?? []; $requirements = $opportunity['requirements'] ?? []; $deadline = $opportunity['deadline'] ?? null; $effort = $opportunity['effort'] ?? 'متوسط'; $potentialReturn = $opportunity['potential_return'] ?? 'متوسط'; $effortEmoji = (str_contains($effort, 'عالي') || str_contains(strtolower($effort), 'high')) ? '🔴' : ((str_contains($effort, 'متوسط') || str_contains(strtolower($effort), 'medium')) ? '🟡' : '🟢'); $returnEmoji = (str_contains($potentialReturn, 'عالي') || str_contains(strtolower($potentialReturn), 'high')) ? '🟢' : ((str_contains($potentialReturn, 'متوسط') || str_contains(strtolower($potentialReturn), 'medium')) ? '🟡' : '🔴'); $message = "💡 *ScoutIQ*\n\n"; $message .= "*{$title}*"; if ($orgName) { $message .= " – {$orgName}"; } $message .= "\n\n🔥 *Match Score:* {$fitScore}/100\n"; if (!empty($whyItMatters)) { $message .= "\n*لماذا تهمك؟*\n"; foreach ((array)$whyItMatters as $why) { $message .= "• {$why}\n"; } } if (!empty($requirements)) { $message .= "\n*المتطلبات:*\n"; foreach ((array)$requirements as $req) { $message .= "• {$req}\n"; } } if ($deadline) { $message .= "\n*الموعد النهائي:*\n{$deadline}\n"; } $message .= "\n*الجهد المطلوب:*\n{$effortEmoji} {$effort}\n"; $message .= "\n*العائد المحتمل:*\n{$returnEmoji} {$potentialReturn}\n"; $message .= "\n*الإجراء:*\n"; if ($url) { $message .= "[قدم الآن]({$url})"; } else { $message .= "لا يوجد رابط متاح"; } $this->send($message, 'opportunity'); } /** * Notify about collector results. */ public function notifyCollectorResults(array $results): void { $message = "*Data Collection Complete*\n\n"; $message .= "Sources: {$results['processed']}/{$results['total_sources']}\n"; $message .= "New Opportunities: {$results['new_opportunities']}\n"; $message .= "New Organizations: {$results['new_organizations']}\n"; $message .= "Errors: {$results['errors']}"; $this->send($message, 'success'); } /** * Send test message. */ public function sendTest(): bool { return $this->send("Test notification from ScoutIQ. Your Telegram integration is working correctly!", 'success'); } }