Add Telegram bot integration for real-time alert notifications
- Implemented ITelegramNotificationService and TelegramNotificationService for sending alerts via Telegram. - Updated MonitoringSettings to include Telegram configuration options. - Enhanced AlertService to send alerts and resolutions through Telegram. - Added API endpoints for checking Telegram status and sending test alerts. - Updated README and TELEGRAM_SETUP.md with setup instructions and features. - Included example configuration in appsettings.telegram.example.json.
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ResourceMonitorService.Configuration;
|
||||
using ResourceMonitorService.Models;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Exceptions;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
|
||||
namespace ResourceMonitorService.Services
|
||||
{
|
||||
public interface ITelegramNotificationService
|
||||
{
|
||||
Task SendAlertAsync(Alert alert);
|
||||
Task SendAlertResolvedAsync(Alert alert);
|
||||
Task<bool> IsEnabledAsync();
|
||||
Task<bool> TestConnectionAsync();
|
||||
}
|
||||
|
||||
public class TelegramNotificationService : ITelegramNotificationService
|
||||
{
|
||||
private readonly ILogger<TelegramNotificationService> _logger;
|
||||
private readonly TelegramSettings _telegramSettings;
|
||||
private readonly ITelegramBotClient? _botClient;
|
||||
|
||||
public TelegramNotificationService(
|
||||
ILogger<TelegramNotificationService> logger,
|
||||
IOptions<MonitoringSettings> settings)
|
||||
{
|
||||
_logger = logger;
|
||||
_telegramSettings = settings.Value.Telegram;
|
||||
|
||||
if (_telegramSettings.IsEnabled && !string.IsNullOrEmpty(_telegramSettings.BotToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
_botClient = new TelegramBotClient(_telegramSettings.BotToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to initialize Telegram bot client");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> IsEnabledAsync()
|
||||
{
|
||||
return await Task.FromResult(_telegramSettings.IsEnabled && _botClient != null);
|
||||
}
|
||||
|
||||
public async Task<bool> TestConnectionAsync()
|
||||
{
|
||||
if (_botClient == null || !_telegramSettings.IsEnabled)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var me = await _botClient.GetMe();
|
||||
_logger.LogInformation("Telegram bot connected successfully: @{Username}", me.Username);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to connect to Telegram bot");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendAlertAsync(Alert alert)
|
||||
{
|
||||
if (_botClient == null || !_telegramSettings.IsEnabled)
|
||||
return;
|
||||
|
||||
// Check if we should send this type of alert
|
||||
if ((alert.Level == "Warning" && !_telegramSettings.SendWarningAlerts) ||
|
||||
(alert.Level == "Critical" && !_telegramSettings.SendCriticalAlerts))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = FormatAlertMessage(alert, _telegramSettings.MessageTemplate);
|
||||
|
||||
foreach (var chatId in _telegramSettings.ChatIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
text: message,
|
||||
parseMode: ParseMode.Markdown,
|
||||
disableNotification: alert.Level == "Warning" // Don't ping for warnings
|
||||
);
|
||||
|
||||
_logger.LogInformation("Telegram alert sent to chat {ChatId}: {AlertLevel} - {Component}",
|
||||
chatId, alert.Level, alert.Component);
|
||||
}
|
||||
catch (ApiRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send Telegram alert to chat {ChatId}: {ErrorCode} - {Description}",
|
||||
chatId, ex.ErrorCode, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error sending Telegram alert to chat {ChatId}", chatId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendAlertResolvedAsync(Alert alert)
|
||||
{
|
||||
if (_botClient == null || !_telegramSettings.IsEnabled || !_telegramSettings.SendResolutionNotifications)
|
||||
return;
|
||||
|
||||
var message = FormatAlertMessage(alert, _telegramSettings.ResolutionTemplate);
|
||||
|
||||
foreach (var chatId in _telegramSettings.ChatIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
text: message,
|
||||
parseMode: ParseMode.Markdown,
|
||||
disableNotification: true // Don't ping for resolutions
|
||||
);
|
||||
|
||||
_logger.LogInformation("Telegram resolution notification sent to chat {ChatId}: {Component}",
|
||||
chatId, alert.Component);
|
||||
}
|
||||
catch (ApiRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send Telegram resolution to chat {ChatId}: {ErrorCode} - {Description}",
|
||||
chatId, ex.ErrorCode, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error sending Telegram resolution to chat {ChatId}", chatId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatAlertMessage(Alert alert, string template)
|
||||
{
|
||||
var levelIcon = alert.Level switch
|
||||
{
|
||||
"Critical" => "🔴",
|
||||
"Warning" => "⚠️",
|
||||
_ => "ℹ️"
|
||||
};
|
||||
|
||||
var componentIcon = alert.Component switch
|
||||
{
|
||||
"CPU" => "🖥️",
|
||||
"CPUTemp" => "🌡️",
|
||||
"Memory" => "💾",
|
||||
"GPU" => "🎮",
|
||||
"GPUTemp" => "🌡️",
|
||||
var disk when disk.StartsWith("Disk") => "💽",
|
||||
var process when process.StartsWith("ProcessMemory") => "⚙️",
|
||||
_ => "📊"
|
||||
};
|
||||
|
||||
// Replace template placeholders
|
||||
var message = template
|
||||
.Replace("{Level}", alert.Level)
|
||||
.Replace("{Component}", alert.Component)
|
||||
.Replace("{Message}", EscapeMarkdown(alert.Message))
|
||||
.Replace("{Timestamp}", alert.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"))
|
||||
.Replace("{CurrentValue}", alert.CurrentValue.ToString("F1"))
|
||||
.Replace("{ThresholdValue}", alert.ThresholdValue.ToString("F1"));
|
||||
|
||||
if (alert.ResolvedAt.HasValue)
|
||||
{
|
||||
message = message.Replace("{ResolvedAt}", alert.ResolvedAt.Value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
// Add icons
|
||||
message = $"{levelIcon} {componentIcon} {message}";
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private static string EscapeMarkdown(string text)
|
||||
{
|
||||
// Escape special Markdown characters
|
||||
return text
|
||||
.Replace("_", "\\_")
|
||||
.Replace("*", "\\*")
|
||||
.Replace("[", "\\[")
|
||||
.Replace("]", "\\]")
|
||||
.Replace("(", "\\(")
|
||||
.Replace(")", "\\)")
|
||||
.Replace("~", "\\~")
|
||||
.Replace("`", "\\`")
|
||||
.Replace(">", "\\>")
|
||||
.Replace("#", "\\#")
|
||||
.Replace("+", "\\+")
|
||||
.Replace("-", "\\-")
|
||||
.Replace("=", "\\=")
|
||||
.Replace("|", "\\|")
|
||||
.Replace("{", "\\{")
|
||||
.Replace("}", "\\}")
|
||||
.Replace(".", "\\.")
|
||||
.Replace("!", "\\!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user