diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
index 4594f7d..24633c5 100644
--- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs
+++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -94,7 +94,7 @@ public class MatrixHttpClient {
request.Options.Set(new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse"), true);
if (LogRequests)
- Console.WriteLine("Sending " + request.Summarise(includeHeaders: true, includeQuery: true, includeContentIfText: true, hideHeaders: ["Accept"]));
+ Console.WriteLine("Sending " + request.Summarise(includeHeaders: true, includeQuery: true, includeContentIfText: false, hideHeaders: ["Accept"]));
HttpResponseMessage? responseMessage;
try {
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index adcc714..4cbe73f 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -73,6 +73,8 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
else if (Filter is not null)
_filterId = (await homeserver.UploadFilterAsync(Filter)).FilterId;
else _filterId = null;
+
+ _filterIsDirty = false;
}
public async Task<SyncResponse?> SyncAsync(CancellationToken? cancellationToken = null) {
@@ -115,7 +117,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
// logger?.LogInformation("SyncHelper: Calling: {}", url);
try {
- SyncResponse? resp = null;
+ SyncResponse? resp;
if (UseInternalStreamingSync) {
resp = await homeserver.ClientHttpClient.GetFromJsonAsync<SyncResponse>(url, cancellationToken: cancellationToken ?? CancellationToken.None);
logger?.LogInformation("Got sync response: ~{} bytes, {} elapsed", resp.ToJson(false, true, true).Length, sw.Elapsed);
diff --git a/LibMatrix/Homeservers/Extensions/NamedCaches/NamedFilterCache.cs b/LibMatrix/Homeservers/Extensions/NamedCaches/NamedFilterCache.cs
index 76533a4..e3c5943 100644
--- a/LibMatrix/Homeservers/Extensions/NamedCaches/NamedFilterCache.cs
+++ b/LibMatrix/Homeservers/Extensions/NamedCaches/NamedFilterCache.cs
@@ -1,3 +1,5 @@
+using System.Text.Json.Nodes;
+using ArcaneLibs.Extensions;
using LibMatrix.Filters;
using LibMatrix.Utilities;
@@ -16,7 +18,13 @@ public class NamedFilterCache(AuthenticatedHomeserverGeneric hs) : NamedCache<st
public async Task<string> GetOrSetValueAsync(string key, SyncFilter? filter = null) {
var existingValue = await GetValueAsync(key);
if (existingValue != null) {
- return existingValue;
+ try {
+ var existingFilter = await hs.GetFilterAsync(existingValue);
+ return existingValue;
+ }
+ catch {
+ // ignored
+ }
}
if (filter is null) {
diff --git a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
index 0d755a1..f7ef317 100644
--- a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
+++ b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
@@ -72,6 +72,7 @@ public class BotInstaller(IServiceCollection services) {
public BotInstaller WithInviteHandler(Func<InviteHandlerHostedService.InviteEventArgs, Task> inviteHandler) {
services.AddSingleton(inviteHandler);
services.AddHostedService<InviteHandlerHostedService>();
+ services.AddSingleton<InviteHandlerHostedService.InviteListenerSyncConfiguration>();
return this;
}
@@ -79,6 +80,7 @@ public class BotInstaller(IServiceCollection services) {
services.AddSingleton<T>();
services.AddSingleton<Func<InviteHandlerHostedService.InviteEventArgs, Task>>(sp => sp.GetRequiredService<T>().HandleInviteAsync);
services.AddHostedService<InviteHandlerHostedService>();
+ services.AddSingleton<InviteHandlerHostedService.InviteListenerSyncConfiguration>();
return this;
}
diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
index f819cc3..a9e4577 100644
--- a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
+++ b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
@@ -1,73 +1,84 @@
+using System.Diagnostics.CodeAnalysis;
using LibMatrix.Filters;
using LibMatrix.Helpers;
using LibMatrix.Homeservers;
using LibMatrix.Responses;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace LibMatrix.Utilities.Bot.Services;
-public class InviteHandlerHostedService : IHostedService {
- private readonly AuthenticatedHomeserverGeneric _hs;
- private readonly ILogger<InviteHandlerHostedService> _logger;
- private readonly Func<InviteEventArgs, Task> _inviteHandler;
-
+public class InviteHandlerHostedService(
+ ILogger<InviteHandlerHostedService> logger,
+ AuthenticatedHomeserverGeneric hs,
+ InviteHandlerHostedService.InviteListenerSyncConfiguration listenerSyncConfiguration,
+ Func<InviteHandlerHostedService.InviteEventArgs, Task> inviteHandler
+) : IHostedService {
private Task? _listenerTask;
- public InviteHandlerHostedService(AuthenticatedHomeserverGeneric hs, ILogger<InviteHandlerHostedService> logger,
- Func<InviteEventArgs, Task> inviteHandler) {
- logger.LogInformation("{} instantiated!", GetType().Name);
- _hs = hs;
- _logger = logger;
- _inviteHandler = inviteHandler;
- }
+ private SyncHelper syncHelper = new SyncHelper(hs, logger) {
+ Timeout = listenerSyncConfiguration.Timeout ?? 30_000,
+ MinimumDelay = listenerSyncConfiguration.MinimumSyncTime ?? new(0),
+ SetPresence = listenerSyncConfiguration.Presence ?? "online"
+ };
/// <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(cancellationToken);
- _logger.LogInformation("Command listener started (StartAsync)!");
+ logger.LogInformation("Command listener started (StartAsync)!");
return Task.CompletedTask;
}
private async Task? Run(CancellationToken cancellationToken) {
- _logger.LogInformation("Starting invite 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 invite listener!");
+ var nextBatchFile = $"inviteHandler.{hs.WhoAmI.UserId}.{hs.WhoAmI.DeviceId}.nextBatch";
+ if (listenerSyncConfiguration.Filter is not null) {
+ syncHelper.Filter = listenerSyncConfiguration.Filter;
+ }
+ else {
+ syncHelper.FilterId = await hs.NamedCaches.FilterCache.GetOrSetValueAsync("gay.rory.libmatrix.utilities.bot.invite_listener_syncfilter.dev", new SyncFilter() {
+ AccountData = new SyncFilter.EventFilter(types: [], limit: 1),
+ Presence = new SyncFilter.EventFilter(types: ["*"]),
+ Room = new SyncFilter.RoomFilter {
+ AccountData = new SyncFilter.RoomFilter.StateFilter(types: [], limit: 1),
+ Ephemeral = new SyncFilter.RoomFilter.StateFilter(types: [], limit: 1),
+ State = new SyncFilter.RoomFilter.StateFilter(types: []),
+ Timeline = new SyncFilter.RoomFilter.StateFilter(types: [], notSenders: [hs.WhoAmI.UserId]),
+ }
+ });
+ }
+
+ if (File.Exists(nextBatchFile)) {
+ syncHelper.Since = await File.ReadAllTextAsync(nextBatchFile, cancellationToken);
+ }
- var syncHelper = new SyncHelper(_hs, _logger) {
- Timeout = 300_000,
- FilterId = filter
- };
syncHelper.InviteReceivedHandlers.Add(async invite => {
- _logger.LogInformation("Received invite to room {}", invite.Key);
-
+ logger.LogInformation("Received invite to room {}", invite.Key);
var inviteEventArgs = new InviteEventArgs() {
RoomId = invite.Key,
InviteData = invite.Value,
- MemberEvent = invite.Value.InviteState?.Events?.First(x => x.Type == "m.room.member" && x.StateKey == _hs.WhoAmI.UserId),
- Homeserver = _hs
+ MemberEvent = invite.Value.InviteState?.Events?.First(x => x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId)
+ ?? throw new LibMatrixException() {
+ ErrorCode = LibMatrixException.ErrorCodes.M_NOT_FOUND,
+ Error = "Room invite doesn't contain a membership event!"
+ },
+ Homeserver = hs
};
- await _inviteHandler(inviteEventArgs);
+ await inviteHandler(inviteEventArgs);
});
+ syncHelper.SyncReceivedHandlers.Add(sync => File.WriteAllTextAsync(nextBatchFile, sync.NextBatch, cancellationToken));
await syncHelper.RunSyncLoopAsync(cancellationToken: cancellationToken);
}
/// <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 invite listener!");
+ logger.LogInformation("Shutting down invite listener!");
if (_listenerTask is null) {
- _logger.LogError("Could not shut down invite listener task because it was null!");
+ logger.LogError("Could not shut down invite listener task because it was null!");
return;
}
@@ -75,13 +86,23 @@ public class InviteHandlerHostedService : IHostedService {
}
public class InviteEventArgs {
- public string RoomId { get; set; }
- public AuthenticatedHomeserverGeneric Homeserver { get; set; }
- public StateEventResponse MemberEvent { get; set; }
- public SyncResponse.RoomsDataStructure.InvitedRoomDataStructure InviteData { get; set; }
+ public required string RoomId { get; init; }
+ public required AuthenticatedHomeserverGeneric Homeserver { get; init; }
+ public required StateEventResponse MemberEvent { get; init; }
+ public required SyncResponse.RoomsDataStructure.InvitedRoomDataStructure InviteData { get; init; }
}
public interface IInviteHandler {
public Task HandleInviteAsync(InviteEventArgs invite);
}
+
+ [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; }
+ }
}
\ No newline at end of file
|