diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs index c8838b334..3a2b05dbb 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabService.cs @@ -116,6 +116,8 @@ public async Task ScheduledTimeArrived(CrontabItem item) { _logger.LogDebug($"ScheduledTimeArrived {item}"); + if (!await HasEnabledTriggerRule(item)) return; + await HookEmitter.Emit(_services, async hook => { if (hook.Triggers == null || hook.Triggers.Contains(item.Title)) @@ -127,4 +129,25 @@ await HookEmitter.Emit(_services, async hook => } }, item.AgentId); } + + /// + /// Returns whether the trigger is treated as enabled for this schedule: true unless a rule with the + /// same trigger name exists and is explicitly disabled (opt-out). Missing rules do not block. + /// + private async Task HasEnabledTriggerRule(CrontabItem item) + { + var agentService = _services.GetRequiredService(); + // No agent context: do not gate (legacy / callers without AgentId). + if (string.IsNullOrEmpty(item.AgentId)) return true; + + var agent = await agentService.GetAgent(item.AgentId); + if (agent == null) + { + _logger.LogWarning("Agent {AgentId} is not found", item.AgentId); + return false; + } + + // Opt-out only: block when a matching trigger rule exists and Disabled is true. + return !agent.Rules.Any(r => r.TriggerName == item.Title && r.Disabled); + } }