diff --git a/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs index 3a5310125..02db4534a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Crontab/Models/CrontabItem.cs @@ -29,6 +29,12 @@ public class CrontabItem : ScheduleTaskArgs [JsonPropertyName("created_time")] public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + [JsonPropertyName("trigger_by_watcher")] + public bool TriggerByWatcher { get; set; } = true; + + [JsonPropertyName("trigger_by_openapi")] + public bool TriggerByOpenAPI { get; set; } + public override string ToString() { return $"{Title}: {Description} [AgentId: {AgentId}, UserId: {UserId}]"; diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabItemExtension.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabItemExtension.cs new file mode 100644 index 000000000..1be4cd93f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabItemExtension.cs @@ -0,0 +1,26 @@ +using NCrontab; + +namespace BotSharp.Core.Crontab.Services +{ + public static class CrontabItemExtension + { + public static bool CheckNextOccurrenceEveryOneMinute(this CrontabItem item) + { + // strip seconds from cron expression + item.Cron = string.Join(" ", item.Cron.Split(' ').TakeLast(5)); + var schedule = CrontabSchedule.Parse(item.Cron, new CrontabSchedule.ParseOptions + { + IncludingSeconds = false // Ensure you account for seconds + }); + + var currentTime = DateTime.UtcNow; + var currentMinute = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, + currentTime.Hour, currentTime.Minute, 0, DateTimeKind.Utc); + + var oneMinuteAgo = currentMinute.AddMinutes(-1); + var nextOccurrence = schedule.GetNextOccurrence(oneMinuteAgo); + + return nextOccurrence == currentMinute; + } + } +} diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs index dddd3d58a..ea1798d43 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabWatcher.cs @@ -58,10 +58,11 @@ private async Task RunCronChecker(IServiceProvider services) { var cron = services.GetRequiredService(); var crons = await cron.GetCrontable(); + var allowedCrons = crons.Where(cron => cron.TriggerByWatcher).ToList(); var settings = services.GetRequiredService(); var publisher = services.GetService(); - foreach (var item in crons) + foreach (var item in allowedCrons) { _logger.LogDebug($"[{DateTime.UtcNow}] Cron task ({item.Title}, {item.Cron}), Last Execution Time: {item.LastExecutionTime}"); diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj index e2b453edb..41fcf07dc 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj +++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -35,6 +35,7 @@ + diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Crontab/CrontabController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Crontab/CrontabController.cs index 3f3127789..2f1e67d03 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/Crontab/CrontabController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/Crontab/CrontabController.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Crontab; +using BotSharp.Abstraction.Crontab.Models; +using BotSharp.Core.Crontab.Services; namespace BotSharp.OpenAPI.Controllers; @@ -42,4 +44,55 @@ public async Task RunCrontab(string name) return false; } } + + /// + /// As the Dkron job trigger API, run every 1 minutes + /// + /// + [HttpPost("/crontab/scheduling-per-minute")] + public async Task SchedulingCrontab() + { + var allowedCrons = await GetCrontabItems(); + + foreach (var item in allowedCrons) + { + if (item.CheckNextOccurrenceEveryOneMinute()) + { + _logger.LogInformation("Crontab: {0}, One occurrence was matched, Beginning execution...", item.Title); + Task.Run(() => ExecuteTimeArrivedItem(item)); + } + } + } + + private async Task> GetCrontabItems(string? title = null) + { + var crontabService = _services.GetRequiredService(); + var crons = await crontabService.GetCrontable(); + var allowedCrons = crons.Where(cron => cron.TriggerByOpenAPI).ToList(); + + if (title is null) + { + return allowedCrons; + } + + return allowedCrons.Where(cron => cron.Title.IsEqualTo(title)).ToList(); + } + + private async Task ExecuteTimeArrivedItem(CrontabItem item) + { + try + { + using var scope = _services.CreateScope(); + var crontabService = scope.ServiceProvider.GetRequiredService(); + _logger.LogWarning($"Start running crontab {item.Title}"); + await crontabService.ScheduledTimeArrived(item); + _logger.LogWarning($"Complete running crontab {item.Title}"); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error when running crontab {item.Title}"); + return false; + } + } }