Skip to content

ICrontabSource #834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ public class ConversationChannel
public const string WebChat = "webchat";
public const string OpenAPI = "openapi";
public const string Phone = "phone";
public const string SMS = "sms";
public const string Messenger = "messenger";
public const string Email = "email";
public const string Cron = "cron";
public const string Crontab = "crontab";
public const string Database = "database";
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class CrontabItem : ScheduleTaskArgs
[JsonPropertyName("expire_seconds")]
public int ExpireSeconds { get; set; } = 60;

[JsonPropertyName("last_execution_time")]
public DateTime? LastExecutionTime { get; set; }

[JsonPropertyName("created_time")]
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@ namespace BotSharp.Core.Crontab.Abstraction;

public interface ICrontabHook
{
Task OnCronTriggered(CrontabItem item);
Task OnCronTriggered(CrontabItem item)
=> Task.CompletedTask;

Task OnTaskExecuting(CrontabItem item)
=> Task.CompletedTask;

Task OnTaskExecuted(CrontabItem item)
=> Task.CompletedTask;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace BotSharp.Core.Crontab.Abstraction;

/// <summary>
/// Provide a cron source for the crontab service.
/// </summary>
public interface ICrontabSource
{
CrontabItem GetCrontabItem();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,28 @@ public async Task<List<CrontabItem>> GetCrontable()
{
var repo = _services.GetRequiredService<IBotSharpRepository>();
var crontable = repo.GetCrontabItems(CrontabItemFilter.Empty());
return crontable.Items.ToList();

// Add fixed crontab items from cronsources
var fixedCrantabItems = crontable.Items.ToList();
var cronsources = _services.GetServices<ICrontabSource>();
foreach (var source in cronsources)
{
var item = source.GetCrontabItem();
fixedCrantabItems.Add(source.GetCrontabItem());
}

return fixedCrantabItems;
}

public async Task ScheduledTimeArrived(CrontabItem item)
{
_logger.LogDebug($"ScheduledTimeArrived {item}");

await HookEmitter.Emit<ICrontabHook>(_services, async hook =>
await hook.OnCronTriggered(item)
);
await Task.Delay(1000 * 10);
{
await hook.OnTaskExecuting(item);
await hook.OnCronTriggered(item);
await hook.OnTaskExecuted(item);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var locker = scope.ServiceProvider.GetRequiredService<IDistributedLocker>();

/*while (!stoppingToken.IsCancellationRequested)
while (!stoppingToken.IsCancellationRequested)
{
var delay = Task.Delay(1000, stoppingToken);
var delay = Task.Delay(1000 * 10, stoppingToken);

await locker.LockAsync("CrontabWatcher", async () =>
{
await RunCronChecker(scope.ServiceProvider);
});

await delay;
}*/
}

_logger.LogWarning("Crontab Watcher background service is stopped.");
}
Expand All @@ -58,10 +58,24 @@ private async Task RunCronChecker(IServiceProvider services)
// Get the current time
var currentTime = DateTime.UtcNow;

// Get the last occurrence from the schedule
var lastOccurrence = GetLastOccurrence(schedule);

// Get the next occurrence from the schedule
var nextOccurrence = schedule.GetNextOccurrence(currentTime.AddSeconds(-1));

// Check if the current time matches the schedule
// 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
bool matches = currentTime >= nextOccurrence && currentTime < nextOccurrence.AddSeconds(1);

if (matches)
Expand All @@ -72,9 +86,21 @@ private async Task RunCronChecker(IServiceProvider services)
}
catch (Exception ex)
{
_logger.LogWarning($"Error when running cron task ({item.ConversationId}, {item.Title}, {item.Cron}): {ex.Message}\r\n{ex.InnerException}");
_logger.LogError($"Error when running cron task ({item.Title}, {item.Cron}): {ex.Message}");
continue;
}
}
}

private DateTime GetLastOccurrence(CrontabSchedule schedule)
{
var nextOccurrence = schedule.GetNextOccurrence(DateTime.UtcNow);
var afterNextOccurrence = schedule.GetNextOccurrence(nextOccurrence);
var interval = afterNextOccurrence - nextOccurrence;
if (interval.TotalMinutes < 10)
{
throw new ArgumentException("The minimum interval must be at least 10 minutes.");
}
return nextOccurrence - interval;
}
}
4 changes: 3 additions & 1 deletion src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ public async Task Triggered(IRuleTrigger trigger, string data)
{
var conv = await convService.NewConversation(new Conversation
{
Channel = trigger.Channel,
Title = data,
AgentId = agent.Id
});

var message = new RoleDialogModel(AgentRole.User, data);

var states = new List<MessageState>
{
new("channel", ConversationChannel.Database),
new("channel", trigger.Channel),
new("channel_id", trigger.EntityId)
};
convService.SetConversationId(conv.Id, states);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace BotSharp.Core.Rules.Triggers;

public interface IRuleConfig
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BotSharp.Core.Rules\BotSharp.Core.Rules.csproj" />
<ProjectReference Include="..\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>

Expand Down
12 changes: 0 additions & 12 deletions src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,4 @@ public IEnumerable<AgentUtility> GetAgentUtilityOptions()
}
return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Name)).OrderBy(x => x.Name).ToList();
}

