59 lines
3.3 KiB
C#
59 lines
3.3 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||
using Microsoft.Extensions.Hosting;
|
||
using Microsoft.Extensions.Logging;
|
||
using Telegram.Bot.Types;
|
||
using Volcanic.AgenticCore.Telegram.Chats;
|
||
using Volcanic.AgenticCore.Telegram.Groups;
|
||
|
||
namespace Volcanic.AgenticCore.Telegram.Triggers;
|
||
|
||
/// <summary>Everything the Telegram host does for group triggers, kept out of the shared host files.</summary>
|
||
public static class TriggersSetup
|
||
{
|
||
public const string DisabledReply = "Group triggers are off on this bot (no OPENROUTER_API_KEY).";
|
||
|
||
public const string HelpLines = "\n/triggers – this chat's group triggers (ask me to create, change or snooze them)";
|
||
|
||
public static readonly BotCommand MenuCommand = new() { Command = "triggers", Description = "Group triggers watching for topics" };
|
||
|
||
/// <summary>The Jev client, groups, store, engine, delivery, feed and the hosted service. Only when triggers are on.</summary>
|
||
public static void Register(IServiceCollection services, TelegramOptions options, string agentDir)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(services);
|
||
ArgumentNullException.ThrowIfNull(options);
|
||
var triggers = options.Triggers;
|
||
services.TryAddSingleton(TimeProvider.System);
|
||
services.AddSingleton(triggers);
|
||
// The engine (a singleton) keeps this typed client for the process lifetime: let the handler recycle its pooled
|
||
// connections instead, so DNS changes are still picked up.
|
||
services.AddHttpClient<IJevClient, JevClient>(client => client.Timeout = TimeSpan.FromSeconds(30))
|
||
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(5) })
|
||
.SetHandlerLifetime(Timeout.InfiniteTimeSpan);
|
||
services.AddSingleton(sp => new GroupRegistry(GroupRegistry.DefaultPath(agentDir), sp.GetRequiredService<ILogger<GroupRegistry>>(), sp.GetRequiredService<TimeProvider>()));
|
||
services.AddSingleton(sp => new TriggerStore(options.WorkspaceRoot, triggers.MaxTriggersPerChat, sp.GetRequiredService<ILogger<TriggerStore>>(), sp.GetRequiredService<TimeProvider>()));
|
||
services.AddSingleton(new SnoozeQueue(triggers.MaxQueuedPerTrigger));
|
||
// Resolves the registry at call time: ChatRegistry → trigger extension → engine → delivery would be a cycle.
|
||
services.AddSingleton<ITriggerDelivery>(sp => new TelegramTriggerDelivery(
|
||
() => sp.GetRequiredService<ChatRegistry>(), sp.GetRequiredService<TimeProvider>(), sp.GetRequiredService<ILogger<TelegramTriggerDelivery>>()));
|
||
services.AddSingleton<TriggerDispatcher>();
|
||
services.AddSingleton<TriggerEngine>();
|
||
services.AddSingleton<IGroupMembership, TelegramGroupMembership>();
|
||
services.AddSingleton<TriggerServices>();
|
||
services.AddSingleton<GroupFeed>();
|
||
services.AddHostedService<TriggersHostedService>();
|
||
}
|
||
}
|
||
|
||
/// <summary>Flushes snoozes that ended while the host was down and arms the snooze timer.</summary>
|
||
public sealed class TriggersHostedService(TriggerEngine engine) : IHostedService
|
||
{
|
||
public Task StartAsync(CancellationToken cancellationToken) => engine.StartAsync(cancellationToken);
|
||
|
||
public Task StopAsync(CancellationToken cancellationToken)
|
||
{
|
||
engine.Dispose();
|
||
return Task.CompletedTask;
|
||
}
|
||
}
|