@@ -84,6 +84,11 @@ class Router
|
||||
$path === '/config/allowed_tokens' && $method === 'PUT'
|
||||
=> $this->updateAllowedTokens(),
|
||||
|
||||
$path === '/config/telegram' && $method === 'GET'
|
||||
=> $this->getTelegramConfig(),
|
||||
$path === '/config/telegram' && $method === 'PUT'
|
||||
=> $this->updateTelegramConfig(),
|
||||
|
||||
default => throw new \RuntimeException('Not found', 404),
|
||||
};
|
||||
|
||||
@@ -271,4 +276,20 @@ class Router
|
||||
|
||||
return ['status' => 'ingested', 'line' => substr($line, 0, 100)];
|
||||
}
|
||||
|
||||
private function getTelegramConfig(): array
|
||||
{
|
||||
return [
|
||||
'bot_token' => $this->repo->getConfig('telegram_bot_token', ''),
|
||||
'chat_id' => $this->repo->getConfig('telegram_chat_id', ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function updateTelegramConfig(): array
|
||||
{
|
||||
$body = json_decode(file_get_contents('php://input'), true);
|
||||
$this->repo->setConfig('telegram_bot_token', $body['bot_token'] ?? '');
|
||||
$this->repo->setConfig('telegram_chat_id', $body['chat_id'] ?? '');
|
||||
return $this->getTelegramConfig();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Jakach\Logging\Notifier;
|
||||
|
||||
use Jakach\Logging\Model\Alert;
|
||||
use Jakach\Logging\Storage\Repository;
|
||||
|
||||
class TelegramNotifier
|
||||
{
|
||||
private ?string $botToken;
|
||||
private ?string $chatId;
|
||||
|
||||
public function __construct(
|
||||
private Repository $repo,
|
||||
) {
|
||||
$this->botToken = $this->repo->getConfig('telegram_bot_token');
|
||||
$this->chatId = $this->repo->getConfig('telegram_chat_id');
|
||||
}
|
||||
|
||||
public function isConfigured(): bool
|
||||
{
|
||||
return !empty($this->botToken) && !empty($this->chatId);
|
||||
}
|
||||
|
||||
public function send(Alert $alert): bool
|
||||
{
|
||||
if (!$this->isConfigured()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$emoji = match ($alert->severity->value) {
|
||||
'emergency', 'critical_high', 'critical' => "\xF0\x9F\x94\xA5",
|
||||
'critical_low', 'error' => "\xE2\x9D\x8C",
|
||||
'warning_high', 'warning' => "\xE2\x9A\xA0\xEF\xB8\x8F",
|
||||
'warning_low' => "\xF0\x9F\x93\xA2",
|
||||
default => "\xE2\x84\xB9\xEF\xB8\x8F",
|
||||
};
|
||||
|
||||
$text = sprintf(
|
||||
"%s *ALERT #%d* [%s]\n",
|
||||
$emoji,
|
||||
$alert->id,
|
||||
strtoupper($alert->severity->value)
|
||||
);
|
||||
$text .= sprintf("*Rule:* %s\n", $alert->ruleName);
|
||||
$text .= sprintf("*Source:* %s\n", $alert->sourceName ?? '—');
|
||||
$text .= sprintf("*Message:* ```\n%s\n```", mb_substr($alert->message, 0, 1000));
|
||||
|
||||
$url = 'https://api.telegram.org/bot' . $this->botToken . '/sendMessage';
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
'chat_id' => $this->chatId,
|
||||
'text' => $text,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => true,
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
fprintf(STDERR, "Telegram send failed (HTTP %d): %s\n", $httpCode, $response);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -239,12 +239,25 @@ class Repository
|
||||
}
|
||||
|
||||
public function setAllowedUserTokens(array $tokens): void
|
||||
{
|
||||
$this->setConfig('allowed_user_tokens', json_encode(array_values($tokens)));
|
||||
}
|
||||
|
||||
public function getConfig(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$stmt = $this->db->pdo()->prepare("SELECT value FROM config WHERE key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ? $row['value'] : $default;
|
||||
}
|
||||
|
||||
public function setConfig(string $key, string $value): void
|
||||
{
|
||||
$stmt = $this->db->pdo()->prepare(
|
||||
"INSERT INTO config (key, value) VALUES (?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
||||
);
|
||||
$stmt->execute(['allowed_user_tokens', json_encode(array_values($tokens))]);
|
||||
$stmt->execute([$key, $value]);
|
||||
}
|
||||
|
||||
// --- Rate Limiting ---
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Jakach\Logging\Worker;
|
||||
|
||||
use Jakach\Logging\Model\{LogSource, Alert};
|
||||
use Jakach\Logging\Notifier\TelegramNotifier;
|
||||
use Jakach\Logging\RuleEngine\Engine;
|
||||
use Jakach\Logging\Storage\Repository;
|
||||
|
||||
@@ -12,6 +13,7 @@ class Orchestrator
|
||||
private SocketListener $socketListener;
|
||||
private Repository $repo;
|
||||
private Engine $engine;
|
||||
private TelegramNotifier $telegram;
|
||||
private array $sourceMap = [];
|
||||
private bool $running = true;
|
||||
|
||||
@@ -19,6 +21,7 @@ class Orchestrator
|
||||
{
|
||||
$this->repo = $repo;
|
||||
$this->engine = $engine;
|
||||
$this->telegram = new TelegramNotifier($repo);
|
||||
$this->socketListener = new SocketListener(function (string $line, int $sourceId) {
|
||||
$this->handleLine($line, $sourceId);
|
||||
});
|
||||
@@ -31,6 +34,10 @@ class Orchestrator
|
||||
{
|
||||
$this->loadSources();
|
||||
|
||||
if ($this->telegram->isConfigured()) {
|
||||
fprintf(STDERR, "Telegram notifier configured\n");
|
||||
}
|
||||
|
||||
pcntl_signal(SIGTERM, function () { $this->running = false; });
|
||||
pcntl_signal(SIGINT, function () { $this->running = false; });
|
||||
|
||||
@@ -74,6 +81,8 @@ class Orchestrator
|
||||
);
|
||||
fprintf(STDERR, "%s\n", $msg);
|
||||
echo $msg . "\n";
|
||||
|
||||
$this->telegram->send($alert);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user