about summary refs log tree commit diff
path: root/Utilities/LibMatrix.Utilities.Bot
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/LibMatrix.Utilities.Bot')
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/BotServiceInstaller.cs (renamed from Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs)187
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Configuration/CommandListenerConfiguration.cs24
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Configuration/InviteListenerConfiguration.cs20
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Configuration/LibMatrixBotConfiguration.cs (renamed from Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs)8
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs3
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs2
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj16
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs121
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs31
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/deps.json142
10 files changed, 378 insertions, 176 deletions
diff --git a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs b/Utilities/LibMatrix.Utilities.Bot/BotServiceInstaller.cs

index 56ceb65..ff0bc9e 100644 --- a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs +++ b/Utilities/LibMatrix.Utilities.Bot/BotServiceInstaller.cs
@@ -1,91 +1,98 @@ -using ArcaneLibs; -using LibMatrix.Homeservers; -using LibMatrix.Services; -using LibMatrix.Utilities.Bot.AppServices; -using LibMatrix.Utilities.Bot.Interfaces; -using LibMatrix.Utilities.Bot.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace LibMatrix.Utilities.Bot; - -public static class BotCommandInstaller { - public static BotInstaller AddMatrixBot(this IServiceCollection services) { - return new BotInstaller(services).AddMatrixBot(); - } -} - -public class BotInstaller(IServiceCollection services) { - public BotInstaller AddMatrixBot() { - services.AddSingleton<LibMatrixBotConfiguration>(); - - services.AddSingleton<AuthenticatedHomeserverGeneric>(x => { - var config = x.GetService<LibMatrixBotConfiguration>() ?? throw new Exception("No configuration found!"); - var hsProvider = x.GetService<HomeserverProviderService>() ?? throw new Exception("No homeserver provider found!"); - - if (x.GetService<AppServiceConfiguration>() is AppServiceConfiguration appsvcConfig) - config.AccessToken = appsvcConfig.AppserviceToken; - else if (Environment.GetEnvironmentVariable("LIBMATRIX_ACCESS_TOKEN_PATH") is string path) - config.AccessTokenPath = path; - - if (string.IsNullOrWhiteSpace(config.AccessToken) && string.IsNullOrWhiteSpace(config.AccessTokenPath)) - throw new Exception("Unable to add bot service without an access token or access token path!"); - - if (!string.IsNullOrWhiteSpace(config.AccessTokenPath)) { - var token = File.ReadAllText(config.AccessTokenPath); - config.AccessToken = token.Trim(); - } - - var hs = hsProvider.GetAuthenticatedWithToken(config.Homeserver, config.AccessToken).Result; - - return hs; - }); - - return this; - } - - public BotInstaller AddCommandHandler() { - Console.WriteLine("Adding command handler..."); - services.AddHostedService<CommandListenerHostedService>(); - return this; - } - - public BotInstaller DiscoverAllCommands() { - foreach (var commandClass in ClassCollector<ICommand>.ResolveFromAllAccessibleAssemblies()) { - Console.WriteLine($"Adding command {commandClass.Name}"); - services.AddScoped(typeof(ICommand), commandClass); - } - - return this; - } - - public BotInstaller AddCommands(IEnumerable<Type> commandClasses) { - foreach (var commandClass in commandClasses) { - if (!commandClass.IsAssignableTo(typeof(ICommand))) - throw new Exception($"Type {commandClass.Name} is not assignable to ICommand!"); - Console.WriteLine($"Adding command {commandClass.Name}"); - services.AddScoped(typeof(ICommand), commandClass); - } - - return this; - } - - public BotInstaller WithInviteHandler(Func<RoomInviteContext, Task> inviteHandler) { - services.AddSingleton(inviteHandler); - services.AddHostedService<InviteHandlerHostedService>(); - services.AddSingleton<InviteHandlerHostedService.InviteListenerSyncConfiguration>(); - return this; - } - - public BotInstaller WithInviteHandler<T>() where T : class, IRoomInviteHandler { - services.AddSingleton<T>(); - services.AddSingleton<Func<RoomInviteContext, Task>>(sp => sp.GetRequiredService<T>().HandleInviteAsync); - services.AddHostedService<InviteHandlerHostedService>(); - services.AddSingleton<InviteHandlerHostedService.InviteListenerSyncConfiguration>(); - return this; - } - - public BotInstaller WithCommandResultHandler(Func<CommandResult, Task> commandResultHandler) { - services.AddSingleton(commandResultHandler); - return this; - } +using ArcaneLibs; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using LibMatrix.Utilities.Bot.AppServices; +using LibMatrix.Utilities.Bot.Interfaces; +using LibMatrix.Utilities.Bot.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace LibMatrix.Utilities.Bot; + +public static class BotServiceInstallerExtensions { + public static BotServiceInstaller AddMatrixBot(this IServiceCollection services) { + return new BotServiceInstaller(services).AddMatrixBot(); + } +} + +public class BotServiceInstaller(IServiceCollection services) { + public BotServiceInstaller AddMatrixBot() { + services.AddSingleton<LibMatrixBotConfiguration>(); + + services.AddSingleton<AuthenticatedHomeserverGeneric>(x => { + var config = x.GetService<LibMatrixBotConfiguration>() ?? throw new Exception("No configuration found!"); + var hsProvider = x.GetService<HomeserverProviderService>() ?? throw new Exception("No homeserver provider found!"); + + if (x.GetService<AppServiceConfiguration>() is AppServiceConfiguration appsvcConfig) + config.AccessToken = appsvcConfig.AppserviceToken; + else if (Environment.GetEnvironmentVariable("LIBMATRIX_ACCESS_TOKEN_PATH") is string path) + config.AccessTokenPath = path; + + if (string.IsNullOrWhiteSpace(config.AccessToken) && string.IsNullOrWhiteSpace(config.AccessTokenPath)) + throw new Exception("Unable to add bot service without an access token or access token path!"); + + if (!string.IsNullOrWhiteSpace(config.AccessTokenPath)) { + var token = File.ReadAllText(config.AccessTokenPath); + config.AccessToken = token.Trim(); + } + + var hs = hsProvider.GetAuthenticatedWithToken(config.Homeserver, config.AccessToken).Result; + + return hs; + }); + + return this; + } + + public BotServiceInstaller AddCommandHandler() { + Console.WriteLine("Adding command handler..."); + services.AddSingleton(s => s.GetRequiredService<LibMatrixBotConfiguration>().CommandListener + ?? throw new Exception("Command handling is enabled, but configuration is missing the LibMatrixBot:CommandListener configuration section!") + ); + services.AddHostedService<CommandListenerHostedService>(); + return this; + } + + public BotServiceInstaller DiscoverAllCommands() { + foreach (var commandClass in ClassCollector<ICommand>.ResolveFromAllAccessibleAssemblies()) { + Console.WriteLine($"Adding command {commandClass.Name}"); + services.AddScoped(typeof(ICommand), commandClass); + } + + return this; + } + + public BotServiceInstaller AddCommands(IEnumerable<Type> commandClasses) { + foreach (var commandClass in commandClasses) { + if (!commandClass.IsAssignableTo(typeof(ICommand))) + throw new Exception($"Type {commandClass.Name} is not assignable to ICommand!"); + Console.WriteLine($"Adding command {commandClass.Name}"); + services.AddScoped(typeof(ICommand), commandClass); + } + + return this; + } + + public BotServiceInstaller WithCommandResultHandler(Func<CommandResult, Task> commandResultHandler) { + services.AddSingleton(commandResultHandler); + return this; + } + + public BotServiceInstaller WithInviteHandler(Func<RoomInviteContext, Task> inviteHandler) { + services.AddSingleton(inviteHandler); + services.AddSingleton(s => s.GetRequiredService<LibMatrixBotConfiguration>().InviteListener + ?? throw new Exception("Invite handling is enabled, but configuration is missing the LibMatrixBot:InviteListener configuration section!") + ); + services.AddHostedService<InviteHandlerHostedService>(); + return this; + } + + public BotServiceInstaller WithInviteHandler<T>() where T : class, IRoomInviteHandler { + services.AddSingleton<T>(); + services.AddSingleton<Func<RoomInviteContext, Task>>(sp => sp.GetRequiredService<T>().HandleInviteAsync); + services.AddSingleton(s => s.GetRequiredService<LibMatrixBotConfiguration>().InviteListener + ?? throw new Exception("Invite handling is enabled, but configuration is missing the LibMatrixBot:InviteListener configuration section!") + ); + services.AddHostedService<InviteHandlerHostedService>(); + return this; + } } \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/Configuration/CommandListenerConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/Configuration/CommandListenerConfiguration.cs new file mode 100644
index 0000000..e3026cd --- /dev/null +++ b/Utilities/LibMatrix.Utilities.Bot/Configuration/CommandListenerConfiguration.cs
@@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using LibMatrix.Filters; + +namespace LibMatrix.Utilities.Bot.Configuration; + +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Configuration")] +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Configuration")] +public class CommandListenerSyncConfiguration { + // public SyncFilter? Filter { get; set; } + public TimeSpan? MinimumSyncTime { get; set; } + public int? Timeout { get; set; } + public string? Presence { get; set; } + // public bool InitialSyncOnStartup { get; set; } +} + +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Configuration")] +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Configuration")] +public class CommandListenerConfiguration { + public CommandListenerSyncConfiguration SyncConfiguration { get; set; } = new(); + + public required List<string> Prefixes { get; set; } + public bool MentionPrefix { get; set; } + public bool SelfCommandsOnly { get; set; } +} \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/Configuration/InviteListenerConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/Configuration/InviteListenerConfiguration.cs new file mode 100644
index 0000000..7fce400 --- /dev/null +++ b/Utilities/LibMatrix.Utilities.Bot/Configuration/InviteListenerConfiguration.cs
@@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using LibMatrix.Filters; + +namespace LibMatrix.Utilities.Bot.Configuration; + +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Configuration")] +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Configuration")] +public class InviteListenerSyncConfiguration { + public SyncFilter? Filter { get; set; } + public TimeSpan? MinimumSyncTime { get; set; } + public int? Timeout { get; set; } + public string? Presence { get; set; } + public bool InitialSyncOnStartup { get; set; } +} + +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Configuration")] +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Configuration")] +public class InviteListenerConfiguration { + public InviteListenerSyncConfiguration SyncConfiguration { get; set; } = new(); +} \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/Configuration/LibMatrixBotConfiguration.cs
index da83cfa..cd272e0 100644 --- a/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Configuration/LibMatrixBotConfiguration.cs
@@ -1,3 +1,4 @@ +using LibMatrix.Utilities.Bot.Configuration; using Microsoft.Extensions.Configuration; namespace LibMatrix.Utilities.Bot; @@ -7,7 +8,10 @@ public class LibMatrixBotConfiguration { public string Homeserver { get; set; } public string? AccessToken { get; set; } public string? AccessTokenPath { get; set; } - public List<string> Prefixes { get; set; } - public bool MentionPrefix { get; set; } public string? LogRoom { get; set; } + + public string? Presence { get; set; } + + public InviteListenerConfiguration? InviteListener { get; set; } + public CommandListenerConfiguration? CommandListener { get; set; } } \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs
index c6abde2..4da6df2 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs
@@ -1,12 +1,13 @@ using LibMatrix.EventTypes.Spec; using LibMatrix.Homeservers; +using LibMatrix.Responses; using LibMatrix.RoomTypes; namespace LibMatrix.Utilities.Bot.Interfaces; public class CommandContext { public required GenericRoom Room { get; set; } - public required StateEventResponse MessageEvent { get; set; } + public required MatrixEventResponse MessageEvent { get; set; } public string MessageContentWithoutReply => (MessageEvent.TypedContent as RoomMessageEventContent)! diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs
index 380c1c7..c5ffc7c 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs
@@ -7,7 +7,7 @@ namespace LibMatrix.Utilities.Bot.Interfaces; public class RoomInviteContext { public required string RoomId { get; init; } public required AuthenticatedHomeserverGeneric Homeserver { get; init; } - public required StateEventResponse MemberEvent { get; init; } + public required MatrixEventResponse MemberEvent { get; init; } public required SyncResponse.RoomsDataStructure.InvitedRoomDataStructure InviteData { get; init; } public async Task<string> TryGetInviterNameAsync() { diff --git a/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj b/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj
index bbb0a65..8c3bfcb 100644 --- a/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj +++ b/Utilities/LibMatrix.Utilities.Bot/LibMatrix.Utilities.Bot.csproj
@@ -1,20 +1,28 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> + <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <LangVersion>preview</LangVersion> + <PackageId>RoryLibMatrix.Utilities.Bot</PackageId> + <PackageLicenseExpression>AGPL-3.0-only</PackageLicenseExpression> + <PackageReadmeFile>README.md</PackageReadmeFile> </PropertyGroup> <ItemGroup> + <None Include="../../README.md" Pack="true" PackagePath="\"/> + </ItemGroup> + + <ItemGroup> <ProjectReference Include="..\..\LibMatrix\LibMatrix.csproj"/> + <PackageReference Include="RoryLibMatrix" Version="*-*" Condition="'$(ContinuousIntegrationBuild)'=='true'"/> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" /> - <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" /> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0"/> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0"/> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0"/> </ItemGroup> diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs
index 9fe460b..5b697de 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs
@@ -1,9 +1,11 @@ +using System.Collections.Frozen; using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec; using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.Filters; using LibMatrix.Helpers; using LibMatrix.Homeservers; +using LibMatrix.Utilities.Bot.Configuration; using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -11,87 +13,92 @@ using Microsoft.Extensions.Logging; namespace LibMatrix.Utilities.Bot.Services; -public class CommandListenerHostedService : IHostedService { - private readonly AuthenticatedHomeserverGeneric _hs; - private readonly ILogger<CommandListenerHostedService> _logger; - private readonly IEnumerable<ICommand> _commands; - private readonly LibMatrixBotConfiguration _config; - private readonly Func<CommandResult, Task>? _commandResultHandler; +public class CommandListenerHostedService( + AuthenticatedHomeserverGeneric hs, + ILogger<CommandListenerHostedService> logger, + IServiceProvider services, + LibMatrixBotConfiguration botConfig, + CommandListenerConfiguration config, + Func<CommandResult, Task>? commandResultHandler = null +) + : IHostedService { + private FrozenSet<ICommand> _commands = null!; private Task? _listenerTask; - private CancellationTokenSource _cts = new(); - - public CommandListenerHostedService(AuthenticatedHomeserverGeneric hs, ILogger<CommandListenerHostedService> logger, IServiceProvider services, - LibMatrixBotConfiguration config, Func<CommandResult, Task>? commandResultHandler = null) { - logger.LogInformation("{} instantiated!", GetType().Name); - _hs = hs; - _logger = logger; - _config = config; - _commandResultHandler = commandResultHandler; - _logger.LogInformation("Getting commands..."); - _commands = services.GetServices<ICommand>(); - _logger.LogInformation("Got {} commands!", _commands.Count()); - } + private readonly CancellationTokenSource _cts = new(); + private long _startupTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); /// <summary>Triggered when the application host is ready to start the service.</summary> /// <param name="cancellationToken">Indicates that the start process has been aborted.</param> public Task StartAsync(CancellationToken cancellationToken) { _listenerTask = Run(_cts.Token); - _logger.LogInformation("Command listener started (StartAsync)!"); + logger.LogInformation("Getting commands..."); + _commands = services.GetServices<ICommand>().ToFrozenSet(); + logger.LogInformation("Got {} commands!", _commands.Count); + logger.LogInformation("Command listener started (StartAsync)!"); return Task.CompletedTask; } private async Task? Run(CancellationToken cancellationToken) { - _logger.LogInformation("Starting command listener!"); - var filter = await _hs.NamedCaches.FilterCache.GetOrSetValueAsync("gay.rory.libmatrix.utilities.bot.command_listener_syncfilter.dev2", new SyncFilter() { - AccountData = new SyncFilter.EventFilter(notTypes: ["*"], limit: 1), - Presence = new SyncFilter.EventFilter(notTypes: ["*"]), - Room = new SyncFilter.RoomFilter() { - AccountData = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), - Ephemeral = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), - State = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), - Timeline = new SyncFilter.RoomFilter.StateFilter(types: ["m.room.message"], notSenders: [_hs.WhoAmI.UserId]), - } - }); + logger.LogInformation("Starting command listener!"); + var filter = await hs.NamedCaches.FilterCache.GetOrSetValueAsync("gay.rory.libmatrix.utilities.bot.command_listener_syncfilter.dev4" + (config.SelfCommandsOnly), + new SyncFilter() { + AccountData = new SyncFilter.EventFilter(notTypes: ["*"], limit: 1), + Presence = new SyncFilter.EventFilter(notTypes: ["*"]), + Room = new SyncFilter.RoomFilter() { + AccountData = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), + Ephemeral = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), + State = new SyncFilter.RoomFilter.StateFilter(notTypes: ["*"]), + Timeline = new SyncFilter.RoomFilter.StateFilter( + types: ["m.room.message"], + notSenders: config.SelfCommandsOnly ? null : [hs.WhoAmI.UserId], + senders: config.SelfCommandsOnly ? [hs.WhoAmI.UserId] : null, + limit: config.SelfCommandsOnly ? 1 : null + ), + } + }); - var syncHelper = new SyncHelper(_hs, _logger) { - Timeout = 30_000, - FilterId = filter + var syncHelper = new SyncHelper(hs, logger) { + FilterId = filter, + Timeout = config.SyncConfiguration.Timeout ?? 30_000, + MinimumDelay = config.SyncConfiguration.MinimumSyncTime ?? TimeSpan.Zero, + SetPresence = config.SyncConfiguration.Presence ?? botConfig.Presence, }; syncHelper.SyncReceivedHandlers.Add(async sync => { - _logger.LogInformation("Sync received!"); + logger.LogInformation("Sync received!"); foreach (var roomResp in sync.Rooms?.Join ?? []) { - if (roomResp.Value.Timeline?.Events is null or { Count: > 5 }) continue; + if (roomResp.Value.Timeline?.Events is null) continue; foreach (var @event in roomResp.Value.Timeline.Events) { @event.RoomId = roomResp.Key; + if (config.SelfCommandsOnly && @event.Sender != hs.WhoAmI.UserId) continue; + if (@event.OriginServerTs < _startupTime) continue; // ignore events older than startup time + try { - // var room = _hs.GetRoom(@event.RoomId); - // _logger.LogInformation(eventResponse.ToJson(indent: false)); if (@event is { Type: "m.room.message", TypedContent: RoomMessageEventContent message }) if (message is { MessageType: "m.text" }) { var usedPrefix = await GetUsedPrefix(@event); if (usedPrefix is null) return; var res = await InvokeCommand(@event, usedPrefix); - await (_commandResultHandler?.Invoke(res) ?? HandleResult(res)); + await (commandResultHandler?.Invoke(res) ?? HandleResult(res)); } } catch (Exception e) { - _logger.LogError(e, "Error in command listener!"); + logger.LogError(e, "Error in command listener!"); Console.WriteLine(@event.ToJson(ignoreNull: false, indent: true)); var fakeResult = new CommandResult() { Result = CommandResult.CommandResultType.Failure_Exception, Exception = e, Success = false, Context = new() { - Homeserver = _hs, + Homeserver = hs, CommandName = "[CommandListener.SyncHandler]", - Room = _hs.GetRoom(roomResp.Key), + Room = hs.GetRoom(roomResp.Key), Args = [], MessageEvent = @event } }; - await (_commandResultHandler?.Invoke(fakeResult) ?? HandleResult(fakeResult)); + await (commandResultHandler?.Invoke(fakeResult) ?? HandleResult(fakeResult)); } } } @@ -103,24 +110,24 @@ public class CommandListenerHostedService : IHostedService { /// <summary>Triggered when the application host is performing a graceful shutdown.</summary> /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param> public async Task StopAsync(CancellationToken cancellationToken) { - _logger.LogInformation("Shutting down command listener!"); + logger.LogInformation("Shutting down command listener!"); if (_listenerTask is null) { - _logger.LogError("Could not shut down command listener task because it was null!"); + logger.LogError("Could not shut down command listener task because it was null!"); return; } await _cts.CancelAsync(); } - private async Task<string?> GetUsedPrefix(StateEventResponse evt) { + private async Task<string?> GetUsedPrefix(MatrixEventResponse evt) { var messageContent = evt.TypedContent as RoomMessageEventContent; var message = messageContent!.BodyWithoutReplyFallback; - var prefix = _config.Prefixes.OrderByDescending(x => x.Length).FirstOrDefault(message.StartsWith); - if (prefix is null && _config.MentionPrefix) { - var profile = await _hs.GetProfileAsync(_hs.WhoAmI.UserId); - var roomProfile = await _hs.GetRoom(evt.RoomId!).GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, _hs.WhoAmI.UserId); - if (message.StartsWith(_hs.WhoAmI.UserId + ": ")) prefix = profile.DisplayName + ": "; // `@bot:server.xyz: ` - else if (message.StartsWith(_hs.WhoAmI.UserId + " ")) prefix = profile.DisplayName + " "; // `@bot:server.xyz ` + var prefix = config.Prefixes.OrderByDescending(x => x.Length).FirstOrDefault(message.StartsWith); + if (prefix is null && config.MentionPrefix) { + var profile = await hs.GetProfileAsync(hs.WhoAmI.UserId); + var roomProfile = await hs.GetRoom(evt.RoomId!).GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, hs.WhoAmI.UserId); + if (message.StartsWith(hs.WhoAmI.UserId + ": ")) prefix = profile.DisplayName + ": "; // `@bot:server.xyz: ` + else if (message.StartsWith(hs.WhoAmI.UserId + " ")) prefix = profile.DisplayName + " "; // `@bot:server.xyz ` else if (!string.IsNullOrWhiteSpace(roomProfile?.DisplayName) && message.StartsWith(roomProfile.DisplayName + ": ")) prefix = roomProfile.DisplayName + ": "; // `local bot: ` else if (!string.IsNullOrWhiteSpace(roomProfile?.DisplayName) && message.StartsWith(roomProfile.DisplayName + " ")) @@ -132,9 +139,9 @@ public class CommandListenerHostedService : IHostedService { return prefix; } - private async Task<CommandResult> InvokeCommand(StateEventResponse evt, string usedPrefix) { + private async Task<CommandResult> InvokeCommand(MatrixEventResponse evt, string usedPrefix) { var message = evt.TypedContent as RoomMessageEventContent; - var room = _hs.GetRoom(evt.RoomId!); + var room = hs.GetRoom(evt.RoomId!); var commandWithoutPrefix = message.BodyWithoutReplyFallback[usedPrefix.Length..].Trim(); var usedCommand = _commands @@ -143,12 +150,12 @@ public class CommandListenerHostedService : IHostedService { .FirstOrDefault(commandWithoutPrefix.StartsWith); var args = usedCommand == null || commandWithoutPrefix.Length <= usedCommand.Length - ? [] - : commandWithoutPrefix[(usedCommand.Length + 1)..].Split(' '); + ? [] + : commandWithoutPrefix[(usedCommand.Length + 1)..].Split(' ').SelectMany(x => x.Split('\n')).ToArray(); var ctx = new CommandContext { Room = room, MessageEvent = evt, - Homeserver = _hs, + Homeserver = hs, Args = args, CommandName = usedCommand ?? commandWithoutPrefix.Split(' ')[0] }; diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
index ad78779..99491a8 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using LibMatrix.Filters; using LibMatrix.Helpers; using LibMatrix.Homeservers; +using LibMatrix.Utilities.Bot.Configuration; using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -12,16 +13,17 @@ namespace LibMatrix.Utilities.Bot.Services; public class InviteHandlerHostedService( ILogger<InviteHandlerHostedService> logger, AuthenticatedHomeserverGeneric hs, - InviteHandlerHostedService.InviteListenerSyncConfiguration listenerSyncConfiguration, + LibMatrixBotConfiguration botConfig, + InviteListenerConfiguration config, Func<RoomInviteContext, Task> inviteHandler ) : IHostedService { private Task? _listenerTask; private CancellationTokenSource _cts = new(); private readonly SyncHelper _syncHelper = new(hs, logger) { - Timeout = listenerSyncConfiguration.Timeout ?? 30_000, - MinimumDelay = listenerSyncConfiguration.MinimumSyncTime ?? new(0), - SetPresence = listenerSyncConfiguration.Presence ?? "online" + Timeout = config.SyncConfiguration.Timeout ?? 30_000, + MinimumDelay = config.SyncConfiguration.MinimumSyncTime ?? TimeSpan.Zero, + SetPresence = config.SyncConfiguration.Presence ?? botConfig.Presence }; /// <summary>Triggered when the application host is ready to start the service.</summary> @@ -34,8 +36,8 @@ public class InviteHandlerHostedService( private async Task? Run(CancellationToken cancellationToken) { logger.LogInformation("Starting invite listener!"); var nextBatchFile = $"inviteHandler.{hs.WhoAmI.UserId}.{hs.WhoAmI.DeviceId}.nextBatch"; - if (listenerSyncConfiguration.Filter is not null) { - _syncHelper.Filter = listenerSyncConfiguration.Filter; + if (config.SyncConfiguration.Filter is not null) { + _syncHelper.Filter = config.SyncConfiguration.Filter; } else { _syncHelper.FilterId = await hs.NamedCaches.FilterCache.GetOrSetValueAsync("gay.rory.libmatrix.utilities.bot.invite_listener_syncfilter.dev0", new SyncFilter() { @@ -51,7 +53,7 @@ public class InviteHandlerHostedService( }); } - if (File.Exists(nextBatchFile) && !listenerSyncConfiguration.InitialSyncOnStartup) { + if (File.Exists(nextBatchFile) && !config.SyncConfiguration.InitialSyncOnStartup) { _syncHelper.Since = await File.ReadAllTextAsync(nextBatchFile, cancellationToken); } @@ -70,7 +72,7 @@ public class InviteHandlerHostedService( await inviteHandler(inviteEventArgs); }); - if (!listenerSyncConfiguration.InitialSyncOnStartup) + if (!config.SyncConfiguration.InitialSyncOnStartup) _syncHelper.SyncReceivedHandlers.Add(sync => File.WriteAllTextAsync(nextBatchFile, sync.NextBatch, cancellationToken)); await _syncHelper.RunSyncLoopAsync(cancellationToken: _cts.Token); } @@ -86,17 +88,4 @@ public class InviteHandlerHostedService( await _cts.CancelAsync(); } - - - - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Configuration")] - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Configuration")] - public class InviteListenerSyncConfiguration { - public InviteListenerSyncConfiguration(IConfiguration config) => config.GetSection("LibMatrixBot:InviteHandler:SyncConfiguration").Bind(this); - public SyncFilter? Filter { get; set; } - public TimeSpan? MinimumSyncTime { get; set; } - public int? Timeout { get; set; } - public string? Presence { get; set; } - public bool InitialSyncOnStartup { get; set; } - } } \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/deps.json b/Utilities/LibMatrix.Utilities.Bot/deps.json new file mode 100644
index 0000000..566f6a9 --- /dev/null +++ b/Utilities/LibMatrix.Utilities.Bot/deps.json
@@ -0,0 +1,142 @@ +[ + { + "pname": "Microsoft.Extensions.Configuration", + "version": "10.0.0", + "hash": "sha256-MsLskVPpkCvov5+DWIaALCt1qfRRX4u228eHxvpE0dg=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.0", + "hash": "sha256-GcgrnTAieCV7AVT13zyOjfwwL86e99iiO/MiMOxPGG0=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Binder", + "version": "10.0.0", + "hash": "sha256-YSiWoA3VQR22k6+bSEAUqeG7UDzZlJfHWDTubUO5V8U=" + }, + { + "pname": "Microsoft.Extensions.Configuration.CommandLine", + "version": "10.0.0", + "hash": "sha256-ldTiRFqnv8/pA0gl6UR+4DDGAIZOf9+MhaLWOuKOXOI=" + }, + { + "pname": "Microsoft.Extensions.Configuration.EnvironmentVariables", + "version": "10.0.0", + "hash": "sha256-UayfeqrAmNyfOkuhcBKfj8UpjQqV/ZMqWrDyxCSG1MA=" + }, + { + "pname": "Microsoft.Extensions.Configuration.FileExtensions", + "version": "10.0.0", + "hash": "sha256-rN+3rqrHiTaBfHgP+E4dA8Qm2cFJPfbEcd93yKLsqlQ=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Json", + "version": "10.0.0", + "hash": "sha256-VCFukgsxiQ2MFGE6RDMFTGopBHbcZL2t0ER7ENaFXRY=" + }, + { + "pname": "Microsoft.Extensions.Configuration.UserSecrets", + "version": "10.0.0", + "hash": "sha256-uIoIpbDPRMfFqT8Y6j/wHbFCAly6H1N9qpxnomRbHIo=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.0", + "hash": "sha256-LYm9hVlo/R9c2aAKHsDYJ5vY9U0+3Jvclme3ou3BtvQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.0", + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics", + "version": "10.0.0", + "hash": "sha256-o7QkCisEcFIh227qBUfWFci2ns4cgEpLqpX7YvHGToQ=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics.Abstractions", + "version": "10.0.0", + "hash": "sha256-cix7QxQ/g3sj6reXu3jn0cRv2RijzceaLLkchEGTt5E=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Abstractions", + "version": "10.0.0", + "hash": "sha256-CHDs2HCN8QcfuYQpgNVszZ5dfXFe4yS9K2GoQXecc20=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Physical", + "version": "10.0.0", + "hash": "sha256-2Rw/cwBO+/A3QY2IjN/c8Y0LhtC1qTBL7VdJiD1J2UQ=" + }, + { + "pname": "Microsoft.Extensions.FileSystemGlobbing", + "version": "10.0.0", + "hash": "sha256-ETfVTdsdBtp69EggLg/AARTQW4lLQYVdVldXIQrsjZA=" + }, + { + "pname": "Microsoft.Extensions.Hosting", + "version": "10.0.0", + "hash": "sha256-tY0g6lCy2yFprE+NmriiU6FGmwpzxV8LqE0ZFNKIwuM=" + }, + { + "pname": "Microsoft.Extensions.Hosting.Abstractions", + "version": "10.0.0", + "hash": "sha256-Sub3Thay/+eR84cEODk/nPh1oYIYtawvDX6r0duReqo=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.0", + "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.0", + "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Configuration", + "version": "10.0.0", + "hash": "sha256-7/TWO1aq8hdgbcTEKDBWIjgSC9KpFN3kRnMX+12bOkU=" + }, + { + "pname": "Microsoft.Extensions.Logging.Console", + "version": "10.0.0", + "hash": "sha256-Rsblo7GSMTOr43876KkdvqS6wU9Typ1yoFK3tL50CBk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Debug", + "version": "10.0.0", + "hash": "sha256-n/+KRVlsgKm17cJImaoAPHAObHpApW/hf6mMsQFGrvY=" + }, + { + "pname": "Microsoft.Extensions.Logging.EventLog", + "version": "10.0.0", + "hash": "sha256-4RJ2r80RtI3QUAhCAYbGnA0YcTmouqtZvQU9o3CrB38=" + }, + { + "pname": "Microsoft.Extensions.Logging.EventSource", + "version": "10.0.0", + "hash": "sha256-tqC13Qwkf4x14iGxOYlXTyeoN8KPVX+mupv2LdpzGHo=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.0", + "hash": "sha256-j5MOqZSKeUtxxzmZjzZMGy0vELHdvPraqwTQQQNVsYA=" + }, + { + "pname": "Microsoft.Extensions.Options.ConfigurationExtensions", + "version": "10.0.0", + "hash": "sha256-XGAs5DxMvWnmjX8dqRwKY0vsuS40SHvsfJqB1rO4L7k=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.0", + "hash": "sha256-Dup08KcptLjlnpN5t5//+p4n8FUTgRAq4n/w1s6us+I=" + }, + { + "pname": "System.Diagnostics.EventLog", + "version": "10.0.0", + "hash": "sha256-pN3tld926Fp0n5ZNjjzIJviUQrynlOAB0vhc1aoso6E=" + } +]