[HttpGet("/agent/rule/options")]
public IEnumerable<AgentRule> GetAgentRuleOptions()
{
var rules = new List<AgentRule>();
var hooks = _services.GetServices<IAgentRuleHook>();
foreach (var hook in hooks)
{
hook.AddRules(rules);
}
return rules.Where(x => !string.IsNullOrWhiteSpace(x.TriggerName)).OrderBy(x => x.TriggerName).ToList();
}
}
33 changes: 33 additions & 0 deletions src/Infrastructure/BotSharp.OpenAPI/Controllers/RulesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Core.Rules.Triggers;

namespace BotSharp.OpenAPI.Controllers;

[Authorize]
[ApiController]
public class RulesController
{
private readonly IServiceProvider _services;

public RulesController(
IServiceProvider services)
{
_services = services;
}

[HttpGet("/rule/triggers")]
public IEnumerable<AgentRule> GetRuleTriggers()
{
var triggers = _services.GetServices<IRuleTrigger>();
return triggers.Select(x => new AgentRule
{
TriggerName = x.GetType().Name
}).OrderBy(x => x.TriggerName).ToList();
}

[HttpGet("/rule/formalization")]
public async Task<string> GetFormalizedRuleDefinition([FromBody] AgentRule rule)
{
return "{}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ public TwiMLResult InitiateOutboundCall(VoiceRequest request, [Required][FromQue
$"twilio/voice/speeches/{conversationId}/intial.mp3"
}
};
string tag = $"AnsweredBy: {Request.Form["AnsweredBy"]}";
string tag = $"twilio:{Request.Form["AnsweredBy"]}";
var db = _services.GetRequiredService<IBotSharpRepository>();
db.AppendConversationTags(conversationId, new List<string> { tag });
var twilio = _services.GetRequiredService<TwilioService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public async Task<bool> Execute(RoleDialogModel message)
url: new Uri($"{_twilioSetting.CallbackHost}/twilio/voice/init-call?conversationId={conversationId}"),
to: new PhoneNumber(args.PhoneNumber),
from: new PhoneNumber(_twilioSetting.PhoneNumber),
asyncAmd: "true",
machineDetection: "DetectMessageEnd");

message.Content = $"The generated phone message: {args.InitialMessage}. \r\n[Conversation ID: {conversationId}]" ?? message.Content;
Expand Down
7 changes: 6 additions & 1 deletion src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ public VoiceResponse ReturnInstructions(string message)
Gather.InputEnum.Speech,
Gather.InputEnum.Dtmf
},
Action = new Uri($"{_settings.CallbackHost}/twilio/voice/{twilioSetting.AgentId}")
Action = new Uri($"{_settings.CallbackHost}/twilio/voice/{twilioSetting.AgentId}"),
Enhanced = true,
SpeechModel = Gather.SpeechModelEnum.PhoneCall,
SpeechTimeout = "auto"
};

gather.Say(message);
Expand All @@ -78,6 +81,7 @@ public VoiceResponse ReturnInstructions(ConversationalVoiceResponse conversation
Gather.InputEnum.Dtmf
},
Action = new Uri($"{_settings.CallbackHost}/{conversationalVoiceResponse.CallbackPath}"),
Enhanced = true,
SpeechModel = Gather.SpeechModelEnum.PhoneCall,
SpeechTimeout = "auto", // timeout > 0 ? timeout.ToString() : "3",
Timeout = conversationalVoiceResponse.Timeout > 0 ? conversationalVoiceResponse.Timeout : 3,
Expand Down Expand Up @@ -115,6 +119,7 @@ public VoiceResponse ReturnNoninterruptedInstructions(ConversationalVoiceRespons
Gather.InputEnum.Dtmf
},
Action = new Uri($"{_settings.CallbackHost}/{conversationalVoiceResponse.CallbackPath}"),
Enhanced = true,
SpeechModel = Gather.SpeechModelEnum.PhoneCall,
SpeechTimeout = "auto", // conversationalVoiceResponse.Timeout > 0 ? conversationalVoiceResponse.Timeout.ToString() : "3",
Timeout = conversationalVoiceResponse.Timeout > 0 ? conversationalVoiceResponse.Timeout : 3,
Expand Down