Files
scoutiq/app/Services/Notification/TelegramNotifier.php
2026-06-06 12:04:41 +03:00

234 lines
7.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services\Notification;
use App\Services\Database\Connection;
use PDO;
use Throwable;
class TelegramNotifier
{
private PDO $pdo;
private ?string $botToken = null;
private ?string $chatId = null;
private ?string $lastError = null;
public function __construct(Connection $connection)
{
$this->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');
}
}