BotSharp/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs

127 lines
4.5 KiB
C#
Raw Normal View History

2024-12-03 22:52:14 +00:00
using BotSharp.Abstraction.Infrastructures;
2025-01-24 21:50:28 +00:00
using BotSharp.Abstraction.Infrastructures.Events;
2024-12-03 22:52:14 +00:00
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NCrontab;
namespace BotSharp.Core.Crontab.Services;
public class CrontabWatcher : BackgroundService
{
private readonly ILogger _logger;
private readonly IServiceProvider _services;
public CrontabWatcher(IServiceProvider services, ILogger<CrontabWatcher> logger)
{
_logger = logger;
_services = services;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Crontab Watcher background service is running.");
using (var scope = _services.CreateScope())
{
var locker = scope.ServiceProvider.GetRequiredService<IDistributedLocker>();
2025-01-16 21:57:50 +00:00
while (!stoppingToken.IsCancellationRequested)
2024-12-03 22:52:14 +00:00
{
2025-01-24 21:50:28 +00:00
var delay = Task.Delay(1000, stoppingToken);
2024-12-03 22:52:14 +00:00
2025-01-24 21:50:28 +00:00
await locker.LockAsync("CrontabWatcher:locker", async () =>
2024-12-03 22:52:14 +00:00
{
await RunCronChecker(scope.ServiceProvider);
});
await delay;
2025-01-16 21:57:50 +00:00
}
2024-12-03 22:52:14 +00:00
_logger.LogWarning("Crontab Watcher background service is stopped.");
}
}
private async Task RunCronChecker(IServiceProvider services)
{
var cron = services.GetRequiredService<ICrontabService>();
var crons = await cron.GetCrontable();
var publisher = services.GetService<IEventPublisher>();
2025-01-24 21:50:28 +00:00
2024-12-03 22:52:14 +00:00
foreach (var item in crons)
{
2025-01-24 21:50:28 +00:00
_logger.LogDebug($"[{DateTime.UtcNow}] Cron task ({item.Title}, {item.Cron}), Last Execution Time: {item.LastExecutionTime}");
2024-12-10 21:16:03 +00:00
try
2024-12-03 22:52:14 +00:00
{
2024-12-11 22:02:22 +00:00
// strip seconds from cron expression
item.Cron = string.Join(" ", item.Cron.Split(' ').TakeLast(5));
2024-12-10 21:16:03 +00:00
var schedule = CrontabSchedule.Parse(item.Cron, new CrontabSchedule.ParseOptions
{
2024-12-11 22:02:22 +00:00
IncludingSeconds = false // Ensure you account for seconds
2024-12-10 21:16:03 +00:00
});
2024-12-03 22:52:14 +00:00
2024-12-10 21:16:03 +00:00
// Get the current time
var currentTime = DateTime.UtcNow;
2024-12-03 22:52:14 +00:00
2025-01-16 21:57:50 +00:00
// Get the last occurrence from the schedule
var lastOccurrence = GetLastOccurrence(schedule);
2024-12-10 21:16:03 +00:00
// Get the next occurrence from the schedule
var nextOccurrence = schedule.GetNextOccurrence(currentTime.AddSeconds(-1));
2024-12-03 22:52:14 +00:00
2025-01-16 21:57:50 +00:00
// Get the previous occurrence from the execution log
var previousOccurrence = item.LastExecutionTime;
// First check if this occurrence was already triggered
if (previousOccurrence.HasValue &&
previousOccurrence.Value >= lastOccurrence &&
previousOccurrence.Value < nextOccurrence.AddSeconds(1))
{
continue;
}
// Then check if the current time matches the schedule
2024-12-10 21:16:03 +00:00
bool matches = currentTime >= nextOccurrence && currentTime < nextOccurrence.AddSeconds(1);
2024-12-03 22:52:14 +00:00
2024-12-10 21:16:03 +00:00
if (matches)
{
2025-01-24 21:50:28 +00:00
_logger.LogInformation($"The current time matches the cron expression {item}");
if (publisher != null)
{
await publisher.PublishAsync($"Crontab:{item.Title}", item.Cron);
}
else
{
await HandleCrontabEvent(item);
}
2024-12-10 21:16:03 +00:00
}
}
catch (Exception ex)
2024-12-03 22:52:14 +00:00
{
2025-01-16 21:57:50 +00:00
_logger.LogError($"Error when running cron task ({item.Title}, {item.Cron}): {ex.Message}");
2024-12-10 21:16:03 +00:00
continue;
2024-12-03 22:52:14 +00:00
}
}
}
2025-01-16 21:57:50 +00:00
private async Task HandleCrontabEvent(CrontabItem item)
{
using var scope = _services.CreateScope();
var cron = scope.ServiceProvider.GetRequiredService<ICrontabService>();
await cron.ScheduledTimeArrived(item);
}
2025-01-16 21:57:50 +00:00
private DateTime GetLastOccurrence(CrontabSchedule schedule)
{
var nextOccurrence = schedule.GetNextOccurrence(DateTime.UtcNow);
var afterNextOccurrence = schedule.GetNextOccurrence(nextOccurrence);
var interval = afterNextOccurrence - nextOccurrence;
2025-01-24 21:50:28 +00:00
if (interval.TotalMinutes < 1)
2025-01-16 21:57:50 +00:00
{
2025-01-24 21:50:28 +00:00
throw new ArgumentException("The minimum interval must be at least 1 minutes.");
2025-01-16 21:57:50 +00:00
}
return nextOccurrence - interval;
}
2024-12-03 22:52:14 +00:00
}