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")]
|