Files
scoutiq/app/Services/Notification/TelegramNotifier.php
2026-06-05 16:08:53 +03:00

198 lines
6.4 KiB
PHP

<?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';
$type = $opportunity['type'] ?? 'other';
$score = $opportunity['score'] ?? 0;
$url = $opportunity['url'] ?? '';
$desc = mb_substr($opportunity['description'] ?? '', 0, 200);
$orgName = $opportunity['org_name'] ?? '';
$message = "*New Opportunity:* {$title}\n";
$message .= "*Type:* {$type} | *Score:* {$score}/100\n";
if ($orgName) $message .= "*Organization:* {$orgName}\n";
if ($desc) $message .= "_{$desc}_\n";
if ($url) $message .= "\n[Open Link]({$url})";
$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');
}
}