about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--LibMatrix/Filters/SyncFilter.cs15
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs1
-rw-r--r--LibMatrix/RoomTypes/PolicyRoom.cs8
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs6
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Interfaces/IRoomInviteHandler.cs5
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs71
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs33
7 files changed, 110 insertions, 29 deletions
diff --git a/LibMatrix/Filters/SyncFilter.cs b/LibMatrix/Filters/SyncFilter.cs

index 787ffa7..8c66656 100644 --- a/LibMatrix/Filters/SyncFilter.cs +++ b/LibMatrix/Filters/SyncFilter.cs
@@ -34,6 +34,15 @@ public class SyncFilter { [JsonPropertyName("include_leave")] public bool? IncludeLeave { get; set; } + private static readonly RoomFilter Empty = new() { + Rooms = [], + IncludeLeave = false, + AccountData = StateFilter.Empty, + Ephemeral = StateFilter.Empty, + State = StateFilter.Empty, + Timeline = StateFilter.Empty, + }; + public class StateFilter( bool? containsUrl = null, bool? includeRedundantMembers = null, @@ -65,6 +74,8 @@ public class SyncFilter { [JsonPropertyName("unread_thread_notifications")] public bool? UnreadThreadNotifications { get; set; } = unreadThreadNotifications; + + public static readonly StateFilter Empty = new(limit: 0, senders: [], types: [], rooms: []); } } @@ -83,5 +94,7 @@ public class SyncFilter { [JsonPropertyName("not_senders")] public List<string>? NotSenders { get; set; } = notSenders; + + public static readonly EventFilter Empty = new(limit: 0, senders: [], types: []); } -} \ No newline at end of file +} diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index cc3c0a1..55fa42c 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -304,7 +304,6 @@ public class GenericRoom { public Task<RoomPowerLevelEventContent?> GetPowerLevelsAsync() => GetStateAsync<RoomPowerLevelEventContent>("m.room.power_levels"); - [Obsolete("This method will be merged into GetNameAsync() in the future.")] public async Task<string> GetNameOrFallbackAsync(int maxMemberNames = 2) { try { var name = await GetNameAsync(); diff --git a/LibMatrix/RoomTypes/PolicyRoom.cs b/LibMatrix/RoomTypes/PolicyRoom.cs
index e0b6edb..c6eec63 100644 --- a/LibMatrix/RoomTypes/PolicyRoom.cs +++ b/LibMatrix/RoomTypes/PolicyRoom.cs
@@ -8,10 +8,10 @@ namespace LibMatrix.RoomTypes; public class PolicyRoom(AuthenticatedHomeserverGeneric homeserver, string roomId) : GenericRoom(homeserver, roomId) { public const string TypeName = "support.feline.policy.lists.msc.v1"; - private static readonly FrozenSet<string> UserPolicyEventTypes = EventContent.GetMatchingEventTypes<UserPolicyRuleEventContent>().ToFrozenSet(); - private static readonly FrozenSet<string> ServerPolicyEventTypes = EventContent.GetMatchingEventTypes<ServerPolicyRuleEventContent>().ToFrozenSet(); - private static readonly FrozenSet<string> RoomPolicyEventTypes = EventContent.GetMatchingEventTypes<RoomPolicyRuleEventContent>().ToFrozenSet(); - private static readonly FrozenSet<string> SpecPolicyEventTypes = [..UserPolicyEventTypes, ..ServerPolicyEventTypes, ..RoomPolicyEventTypes]; + public static readonly FrozenSet<string> UserPolicyEventTypes = EventContent.GetMatchingEventTypes<UserPolicyRuleEventContent>().ToFrozenSet(); + public static readonly FrozenSet<string> ServerPolicyEventTypes = EventContent.GetMatchingEventTypes<ServerPolicyRuleEventContent>().ToFrozenSet(); + public static readonly FrozenSet<string> RoomPolicyEventTypes = EventContent.GetMatchingEventTypes<RoomPolicyRuleEventContent>().ToFrozenSet(); + public static readonly FrozenSet<string> SpecPolicyEventTypes = [..UserPolicyEventTypes, ..ServerPolicyEventTypes, ..RoomPolicyEventTypes]; public async IAsyncEnumerable<StateEventResponse> GetPoliciesAsync() { var fullRoomState = GetFullStateAsync(); diff --git a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
index f7ef317..4947394 100644 --- a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs +++ b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
@@ -69,16 +69,16 @@ public class BotInstaller(IServiceCollection services) { return this; } - public BotInstaller WithInviteHandler(Func<InviteHandlerHostedService.InviteEventArgs, Task> inviteHandler) { + 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, InviteHandlerHostedService.IInviteHandler { + public BotInstaller WithInviteHandler<T>() where T : class, IRoomInviteHandler { services.AddSingleton<T>(); - services.AddSingleton<Func<InviteHandlerHostedService.InviteEventArgs, Task>>(sp => sp.GetRequiredService<T>().HandleInviteAsync); + services.AddSingleton<Func<RoomInviteContext, Task>>(sp => sp.GetRequiredService<T>().HandleInviteAsync); services.AddHostedService<InviteHandlerHostedService>(); services.AddSingleton<InviteHandlerHostedService.InviteListenerSyncConfiguration>(); return this; diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/IRoomInviteHandler.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/IRoomInviteHandler.cs new file mode 100644
index 0000000..a25ca4a --- /dev/null +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/IRoomInviteHandler.cs
@@ -0,0 +1,5 @@ +namespace LibMatrix.Utilities.Bot.Interfaces; + +public interface IRoomInviteHandler { + public Task HandleInviteAsync(RoomInviteContext invite); +} \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs new file mode 100644
index 0000000..380c1c7 --- /dev/null +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/RoomInviteContext.cs
@@ -0,0 +1,71 @@ +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.Homeservers; +using LibMatrix.Responses; + +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 SyncResponse.RoomsDataStructure.InvitedRoomDataStructure InviteData { get; init; } + + public async Task<string> TryGetInviterNameAsync() { + var name = InviteData.InviteState?.Events? + .FirstOrDefault(evt => evt is { Type: RoomMemberEventContent.EventId } && evt.StateKey == MemberEvent.Sender)? + .ContentAs<RoomMemberEventContent>()?.DisplayName; + + if (!string.IsNullOrWhiteSpace(name)) + return name; + + try { + await Homeserver.GetProfileAsync(MemberEvent.Sender!); + } + catch { + //ignored + } + + return MemberEvent.Sender!; + } + + public async Task<string> TryGetRoomNameAsync() { + // try to get room name from invite state + var name = InviteData.InviteState?.Events? + .FirstOrDefault(evt => evt is { Type: RoomNameEventContent.EventId, StateKey: "" })? + .ContentAs<RoomNameEventContent>()?.Name; + + if (!string.IsNullOrWhiteSpace(name)) + return name; + + // try to get room alias + var alias = InviteData.InviteState?.Events? + .FirstOrDefault(evt => evt is { Type: RoomCanonicalAliasEventContent.EventId, StateKey: "" })? + .ContentAs<RoomCanonicalAliasEventContent>()?.Alias; + + if (!string.IsNullOrWhiteSpace(alias)) + return alias; + + // try to get room name via public previews + try { + name = await Homeserver.GetRoom(RoomId).GetNameOrFallbackAsync(); + if (name != RoomId && !string.IsNullOrWhiteSpace(name)) + return name; + } + catch { + //ignored + } + + // fallback to room alias via public previews + try { + alias = (await Homeserver.GetRoom(RoomId).GetCanonicalAliasAsync())?.Alias; + if (!string.IsNullOrWhiteSpace(alias)) + return alias; + } + catch { + //ignored + } + + // fall back to room ID + return RoomId; + } +} \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
index 9a5e3d9..ad78779 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/InviteListenerHostedService.cs
@@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using LibMatrix.Filters; using LibMatrix.Helpers; using LibMatrix.Homeservers; -using LibMatrix.Responses; +using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -13,9 +13,10 @@ public class InviteHandlerHostedService( ILogger<InviteHandlerHostedService> logger, AuthenticatedHomeserverGeneric hs, InviteHandlerHostedService.InviteListenerSyncConfiguration listenerSyncConfiguration, - Func<InviteHandlerHostedService.InviteEventArgs, Task> inviteHandler + Func<RoomInviteContext, Task> inviteHandler ) : IHostedService { private Task? _listenerTask; + private CancellationTokenSource _cts = new(); private readonly SyncHelper _syncHelper = new(hs, logger) { Timeout = listenerSyncConfiguration.Timeout ?? 30_000, @@ -37,14 +38,15 @@ public class InviteHandlerHostedService( _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: ["*"]), + _syncHelper.FilterId = await hs.NamedCaches.FilterCache.GetOrSetValueAsync("gay.rory.libmatrix.utilities.bot.invite_listener_syncfilter.dev0", new SyncFilter() { + AccountData = SyncFilter.EventFilter.Empty, + Presence = SyncFilter.EventFilter.Empty, 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: []), + AccountData = SyncFilter.RoomFilter.StateFilter.Empty, + Ephemeral = SyncFilter.RoomFilter.StateFilter.Empty, + State = SyncFilter.RoomFilter.StateFilter.Empty, Timeline = new SyncFilter.RoomFilter.StateFilter(types: [], notSenders: [hs.WhoAmI.UserId]), + IncludeLeave = false } }); } @@ -55,7 +57,7 @@ public class InviteHandlerHostedService( _syncHelper.InviteReceivedHandlers.Add(async invite => { logger.LogInformation("Received invite to room {}", invite.Key); - var inviteEventArgs = new InviteEventArgs() { + var inviteEventArgs = new RoomInviteContext() { RoomId = invite.Key, InviteData = invite.Value, MemberEvent = invite.Value.InviteState?.Events?.First(x => x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId) @@ -70,7 +72,7 @@ public class InviteHandlerHostedService( if (!listenerSyncConfiguration.InitialSyncOnStartup) _syncHelper.SyncReceivedHandlers.Add(sync => File.WriteAllTextAsync(nextBatchFile, sync.NextBatch, cancellationToken)); - await _syncHelper.RunSyncLoopAsync(cancellationToken: cancellationToken); + await _syncHelper.RunSyncLoopAsync(cancellationToken: _cts.Token); } /// <summary>Triggered when the application host is performing a graceful shutdown.</summary> @@ -82,19 +84,10 @@ public class InviteHandlerHostedService( return; } - await _listenerTask.WaitAsync(cancellationToken); + await _cts.CancelAsync(); } - public class InviteEventArgs { - 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")]