feature/telegram-alert #4

Merged
king merged 2 commits from feature/telegram-alert into master 2025-08-07 17:54:33 +08:00
11 changed files with 610 additions and 4 deletions
Showing only changes of commit d6efa9163b - Show all commits
+14
View File
@@ -38,6 +38,8 @@ namespace ResourceMonitorService.Configuration
new() { Component = "CPUTemp", WarningThreshold = 75, CriticalThreshold = 85, DurationSeconds = 60 },
new() { Component = "GPUTemp", WarningThreshold = 80, CriticalThreshold = 90, DurationSeconds = 60 }
};
public TelegramSettings Telegram { get; set; } = new();
}
public class AlertThresholdConfig
@@ -73,4 +75,16 @@ namespace ResourceMonitorService.Configuration
public bool EnableConsoleLogging { get; set; } = true;
public bool EnablePerformanceLogging { get; set; } = false;
}
public class TelegramSettings
{
public bool IsEnabled { get; set; } = false;
public string BotToken { get; set; } = string.Empty;
public List<long> ChatIds { get; set; } = new();
public bool SendWarningAlerts { get; set; } = true;
public bool SendCriticalAlerts { get; set; } = true;
public bool SendResolutionNotifications { get; set; } = true;
public string MessageTemplate { get; set; } = "🚨 *{Level} Alert*\n\n📊 *{Component}*\n💬 {Message}\n⏰ {Timestamp:yyyy-MM-dd HH:mm:ss}";
public string ResolutionTemplate { get; set; } = "✅ *Alert Resolved*\n\n📊 *{Component}*\n💬 {Message}\n⏰ Resolved at {ResolvedAt:yyyy-MM-dd HH:mm:ss}";
}
}
+1
View File
@@ -51,6 +51,7 @@ namespace ResourceMonitorService
services.AddSingleton<ISystemInfoService, SystemInfoService>();
services.AddSingleton<IResourceMonitorService, Services.ResourceMonitorService>();
services.AddSingleton<IGameDetectionService, GameDetectionService>();
services.AddSingleton<ITelegramNotificationService, TelegramNotificationService>();
services.AddSingleton<IAlertService, AlertService>();
// Register the main worker service
+39
View File
@@ -22,6 +22,7 @@ A comprehensive system monitoring service specifically designed for Windows VMs
- **Game Detection**: Multi-platform game detection with fullscreen monitoring and configurable root folders
- **Process Management**: View top processes with CPU/memory percentages, terminate processes via API
- **Smart Alerting**: Duration-based alerting to prevent false positives
- **Telegram Bot Integration**: Real-time alerts via Telegram bot with customizable notifications
- **System Control**: Remote shutdown/restart capabilities
- **Health Monitoring**: Comprehensive health checks and uptime tracking
- **Real-time Metrics**: CPU usage calculation and memory percentage tracking for processes
@@ -57,6 +58,10 @@ The service runs on `http://localhost:5000` by default and provides the followin
- `POST /api/alerts/{alertId}/resolve` - Manually resolve an alert
- `GET /api/alerts/enabled` - Check if alerting is enabled
### Telegram Bot Integration
- `GET /api/telegram/status` - Check Telegram bot status and connection
- `POST /api/telegram/test` - Send a test alert to verify bot configuration
### System Control
- `POST /api/process/kill` - Terminate a process (requires process ID and optional force flag)
- `POST /api/system/shutdown` - Shutdown, restart, or cancel system operations
@@ -180,6 +185,40 @@ D:\Games\The Witcher 3\witcher3.exe
- Use absolute paths (e.g., `C:\Games`, not `Games`)
- Root folders are checked in order, so prioritize most common locations first
### Telegram Bot Alerts
The service supports real-time alert notifications via Telegram bot. To set up Telegram alerts:
1. **Create a Telegram Bot** - Contact `@BotFather` on Telegram and create a new bot
2. **Get Chat ID** - Send a message to your bot, then visit `https://api.telegram.org/bot<TOKEN>/getUpdates`
3. **Configure Settings** - Add Telegram configuration to your `appsettings.json`:
```json
{
"MonitoringSettings": {
"Telegram": {
"IsEnabled": true,
"BotToken": "123456789:ABCdefGHIjklMNOpqrSTUvwxyz",
"ChatIds": [123456789, -987654321],
"SendWarningAlerts": true,
"SendCriticalAlerts": true,
"SendResolutionNotifications": true,
"MessageTemplate": "🚨 *{Level} Alert*\n\n📊 *{Component}*\n💬 {Message}\n⏰ {Timestamp}",
"ResolutionTemplate": "✅ *Alert Resolved*\n\n📊 *{Component}*\n💬 {Message}\n⏰ Resolved at {ResolvedAt}"
}
}
}
```
**Features:**
- **Multiple Chats**: Send alerts to multiple users/groups by adding chat IDs
- **Customizable Templates**: Modify message format with placeholders for alert data
- **Alert Filtering**: Choose which alert levels to send (Warning/Critical)
- **Silent Notifications**: Warning alerts are sent silently, critical alerts with sound
- **Resolution Notifications**: Optional notifications when alerts are resolved
📋 For detailed setup instructions, see [TELEGRAM_SETUP.md](TELEGRAM_SETUP.md)
## 📊 Example API Responses
### Health Check
+1
View File
@@ -17,5 +17,6 @@
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Telegram.Bot" Version="22.6.0" />
</ItemGroup>
</Project>
+29 -1
View File
@@ -21,6 +21,7 @@ namespace ResourceMonitorService.Services
{
private readonly ILogger<AlertService> _logger;
private readonly MonitoringSettings _settings;
private readonly ITelegramNotificationService _telegramService;
private readonly ConcurrentDictionary<string, Alert> _activeAlerts;
private readonly ConcurrentQueue<Alert> _alertHistory;
private readonly Dictionary<string, DateTime> _lastAlertTime;
@@ -29,10 +30,11 @@ namespace ResourceMonitorService.Services
public event EventHandler<Alert>? AlertTriggered;
public event EventHandler<Alert>? AlertResolved;
public AlertService(ILogger<AlertService> logger, IOptions<MonitoringSettings> settings)
public AlertService(ILogger<AlertService> logger, IOptions<MonitoringSettings> settings, ITelegramNotificationService telegramService)
{
_logger = logger;
_settings = settings.Value;
_telegramService = telegramService;
_activeAlerts = new ConcurrentDictionary<string, Alert>();
_alertHistory = new ConcurrentQueue<Alert>();
_lastAlertTime = new Dictionary<string, DateTime>();
@@ -183,6 +185,19 @@ namespace ResourceMonitorService.Services
_logger.LogWarning("Alert triggered: {Message}", alert.Message);
AlertTriggered?.Invoke(this, alert);
// Send Telegram notification
_ = Task.Run(async () =>
{
try
{
await _telegramService.SendAlertAsync(alert);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send Telegram alert notification");
}
});
}
else
{
@@ -264,6 +279,19 @@ namespace ResourceMonitorService.Services
_logger.LogInformation("Alert resolved: {Message}", resolvedAlert.Message);
AlertResolved?.Invoke(this, resolvedAlert);
// Send Telegram resolution notification
_ = Task.Run(async () =>
{
try
{
await _telegramService.SendAlertResolvedAsync(resolvedAlert);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send Telegram resolution notification");
}
});
}
}
}
+5 -2
View File
@@ -154,8 +154,11 @@ namespace ResourceMonitorService.Services
return false;
// Get screen dimensions
var screenWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
var screenHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
var primaryScreen = System.Windows.Forms.Screen.PrimaryScreen;
if (primaryScreen == null)
return false;
var screenWidth = primaryScreen.Bounds.Width;
var screenHeight = primaryScreen.Bounds.Height;
// Check if window covers the entire screen
var windowWidth = rect.Right - rect.Left;
+206
View File
@@ -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("!", "\\!");
}
}
}
+196
View File
@@ -0,0 +1,196 @@
# Telegram Bot Alert Setup Guide
The Resource Monitor Service supports sending alerts via Telegram bot. This allows you to receive real-time notifications about system resource warnings and critical alerts directly to your Telegram chat.
## Prerequisites
1. **Create a Telegram Bot**:
- Open Telegram and search for `@BotFather`
- Send `/newbot` command
- Follow the instructions to create a new bot
- Save the Bot Token (format: `123456789:ABCdefGHIjklMNOpqrSTUvwxyz`)
2. **Get Your Chat ID**:
- Send a message to your bot
- Visit: `https://api.telegram.org/bot<YourBOTToken>/getUpdates`
- Find your chat ID in the response (it's a number, can be negative for groups)
## Configuration
### 1. Edit appsettings.json
Add or update the Telegram configuration in your `appsettings.json`:
```json
{
"MonitoringSettings": {
// ... other settings ...
"Telegram": {
"IsEnabled": true,
"BotToken": "123456789:ABCdefGHIjklMNOpqrSTUvwxyz",
"ChatIds": [12345678, -987654321],
"SendWarningAlerts": true,
"SendCriticalAlerts": true,
"SendResolutionNotifications": true,
"MessageTemplate": "🚨 *{Level} Alert*\n\n📊 *{Component}*\n💬 {Message}\n⏰ {Timestamp}",
"ResolutionTemplate": "✅ *Alert Resolved*\n\n📊 *{Component}*\n💬 {Message}\n⏰ Resolved at {ResolvedAt}"
}
}
}
```
### 2. Configuration Options
| Setting | Description | Default |
|---------|-------------|---------|
| `IsEnabled` | Enable/disable Telegram notifications | `false` |
| `BotToken` | Your Telegram bot token from BotFather | `""` |
| `ChatIds` | Array of chat IDs to send alerts to | `[]` |
| `SendWarningAlerts` | Send warning level alerts | `true` |
| `SendCriticalAlerts` | Send critical level alerts | `true` |
| `SendResolutionNotifications` | Send alert resolution notifications | `true` |
| `MessageTemplate` | Template for alert messages | See above |
| `ResolutionTemplate` | Template for resolution messages | See above |
### 3. Message Templates
Templates support the following placeholders:
- `{Level}` - Alert level (Warning, Critical)
- `{Component}` - Component name (CPU, Memory, GPU, etc.)
- `{Message}` - Full alert message
- `{Timestamp}` - Alert timestamp
- `{CurrentValue}` - Current resource value
- `{ThresholdValue}` - Threshold that was exceeded
- `{ResolvedAt}` - Resolution timestamp (resolution template only)
## API Endpoints
### Check Telegram Status
```
GET /api/telegram/status
```
Returns the current status of Telegram integration:
```json
{
"enabled": true,
"connected": true
}
```
### Send Test Alert
```
POST /api/telegram/test
```
Sends a test alert to verify the Telegram bot is working correctly.
## Features
### Alert Types
The bot sends different types of alerts:
1. **Warning Alerts** ⚠️
- Sent when thresholds are exceeded but not critical
- Notifications are silent (no sound/vibration)
2. **Critical Alerts** 🔴
- Sent when critical thresholds are exceeded
- Normal notifications (with sound/vibration)
3. **Resolution Notifications**
- Sent when alerts are resolved
- Always silent notifications
### Icons and Formatting
The bot automatically adds relevant icons:
- 🖥️ CPU usage
- 🌡️ Temperature alerts
- 💾 Memory usage
- 🎮 GPU usage
- 💽 Disk usage
- ⚙️ Process alerts
Messages are formatted using Telegram's Markdown formatting for better readability.
### Multiple Chat Support
You can send alerts to multiple chats:
- Personal chats
- Group chats
- Channels (if bot is admin)
Just add multiple chat IDs to the `ChatIds` array.
## Troubleshooting
### Common Issues
1. **Bot not responding**:
- Verify bot token is correct
- Ensure bot is not blocked
- Check `/api/telegram/status` endpoint
2. **Messages not received**:
- Verify chat ID is correct
- Ensure you've sent at least one message to the bot
- Check bot has permission to send messages
3. **Connection errors**:
- Check internet connectivity
- Verify Telegram API is accessible
- Check firewall settings
### Testing
1. Set `IsEnabled: true` in configuration
2. Restart the service
3. Call `POST /api/telegram/test` to send a test message
4. Check if the message is received in Telegram
### Logs
Monitor the service logs for Telegram-related errors:
```
grep -i telegram logs/resourcemonitor-*.txt
```
## Security Considerations
1. **Keep bot token secure** - Never commit it to version control
2. **Use environment variables** for sensitive configuration in production
3. **Limit chat IDs** to trusted users/groups only
4. **Regular token rotation** if compromised
## Example Alert Messages
### Warning Alert
```
⚠️ 🖥️ 🚨 Warning Alert
📊 CPU
💬 CPU Usage is warning: 85.2% (threshold: 80.0%)
⏰ 2025-08-07 14:30:15
```
### Critical Alert
```
🔴 💾 🚨 Critical Alert
📊 Memory
💬 Memory Usage is critical: 96.8% (threshold: 95.0%)
⏰ 2025-08-07 14:35:22
```
### Resolution
```
✅ 🖥️ Alert Resolved
📊 CPU
💬 CPU Usage is warning: 85.2% (threshold: 80.0%)
⏰ Resolved at 2025-08-07 14:32:45
```
+34
View File
@@ -20,6 +20,7 @@ namespace ResourceMonitorService
private readonly IResourceMonitorService _resourceMonitorService;
private readonly IGameDetectionService _gameDetectionService;
private readonly IAlertService _alertService;
private readonly ITelegramNotificationService _telegramService;
private readonly ApiSettings _apiSettings;
private readonly MonitoringSettings _monitoringSettings;
@@ -30,6 +31,7 @@ namespace ResourceMonitorService
IResourceMonitorService resourceMonitorService,
IGameDetectionService gameDetectionService,
IAlertService alertService,
ITelegramNotificationService telegramService,
IOptions<ApiSettings> apiSettings,
IOptions<MonitoringSettings> monitoringSettings)
{
@@ -39,6 +41,7 @@ namespace ResourceMonitorService
_resourceMonitorService = resourceMonitorService;
_gameDetectionService = gameDetectionService;
_alertService = alertService;
_telegramService = telegramService;
_apiSettings = apiSettings.Value;
_monitoringSettings = monitoringSettings.Value;
}
@@ -148,6 +151,37 @@ namespace ResourceMonitorService
app.MapGet($"{basePath}/alerts/enabled", async () =>
Results.Ok(new { Enabled = await _alertService.IsAlertingEnabledAsync() }));
// Telegram endpoints
app.MapGet($"{basePath}/telegram/status", async () =>
Results.Ok(new {
Enabled = await _telegramService.IsEnabledAsync(),
Connected = await _telegramService.TestConnectionAsync()
}));
app.MapPost($"{basePath}/telegram/test", async () =>
{
try
{
var testAlert = new Alert
{
Timestamp = DateTime.Now,
Component = "Test",
Level = "Warning",
Message = "This is a test alert from Resource Monitor Service",
CurrentValue = 100,
ThresholdValue = 90,
IsResolved = false
};
await _telegramService.SendAlertAsync(testAlert);
return Results.Ok(new { Message = "Test alert sent successfully" });
}
catch (Exception ex)
{
return Results.Problem($"Failed to send test alert: {ex.Message}");
}
});
// Process management endpoints (enhanced)
app.MapPost($"{basePath}/process/kill", async (HttpContext context) =>
{
+11 -1
View File
@@ -88,7 +88,17 @@
"DurationSeconds": 60,
"IsEnabled": true
}
]
],
"Telegram": {
"IsEnabled": true,
"BotToken": "7705627522:AAHDTVMF1uPJW7qm-Di0g_BmefAVWdOrS2U",
"ChatIds": [398126624],
"SendWarningAlerts": true,
"SendCriticalAlerts": true,
"SendResolutionNotifications": true,
"MessageTemplate": "🚨 *{Level} Alert*\n\n📊 *{Component}*\n💬 {Message}\n⏰ {Timestamp}",
"ResolutionTemplate": "✅ *Alert Resolved*\n\n📊 *{Component}*\n💬 {Message}\n⏰ Resolved at {ResolvedAt}"
}
},
"LoggingSettings": {
"LogLevel": "Information",
+74
View File
@@ -0,0 +1,74 @@
{
"MonitoringSettings": {
"UpdateIntervalMs": 5000,
"DataRetentionDays": 7,
"EnableGpuMonitoring": true,
"EnableDiskMonitoring": true,
"EnableNetworkMonitoring": true,
"EnableTemperatureMonitoring": true,
"EnableProcessMonitoring": true,
"EnableGameDetection": true,
"EnableAlerts": true,
"MaxProcessesToTrack": 10,
"MaxHistoryPoints": 1000,
"GamePlatformPaths": [
"\\steamapps\\common\\",
"\\Epic Games\\",
"\\GOG Galaxy\\Games\\",
"\\Origin Games\\",
"\\Ubisoft Game Launcher\\games\\"
],
"GameRootFolders": [
"C:\\Games",
"D:\\Games",
"E:\\Games"
],
"AlertThresholds": [
{
"Component": "CPU",
"WarningThreshold": 80,
"CriticalThreshold": 95,
"DurationSeconds": 30,
"IsEnabled": true
},
{
"Component": "Memory",
"WarningThreshold": 85,
"CriticalThreshold": 95,
"DurationSeconds": 30,
"IsEnabled": true
},
{
"Component": "GPU",
"WarningThreshold": 85,
"CriticalThreshold": 95,
"DurationSeconds": 30,
"IsEnabled": true
},
{
"Component": "CPUTemp",
"WarningThreshold": 75,
"CriticalThreshold": 85,
"DurationSeconds": 60,
"IsEnabled": true
},
{
"Component": "GPUTemp",
"WarningThreshold": 80,
"CriticalThreshold": 90,
"DurationSeconds": 60,
"IsEnabled": true
}
],
"Telegram": {
"IsEnabled": true,
"BotToken": "123456789:ABCdefGHIjklMNOpqrSTUvwxyz",
"ChatIds": [123456789],
"SendWarningAlerts": true,
"SendCriticalAlerts": true,
"SendResolutionNotifications": true,
"MessageTemplate": "🚨 *{Level} Alert*\n\n📊 *{Component}*\n💬 {Message}\n⏰ {Timestamp}",
"ResolutionTemplate": "✅ *Alert Resolved*\n\n📊 *{Component}*\n💬 {Message}\n⏰ Resolved at {ResolvedAt}"
}
}
}