feat(telegram): log trigger changes and the trigger count at startup

This commit is contained in:
EugeneTes
2026-10-05 18:43:30 +00:00
parent 145977c6fb
commit 3a07d00eb2
3 changed files with 30 additions and 3 deletions

View File

@@ -65,6 +65,16 @@ public sealed class TriggerStore
public static string PathIn(string workspace) => Path.Combine(DirectoryIn(workspace), "triggers.json");
public int Count
{
get { lock (_lock) return _byOwner.Values.Sum(l => l.Count); }
}
public int OwnerCount
{
get { lock (_lock) return _byOwner.Values.Count(l => l.Count > 0); }
}
public IReadOnlyList<Trigger> List(long owner)
{
lock (_lock) return _byOwner.TryGetValue(owner, out var list) ? [.. list] : [];

View File

@@ -1,6 +1,8 @@
using System.ComponentModel;
using System.Globalization;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volcanic.AgenticCore.Abstractions.Extensions;
using Volcanic.AgenticCore.Abstractions.Tools;
using Volcanic.AgenticCore.Telegram.Groups;
@@ -8,8 +10,12 @@ using Volcanic.AgenticCore.Telegram.Groups;
namespace Volcanic.AgenticCore.Telegram.Triggers;
/// <summary>What the trigger tool, the extension and <c>/triggers</c> share; registered only when triggers are on.</summary>
public sealed class TriggerServices(TriggerEngine engine, GroupRegistry groups, IGroupMembership membership, TriggerOptions options)
public sealed class TriggerServices(TriggerEngine engine, GroupRegistry groups, IGroupMembership membership, TriggerOptions options,
ILogger<TriggerServices>? logger = null)
{
/// <summary>Every change the tool makes is logged here.</summary>
public ILogger Logger { get; } = logger ?? NullLogger<TriggerServices>.Instance;
public TriggerEngine Engine => engine;
public TriggerStore Store => engine.Store;
public GroupRegistry Groups => groups;
@@ -120,6 +126,8 @@ public sealed class TriggerTool(long owner, TriggerServices services, Func<TimeZ
throw Error($"The user isn't a member of \"{group.Title}\" (or the bot can't check), so no trigger can watch it for them.");
var created = await services.Store.AddAsync(owner, new NewTrigger(args.Name.Trim(), group.Id, group.Title, args.Instructions.Trim(),
Clean(args.CriteriaTrue), Clean(args.CriteriaFalse), threshold), cancellationToken).ConfigureAwait(false);
services.Logger.LogInformation("Chat {Owner}: trigger {TriggerId} \"{Name}\" created on group {GroupId} \"{Title}\", threshold {Threshold:0.00}: {Instructions}",
owner, created.Id, created.Name, group.Id, group.Title, created.Threshold, created.Instructions);
return ToolResult.Text("Created: " + TriggerPrompts.Line(created, zone(), services.Engine.Now, 0));
}
@@ -138,6 +146,8 @@ public sealed class TriggerTool(long owner, TriggerServices services, Func<TimeZ
Threshold = threshold ?? t.Threshold,
Enabled = args.Enabled ?? t.Enabled,
}, cancellationToken).ConfigureAwait(false) ?? throw Error($"No trigger {id} in this chat.");
services.Logger.LogInformation("Chat {Owner}: trigger {TriggerId} \"{Name}\" updated: threshold {Threshold:0.00}, {State}: {Instructions}",
owner, updated.Id, updated.Name, updated.Threshold, updated.Enabled ? "on" : "off", updated.Instructions);
return ToolResult.Text("Updated: " + TriggerPrompts.Line(updated, zone(), services.Engine.Now, services.Engine.Queued(owner, updated.Id)));
}
@@ -145,6 +155,7 @@ public sealed class TriggerTool(long owner, TriggerServices services, Func<TimeZ
{
var id = RequireId(args.Id, "delete");
var removed = await services.Engine.DeleteAsync(owner, id, cancellationToken).ConfigureAwait(false) ?? throw Error($"No trigger {id} in this chat.");
services.Logger.LogInformation("Chat {Owner}: trigger {TriggerId} \"{Name}\" deleted", owner, removed.Id, removed.Name);
return ToolResult.Text($"Deleted {removed.Id} (\"{removed.Name}\").");
}
@@ -166,6 +177,7 @@ public sealed class TriggerTool(long owner, TriggerServices services, Func<TimeZ
var snoozed = await services.Engine.SnoozeAsync(owner, Id(args.Id), until, cancellationToken).ConfigureAwait(false);
if (snoozed.Count == 0) throw Error(args.Id is null ? "This chat has no triggers." : $"No trigger {args.Id.Trim()} in this chat.");
services.Logger.LogInformation("Chat {Owner}: {Triggers} snoozed until {Until:O}", owner, string.Join(", ", snoozed.Select(t => t.Id)), until);
return ToolResult.Text($"Snoozed {string.Join(", ", snoozed.Select(t => t.Id))} until {TriggerPrompts.Stamp(until, tz)} ({tz.Id}). " +
"Messages are collected meanwhile and checked as one batch when the snooze ends; you get one prompt if anything is related.");
}

View File

@@ -46,9 +46,14 @@ public static class TriggersSetup
}
/// <summary>Flushes snoozes that ended while the host was down and arms the snooze timer.</summary>
public sealed class TriggersHostedService(TriggerEngine engine) : IHostedService
public sealed class TriggersHostedService(TriggerEngine engine, GroupRegistry groups, ILogger<TriggersHostedService> logger) : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken) => engine.StartAsync(cancellationToken);
public Task StartAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Group triggers: {Count} trigger(s) loaded for {Chats} chat(s), {Groups} watched group(s)",
engine.Store.Count, engine.Store.OwnerCount, groups.All().Count(g => g.Active));
return engine.StartAsync(cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken)
{