From f5447484512d726f4403f0d7725777d0a95601fb Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Tue, 19 Sep 2023 00:16:36 +0200 Subject: Add more stuff, add unit tests --- .gitignore | 1 + .../Bot/Commands/CmdCommand.cs | 10 +- .../Bot/Commands/HelpCommand.cs | 4 +- .../Bot/Commands/PingCommand.cs | 4 +- .../Bot/Interfaces/CommandContext.cs | 2 +- ExampleBots/LibMatrix.ExampleBot/Bot/MRUBot.cs | 17 +- .../Bot/StartupTasks/ServerRoomSizeCalulator.cs | 3 +- .../Bot/Commands/BanMediaCommand.cs | 34 +-- ExampleBots/MediaModeratorPoC/Bot/MediaModBot.cs | 37 +-- .../Bot/Commands/CreateSystemCommand.cs | 10 +- .../PluralContactBotPoC/Bot/PluralContactBot.cs | 23 +- ExampleBots/PluralContactBotPoC/Program.cs | 1 - .../EventTypes/Common/MjolnirShortcodeEventData.cs | 11 + LibMatrix/EventTypes/Common/RoomEmotesEventData.cs | 26 ++ LibMatrix/EventTypes/Spec/RoomMessageEventData.cs | 31 ++ .../Spec/State/CanonicalAliasEventContent.cs | 13 + .../EventTypes/Spec/State/GuestAccessEventData.cs | 16 + .../Spec/State/HistoryVisibilityEventData.cs | 11 + .../EventTypes/Spec/State/JoinRulesEventData.cs | 30 ++ .../Spec/State/PolicyRuleStateEventData.cs | 56 ++++ .../Spec/State/PresenceStateEventData.cs | 21 ++ .../Spec/State/ProfileResponseEventData.cs | 12 + .../EventTypes/Spec/State/RoomAliasEventData.cs | 11 + .../EventTypes/Spec/State/RoomAvatarEventData.cs | 28 ++ .../EventTypes/Spec/State/RoomCreateEventData.cs | 27 ++ .../Spec/State/RoomEncryptionEventData.cs | 15 + .../EventTypes/Spec/State/RoomMemberEventData.cs | 29 ++ .../EventTypes/Spec/State/RoomNameEventData.cs | 11 + .../EventTypes/Spec/State/RoomPinnedEventData.cs | 11 + .../Spec/State/RoomPowerLevelEventData.cs | 56 ++++ .../EventTypes/Spec/State/RoomTopicEventData.cs | 12 + .../EventTypes/Spec/State/RoomTypingEventData.cs | 11 + .../EventTypes/Spec/State/ServerACLEventData.cs | 17 ++ .../EventTypes/Spec/State/SpaceChildEventData.cs | 15 + .../EventTypes/Spec/State/SpaceParentEventData.cs | 14 + LibMatrix/EventTypes/UnknownStateEventData.cs | 7 + LibMatrix/Extensions/HttpClientExtensions.cs | 12 +- LibMatrix/Extensions/StringExtensions.cs | 13 - LibMatrix/Helpers/MediaResolver.cs | 7 - LibMatrix/Helpers/MessageFormatter.cs | 2 +- LibMatrix/Helpers/SyncHelper.cs | 18 +- .../Homeservers/AuthenticatedHomeserverGeneric.cs | 23 +- .../AuthenticatedHomeserverMxApiExtended.cs | 2 +- .../Homeservers/AuthenticatedHomeserverSynapse.cs | 2 +- LibMatrix/Homeservers/RemoteHomeServer.cs | 27 +- LibMatrix/Interfaces/IStateEventType.cs | 4 +- LibMatrix/Responses/CreateRoomRequest.cs | 2 +- LibMatrix/Responses/StateEventResponse.cs | 5 + LibMatrix/RoomTypes/GenericRoom.cs | 33 +- LibMatrix/RoomTypes/SpaceRoom.cs | 6 +- LibMatrix/Services/HomeserverProviderService.cs | 4 +- LibMatrix/Services/HomeserverResolverService.cs | 2 +- LibMatrix/Services/TieredStorageService.cs | 6 +- LibMatrix/StateEvent.cs | 5 +- .../Common/MjolnirShortcodeEventData.cs | 11 - .../StateEventTypes/Common/RoomEmotesEventData.cs | 26 -- .../Spec/CanonicalAliasEventContent.cs | 13 - .../StateEventTypes/Spec/GuestAccessEventData.cs | 16 - .../Spec/HistoryVisibilityEventData.cs | 11 - .../StateEventTypes/Spec/JoinRulesEventData.cs | 30 -- .../Spec/PolicyRuleStateEventData.cs | 56 ---- .../StateEventTypes/Spec/PresenceStateEventData.cs | 21 -- .../Spec/ProfileResponseEventData.cs | 12 - .../StateEventTypes/Spec/RoomAliasEventData.cs | 11 - .../StateEventTypes/Spec/RoomAvatarEventData.cs | 28 -- .../StateEventTypes/Spec/RoomCreateEventData.cs | 27 -- .../Spec/RoomEncryptionEventData.cs | 15 - .../StateEventTypes/Spec/RoomMemberEventData.cs | 29 -- .../StateEventTypes/Spec/RoomMessageEventData.cs | 32 -- .../StateEventTypes/Spec/RoomNameEventData.cs | 11 - .../StateEventTypes/Spec/RoomPinnedEventData.cs | 11 - .../Spec/RoomPowerLevelEventData.cs | 56 ---- .../StateEventTypes/Spec/RoomTopicEventData.cs | 12 - .../StateEventTypes/Spec/RoomTypingEventData.cs | 11 - .../StateEventTypes/Spec/ServerACLEventData.cs | 17 -- .../StateEventTypes/Spec/SpaceChildEventData.cs | 15 - .../StateEventTypes/Spec/SpaceParentEventData.cs | 14 - LibMatrix/StateEventTypes/UnknownStateEventData.cs | 7 - Tests/LibMatrix.Tests/Config.cs | 17 ++ Tests/LibMatrix.Tests/Fixtures/TestFixture.cs | 37 +++ Tests/LibMatrix.Tests/LibMatrix.Tests.csproj | 13 +- Tests/LibMatrix.Tests/ResolverTest.cs | 10 - Tests/LibMatrix.Tests/Tests/AuthTests.cs | 52 ++++ Tests/LibMatrix.Tests/Tests/ResolverTest.cs | 54 ++++ Tests/LibMatrix.Tests/Tests/RoomTests.cs | 332 +++++++++++++++++++++ Tests/TestDataGenerator/Bot/DataFetcher.cs | 69 +++++ .../Bot/DataFetcherConfiguration.cs | 9 + Tests/TestDataGenerator/Program.cs | 31 ++ .../Properties/launchSettings.json | 26 ++ Tests/TestDataGenerator/TestDataGenerator.csproj | 32 ++ .../TestDataGenerator/appsettings.Development.json | 18 ++ Tests/TestDataGenerator/appsettings.json | 9 + .../AppServiceConfiguration.cs | 2 +- .../LibMatrix.Utilities.Bot/BotCommandInstaller.cs | 3 +- .../Commands/HelpCommand.cs | 8 +- .../Commands/PingCommand.cs | 8 +- .../LibMatrix.Utilities.Bot/FileStorageProvider.cs | 2 +- .../Interfaces/CommandContext.cs | 7 +- .../LibMatrix.Utilities.Bot/Interfaces/ICommand.cs | 2 +- .../Services/CommandListenerHostedService.cs | 12 +- 100 files changed, 1385 insertions(+), 670 deletions(-) create mode 100644 LibMatrix/EventTypes/Common/MjolnirShortcodeEventData.cs create mode 100644 LibMatrix/EventTypes/Common/RoomEmotesEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/RoomMessageEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/CanonicalAliasEventContent.cs create mode 100644 LibMatrix/EventTypes/Spec/State/GuestAccessEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/HistoryVisibilityEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/JoinRulesEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/PolicyRuleStateEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/PresenceStateEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/ProfileResponseEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomAliasEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomAvatarEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomEncryptionEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomMemberEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomNameEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomPinnedEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomTopicEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/RoomTypingEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/ServerACLEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs create mode 100644 LibMatrix/EventTypes/Spec/State/SpaceParentEventData.cs create mode 100644 LibMatrix/EventTypes/UnknownStateEventData.cs delete mode 100644 LibMatrix/Extensions/StringExtensions.cs delete mode 100644 LibMatrix/Helpers/MediaResolver.cs delete mode 100644 LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/CanonicalAliasEventContent.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/ProfileResponseEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs delete mode 100644 LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs delete mode 100644 LibMatrix/StateEventTypes/UnknownStateEventData.cs create mode 100644 Tests/LibMatrix.Tests/Config.cs create mode 100644 Tests/LibMatrix.Tests/Fixtures/TestFixture.cs delete mode 100644 Tests/LibMatrix.Tests/ResolverTest.cs create mode 100644 Tests/LibMatrix.Tests/Tests/AuthTests.cs create mode 100644 Tests/LibMatrix.Tests/Tests/ResolverTest.cs create mode 100644 Tests/LibMatrix.Tests/Tests/RoomTests.cs create mode 100644 Tests/TestDataGenerator/Bot/DataFetcher.cs create mode 100644 Tests/TestDataGenerator/Bot/DataFetcherConfiguration.cs create mode 100644 Tests/TestDataGenerator/Program.cs create mode 100644 Tests/TestDataGenerator/Properties/launchSettings.json create mode 100644 Tests/TestDataGenerator/TestDataGenerator.csproj create mode 100644 Tests/TestDataGenerator/appsettings.Development.json create mode 100644 Tests/TestDataGenerator/appsettings.json diff --git a/.gitignore b/.gitignore index 625aad4..fba66db 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ MatrixRoomUtils.Bot/bot_data/ appsettings.Local*.json appservice.yaml appservice.json +Tests/LibMatrix.Tests/appsettings.json diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/CmdCommand.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/CmdCommand.cs index 5b2828e..f3b4dde 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/CmdCommand.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/CmdCommand.cs @@ -1,6 +1,6 @@ using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; using LibMatrix.ExampleBot.Bot.Interfaces; -using LibMatrix.StateEventTypes.Spec; namespace LibMatrix.ExampleBot.Bot.Commands; @@ -18,7 +18,7 @@ public class CmdCommand : ICommand { cmd = cmd.Trim(); cmd += "\""; - await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent(body: $"Command being executed: `{cmd}`")); + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: $"Command being executed: `{cmd}`")); var output = ArcaneLibs.Util.GetCommandOutputAsync( Environment.OSVersion.Platform == PlatformID.Unix ? "/bin/sh" : "cmd.exe", @@ -27,7 +27,7 @@ public class CmdCommand : ICommand { // .Split("\n").ToList(); var msg = ""; - EventIdResponse? msgId = await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent { + EventIdResponse? msgId = await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent { FormattedBody = $"Waiting for command output...", Body = msg.RemoveAnsi(), Format = "m.notice" @@ -38,14 +38,14 @@ public class CmdCommand : ICommand { Console.WriteLine($"{@out.Length:0000} {@out}"); msg += @out + "\n"; if (lastSendTask.IsCompleted) - lastSendTask = ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent { + lastSendTask = ctx.Room.SendMessageEventAsync(new RoomMessageEventContent { FormattedBody = $"
\n{msg}\n
", Body = msg.RemoveAnsi(), Format = "org.matrix.custom.html" }); if (msg.Length > 31000) { await lastSendTask; - msgId = await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent { + msgId = await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent { FormattedBody = $"Waiting for command output...", Body = msg.RemoveAnsi(), Format = "m.notice" diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/HelpCommand.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/HelpCommand.cs index c750130..23c4fe2 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/HelpCommand.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/HelpCommand.cs @@ -1,6 +1,6 @@ using System.Text; +using LibMatrix.EventTypes.Spec; using LibMatrix.ExampleBot.Bot.Interfaces; -using LibMatrix.StateEventTypes.Spec; using Microsoft.Extensions.DependencyInjection; namespace LibMatrix.ExampleBot.Bot.Commands; @@ -17,6 +17,6 @@ public class HelpCommand(IServiceProvider services) : ICommand { sb.AppendLine($"- {command.Name}: {command.Description}"); } - await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent(body: sb.ToString())); + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: sb.ToString())); } } diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/PingCommand.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/PingCommand.cs index a261a59..ba242fe 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/PingCommand.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/Commands/PingCommand.cs @@ -1,5 +1,5 @@ +using LibMatrix.EventTypes.Spec; using LibMatrix.ExampleBot.Bot.Interfaces; -using LibMatrix.StateEventTypes.Spec; namespace LibMatrix.ExampleBot.Bot.Commands; @@ -8,6 +8,6 @@ public class PingCommand : ICommand { public string Description { get; } = "Pong!"; public async Task Invoke(CommandContext ctx) { - await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent(body: "pong!")); + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "pong!")); } } diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/Interfaces/CommandContext.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/Interfaces/CommandContext.cs index 3715cb6..9b6ef7a 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/Interfaces/CommandContext.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/Interfaces/CommandContext.cs @@ -1,6 +1,6 @@ +using LibMatrix.EventTypes.Spec; using LibMatrix.Responses; using LibMatrix.RoomTypes; -using LibMatrix.StateEventTypes.Spec; namespace LibMatrix.ExampleBot.Bot.Interfaces; diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/MRUBot.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/MRUBot.cs index 0b4e2ba..f04ec3a 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/MRUBot.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/MRUBot.cs @@ -1,10 +1,11 @@ using System.Diagnostics.CodeAnalysis; using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.ExampleBot.Bot.Interfaces; using LibMatrix.Extensions; using LibMatrix.Homeservers; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -43,7 +44,7 @@ public class MRUBot : IHostedService { throw; } - await (await hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); + await (hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); // foreach (var room in await hs.GetJoinedRooms()) { // if(room.RoomId is "!OGEhHVWSdvArJzumhm:matrix.org") continue; @@ -61,12 +62,12 @@ public class MRUBot : IHostedService { $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as RoomMemberEventContent).Reason}"); if (inviteEvent.Sender.EndsWith(":rory.gay") || inviteEvent.Sender == "@mxidupwitch:the-apothecary.club") { try { - var senderProfile = await hs.GetProfile(inviteEvent.Sender); - await (await hs.GetRoom(args.Key)).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); + var senderProfile = await hs.GetProfileAsync(inviteEvent.Sender); + await (hs.GetRoom(args.Key)).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); } catch (Exception e) { _logger.LogError("{}", e.ToString()); - await (await hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); + await (hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); } } }); @@ -74,13 +75,13 @@ public class MRUBot : IHostedService { _logger.LogInformation( "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: false, ignoreNull: true)); - var room = await hs.GetRoom(@event.RoomId); + 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" } && message.Body.StartsWith(_configuration.Prefix)) { var command = _commands.FirstOrDefault(x => x.Name == message.Body.Split(' ')[0][_configuration.Prefix.Length..]); if (command == null) { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent(messageType: "m.text", body: "Command not found!")); return; } @@ -93,7 +94,7 @@ public class MRUBot : IHostedService { await command.Invoke(ctx); } else { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent(messageType: "m.text", body: "You do not have permission to run this command!")); } } diff --git a/ExampleBots/LibMatrix.ExampleBot/Bot/StartupTasks/ServerRoomSizeCalulator.cs b/ExampleBots/LibMatrix.ExampleBot/Bot/StartupTasks/ServerRoomSizeCalulator.cs index 4785192..890db85 100644 --- a/ExampleBots/LibMatrix.ExampleBot/Bot/StartupTasks/ServerRoomSizeCalulator.cs +++ b/ExampleBots/LibMatrix.ExampleBot/Bot/StartupTasks/ServerRoomSizeCalulator.cs @@ -3,7 +3,6 @@ using ArcaneLibs.Extensions; using LibMatrix.ExampleBot.Bot.Interfaces; using LibMatrix.Homeservers; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -39,7 +38,7 @@ public class ServerRoomSizeCalulator : IHostedService { throw; } - await (await hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); + await (hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); Dictionary totalRoomSize = new(); foreach (var room in await hs.GetJoinedRooms()) { diff --git a/ExampleBots/MediaModeratorPoC/Bot/Commands/BanMediaCommand.cs b/ExampleBots/MediaModeratorPoC/Bot/Commands/BanMediaCommand.cs index 4642007..d633f89 100644 --- a/ExampleBots/MediaModeratorPoC/Bot/Commands/BanMediaCommand.cs +++ b/ExampleBots/MediaModeratorPoC/Bot/Commands/BanMediaCommand.cs @@ -1,11 +1,11 @@ using System.Security.Cryptography; using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; using LibMatrix.Helpers; using LibMatrix.Responses; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; +using LibMatrix.Utilities.Bot.Interfaces; using MediaModeratorPoC.Bot.AccountData; -using MediaModeratorPoC.Bot.Interfaces; using MediaModeratorPoC.Bot.StateEventTypes; namespace MediaModeratorPoC.Bot.Commands; @@ -17,11 +17,11 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic public async Task CanInvoke(CommandContext ctx) { //check if user is admin in control room var botData = await ctx.Homeserver.GetAccountData("gay.rory.media_moderator_poc_data"); - var controlRoom = await ctx.Homeserver.GetRoom(botData.ControlRoom); + var controlRoom = ctx.Homeserver.GetRoom(botData.ControlRoom); var isAdmin = (await controlRoom.GetPowerLevelsAsync())!.UserHasPermission(ctx.MessageEvent.Sender, "m.room.ban"); if (!isAdmin) { // await ctx.Reply("You do not have permission to use this command!"); - await (await ctx.Homeserver.GetRoom(botData.LogRoom!)).SendMessageEventAsync("m.room.message", + await ctx.Homeserver.GetRoom(botData.LogRoom!).SendMessageEventAsync( new RoomMessageEventContent(body: $"User {ctx.MessageEvent.Sender} tried to use command {Name} but does not have permission!", messageType: "m.text")); } @@ -30,14 +30,14 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic public async Task Invoke(CommandContext ctx) { var botData = await ctx.Homeserver.GetAccountData("gay.rory.media_moderator_poc_data"); - var policyRoom = await ctx.Homeserver.GetRoom(botData.PolicyRoom ?? botData.ControlRoom); - var logRoom = await ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom); + var policyRoom = ctx.Homeserver.GetRoom(botData.PolicyRoom ?? botData.ControlRoom); + var logRoom = ctx.Homeserver.GetRoom(botData.LogRoom ?? botData.ControlRoom); //check if reply var messageContent = ctx.MessageEvent.TypedContent as RoomMessageEventContent; if (messageContent?.RelatesTo is { InReplyTo: not null }) { try { - await logRoom.SendMessageEventAsync("m.room.message", + await logRoom.SendMessageEventAsync( new RoomMessageEventContent( body: $"User {MessageFormatter.HtmlFormatMention(ctx.MessageEvent.Sender)} is trying to ban media {messageContent!.RelatesTo!.InReplyTo!.EventId}", messageType: "m.text")); @@ -47,14 +47,14 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic //check if recommendation is in list if (ctx.Args.Length < 2) { - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatError("You must specify a recommendation type and reason!")); + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatError("You must specify a recommendation type and reason!")); return; } var recommendation = ctx.Args[0]; if (recommendation is not ("ban" or "kick" or "mute" or "redact" or "spoiler" or "warn" or "warn_admins")) { - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatError($"Invalid recommendation type {recommendation}, must be `warn_admins`, `warn`, `spoiler`, `redact`, `mute`, `kick` or `ban`!")); + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatError($"Invalid recommendation type {recommendation}, must be `warn_admins`, `warn`, `spoiler`, `redact`, `mute`, `kick` or `ban`!")); return; } @@ -69,7 +69,7 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic fileHash = await hashAlgo.ComputeHashAsync(await ctx.Homeserver._httpClient.GetStreamAsync(resolvedUri)); } catch (Exception ex) { - await logRoom.SendMessageEventAsync("m.room.message", + await logRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Error calculating file hash for {mxcUri} via {mxcUri.Split('/')[2]}, retrying via {ctx.Homeserver.HomeServerDomain}...", ex)); try { @@ -77,8 +77,8 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic fileHash = await hashAlgo.ComputeHashAsync(await ctx.Homeserver._httpClient.GetStreamAsync(resolvedUri)); } catch (Exception ex2) { - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatException("Error calculating file hash", ex2)); - await logRoom.SendMessageEventAsync("m.room.message", + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatException("Error calculating file hash", ex2)); + await logRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Error calculating file hash via {ctx.Homeserver.HomeServerDomain}!", ex2)); } } @@ -91,18 +91,18 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic Recommendation = recommendation, }); - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatSuccessJson("Media policy created", policy)); - await logRoom.SendMessageEventAsync("m.room.message", MessageFormatter.FormatSuccessJson("Media policy created", policy)); + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatSuccessJson("Media policy created", policy)); + await logRoom.SendMessageEventAsync(MessageFormatter.FormatSuccessJson("Media policy created", policy)); } catch (Exception e) { - await logRoom.SendMessageEventAsync("m.room.message", MessageFormatter.FormatException("Error creating policy", e)); - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatException("Error creating policy", e)); + await logRoom.SendMessageEventAsync(MessageFormatter.FormatException("Error creating policy", e)); + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatException("Error creating policy", e)); await using var stream = new MemoryStream(e.ToString().AsBytes().ToArray()); await logRoom.SendFileAsync("m.file", "error.log.cs", stream); } } else { - await ctx.Room.SendMessageEventAsync("m.room.message", MessageFormatter.FormatError("This command must be used in reply to a message!")); + await ctx.Room.SendMessageEventAsync(MessageFormatter.FormatError("This command must be used in reply to a message!")); } } } diff --git a/ExampleBots/MediaModeratorPoC/Bot/MediaModBot.cs b/ExampleBots/MediaModeratorPoC/Bot/MediaModBot.cs index 7104114..e6ba269 100644 --- a/ExampleBots/MediaModeratorPoC/Bot/MediaModBot.cs +++ b/ExampleBots/MediaModeratorPoC/Bot/MediaModBot.cs @@ -6,14 +6,15 @@ using System.Text.Encodings.Web; using System.Text.RegularExpressions; using ArcaneLibs.Extensions; using LibMatrix; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.Responses; using LibMatrix.RoomTypes; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; +using LibMatrix.Utilities.Bot.Interfaces; using MediaModeratorPoC.Bot.AccountData; -using MediaModeratorPoC.Bot.Interfaces; using MediaModeratorPoC.Bot.StateEventTypes; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -86,9 +87,9 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger await hs.SetAccountData("gay.rory.media_moderator_poc_data", botData); } - _policyRoom = await hs.GetRoom(botData.PolicyRoom ?? botData.ControlRoom); - _logRoom = await hs.GetRoom(botData.LogRoom ?? botData.ControlRoom); - _controlRoom = await hs.GetRoom(botData.ControlRoom); + _policyRoom = hs.GetRoom(botData.PolicyRoom ?? botData.ControlRoom); + _logRoom = hs.GetRoom(botData.LogRoom ?? botData.ControlRoom); + _controlRoom = hs.GetRoom(botData.ControlRoom); List admins = new(); @@ -113,18 +114,18 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as RoomMemberEventContent).Reason}"); if (inviteEvent.Sender.EndsWith(":rory.gay") || inviteEvent.Sender.EndsWith(":conduit.rory.gay")) { try { - var senderProfile = await hs.GetProfile(inviteEvent.Sender); - await (await hs.GetRoom(args.Key)).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); + var senderProfile = await hs.GetProfileAsync(inviteEvent.Sender); + await (hs.GetRoom(args.Key)).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); } catch (Exception e) { logger.LogError("{}", e.ToString()); - await (await hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); + await (hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); } } }); hs.SyncHelper.TimelineEventHandlers.Add(async @event => { - var room = await hs.GetRoom(@event.RoomId); + var room = hs.GetRoom(@event.RoomId); try { logger.LogInformation( "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: true, ignoreNull: true)); @@ -136,7 +137,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger if (matchedPolicy is null) return; var matchedpolicyData = matchedPolicy.TypedContent as MediaPolicyEventContent; var recommendation = matchedpolicyData.Recommendation; - await _logRoom.SendMessageEventAsync("m.room.message", + await _logRoom.SendMessageEventAsync( new RoomMessageEventContent( body: $"User {MessageFormatter.HtmlFormatMention(@event.Sender)} posted an image in {MessageFormatter.HtmlFormatMention(room.RoomId)} that matched rule {matchedPolicy.StateKey}, applying action {matchedpolicyData.Recommendation}, as described in rule: {matchedPolicy.RawContent!.ToJson(ignoreNull: true)}", @@ -147,7 +148,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger }); switch (recommendation) { case "warn_admins": { - await _controlRoom.SendMessageEventAsync("m.room.message", + await _controlRoom.SendMessageEventAsync( new RoomMessageEventContent( body: $"{string.Join(' ', admins)}\nUser {MessageFormatter.HtmlFormatMention(@event.Sender)} posted a banned image {message.Url}", messageType: "m.text") { @@ -158,7 +159,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger break; } case "warn": { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent( body: $"Please be careful when posting this image: {matchedpolicyData.Reason}", messageType: "m.text") { @@ -184,7 +185,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger // // // - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent( body: $"Please be careful when posting this image: {matchedpolicyData.Reason}, I have spoilered it for you:", @@ -194,7 +195,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger $"Please be careful when posting this image: {matchedpolicyData.Reason}, I have spoilered it for you:" }); var imageUrl = message.Url; - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent(body: $"CN: {imageUrl}", messageType: "m.text") { Format = "org.matrix.custom.html", @@ -244,9 +245,9 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger } catch (Exception e) { logger.LogError("{}", e.ToString()); - await _controlRoom.SendMessageEventAsync("m.room.message", + await _controlRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Unable to ban user in {MessageFormatter.HtmlFormatMention(room.RoomId)}", e)); - await _logRoom.SendMessageEventAsync("m.room.message", + await _logRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Unable to ban user in {MessageFormatter.HtmlFormatMention(room.RoomId)}", e)); await using var stream = new MemoryStream(e.ToString().AsBytes().ToArray()); await _controlRoom.SendFileAsync("m.file", "error.log.cs", stream); @@ -274,7 +275,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger fileHash = await hashAlgo.ComputeHashAsync(await hs._httpClient.GetStreamAsync(resolvedUri)); } catch (Exception ex) { - await _logRoom.SendMessageEventAsync("m.room.message", + await _logRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Error calculating file hash for {mxcUri} via {mxcUri.Split('/')[2]} ({resolvedUri}), retrying via {hs.HomeServerDomain}...", ex)); try { @@ -282,7 +283,7 @@ public class MediaModBot(AuthenticatedHomeserverGeneric hs, ILogger fileHash = await hashAlgo.ComputeHashAsync(await hs._httpClient.GetStreamAsync(resolvedUri)); } catch (Exception ex2) { - await _logRoom.SendMessageEventAsync("m.room.message", + await _logRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Error calculating file hash via {hs.HomeServerDomain} ({resolvedUri})!", ex2)); } } diff --git a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs index 5da4f5e..55624a8 100644 --- a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs +++ b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs @@ -1,8 +1,8 @@ using LibMatrix; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Helpers; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; -using MediaModeratorPoC.Bot.Interfaces; +using LibMatrix.Utilities.Bot.Interfaces; using PluralContactBotPoC.Bot.AccountData; using PluralContactBotPoC.Bot.StateEventTypes; @@ -18,7 +18,7 @@ public class CreateSystemCommand(IServiceProvider services, HomeserverProviderSe public async Task Invoke(CommandContext ctx) { if (ctx.Args.Length != 1) { - await ctx.Reply("m.notice", MessageFormatter.FormatError("Only one argument is allowed: system name!")); + await ctx.Reply(MessageFormatter.FormatError("Only one argument is allowed: system name!")); return; } @@ -26,7 +26,7 @@ public class CreateSystemCommand(IServiceProvider services, HomeserverProviderSe try { try { await ctx.Homeserver.GetAccountData("gay.rory.plural_contact_bot.system_data"); - await ctx.Reply("m.notice", MessageFormatter.FormatError($"System {sysName} already exists!")); + await ctx.Reply(MessageFormatter.FormatError($"System {sysName} already exists!")); } catch (MatrixException e) { if (e is { ErrorCode: "M_NOT_FOUND" }) { @@ -51,7 +51,7 @@ public class CreateSystemCommand(IServiceProvider services, HomeserverProviderSe } } catch (Exception e) { - await ctx.Reply("m.notice", MessageFormatter.FormatException("Something went wrong!", e)); + await ctx.Reply(MessageFormatter.FormatException("Something went wrong!", e)); } } } diff --git a/ExampleBots/PluralContactBotPoC/Bot/PluralContactBot.cs b/ExampleBots/PluralContactBotPoC/Bot/PluralContactBot.cs index 2136b42..c3cebe2 100644 --- a/ExampleBots/PluralContactBotPoC/Bot/PluralContactBot.cs +++ b/ExampleBots/PluralContactBotPoC/Bot/PluralContactBot.cs @@ -1,12 +1,13 @@ using System.Text; using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.RoomTypes; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; using LibMatrix.Utilities.Bot; -using MediaModeratorPoC.Bot.Interfaces; +using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using PluralContactBotPoC.Bot.AccountData; @@ -35,7 +36,7 @@ public class PluralContactBot(AuthenticatedHomeserverGeneric hs, ILogger { var inviteEvent = @@ -46,9 +47,9 @@ public class PluralContactBot(AuthenticatedHomeserverGeneric hs, ILogger($"gay.rory.plural_contact_bot.system_data#{inviteEvent.StateKey}"); if (accountData.Members.Contains(inviteEvent.Sender)) { - await (await hs.GetRoom(args.Key)).JoinAsync(reason: "I was invited by a system member!"); + await (hs.GetRoom(args.Key)).JoinAsync(reason: "I was invited by a system member!"); - await _logRoom.SendMessageEventAsync("m.room.message", + await _logRoom.SendMessageEventAsync( MessageFormatter.FormatSuccess( $"I was invited by a system member ({MessageFormatter.HtmlFormatMention(inviteEvent.Sender)}) to {MessageFormatter.HtmlFormatMention(args.Key)}")); @@ -56,25 +57,25 @@ public class PluralContactBot(AuthenticatedHomeserverGeneric hs, ILogger { - var room = await hs.GetRoom(@event.RoomId); + var room = hs.GetRoom(@event.RoomId); try { logger.LogInformation( "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: true, ignoreNull: true)); @@ -83,7 +84,7 @@ public class PluralContactBot(AuthenticatedHomeserverGeneric hs, ILogger? Emoticons { get; set; } + + [JsonPropertyName("images")] + public Dictionary? Images { get; set; } + + [JsonPropertyName("pack")] + public PackInfo? Pack { get; set; } + + public class EmoticonData { + [JsonPropertyName("url")] + public string? Url { get; set; } + } + + public class PackInfo { + + } +} diff --git a/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs b/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs new file mode 100644 index 0000000..b76b176 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec; + +[MatrixEvent(EventName = "m.room.message")] +public class RoomMessageEventContent : EventContent { + public RoomMessageEventContent(string? messageType = "m.notice", string? body = null) { + MessageType = messageType; + Body = body; + } + + [JsonPropertyName("body")] + public string Body { get; set; } + + [JsonPropertyName("msgtype")] + public string MessageType { get; set; } = "m.notice"; + + [JsonPropertyName("formatted_body")] + public string FormattedBody { get; set; } + + [JsonPropertyName("format")] + public string Format { get; set; } + + /// + /// Media URI for this message, if any + /// + [JsonPropertyName("url")] + public string? Url { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/CanonicalAliasEventContent.cs b/LibMatrix/EventTypes/Spec/State/CanonicalAliasEventContent.cs new file mode 100644 index 0000000..71f3d0d --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/CanonicalAliasEventContent.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.canonical_alias")] +public class CanonicalAliasEventContent : EventContent { + [JsonPropertyName("alias")] + public string? Alias { get; set; } + [JsonPropertyName("alt_aliases")] + public string[]? AltAliases { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/GuestAccessEventData.cs b/LibMatrix/EventTypes/Spec/State/GuestAccessEventData.cs new file mode 100644 index 0000000..af1b2ce --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/GuestAccessEventData.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.guest_access")] +public class GuestAccessEventContent : EventContent { + [JsonPropertyName("guest_access")] + public string GuestAccess { get; set; } + + public bool IsGuestAccessEnabled { + get => GuestAccess == "can_join"; + set => GuestAccess = value ? "can_join" : "forbidden"; + } +} diff --git a/LibMatrix/EventTypes/Spec/State/HistoryVisibilityEventData.cs b/LibMatrix/EventTypes/Spec/State/HistoryVisibilityEventData.cs new file mode 100644 index 0000000..b57ade5 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/HistoryVisibilityEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.history_visibility")] +public class HistoryVisibilityEventContent : EventContent { + [JsonPropertyName("history_visibility")] + public string HistoryVisibility { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/JoinRulesEventData.cs b/LibMatrix/EventTypes/Spec/State/JoinRulesEventData.cs new file mode 100644 index 0000000..0098bef --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/JoinRulesEventData.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.join_rules")] +public class JoinRulesEventContent : EventContent { + private static string Public = "public"; + private static string Invite = "invite"; + private static string Knock = "knock"; + + /// + /// one of ["public", "invite", "knock", "restricted", "knock_restricted"] + /// "private" is reserved without implementation! + /// + [JsonPropertyName("join_rule")] + public string JoinRule { get; set; } + + [JsonPropertyName("allow")] + public List Allow { get; set; } + + public class AllowEntry { + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("room_id")] + public string RoomId { get; set; } + } +} diff --git a/LibMatrix/EventTypes/Spec/State/PolicyRuleStateEventData.cs b/LibMatrix/EventTypes/Spec/State/PolicyRuleStateEventData.cs new file mode 100644 index 0000000..fde02c1 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/PolicyRuleStateEventData.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.policy.rule.user")] +[MatrixEvent(EventName = "m.policy.rule.server")] +[MatrixEvent(EventName = "org.matrix.mjolnir.rule.server")] +public class PolicyRuleEventContent : EventContent { + /// + /// Entity this ban applies to, can use * and ? as globs. + /// + [JsonPropertyName("entity")] + public string Entity { get; set; } + + /// + /// Reason this user is banned + /// + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// + /// Suggested action to take + /// + [JsonPropertyName("recommendation")] + public string? Recommendation { get; set; } + + /// + /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry. + /// + [JsonPropertyName("support.feline.policy.expiry.rev.2")] //stable prefix: expiry, msc pending + public long? Expiry { get; set; } + + //utils + /// + /// Readable expiry time, provided for easy interaction + /// + [JsonPropertyName("gay.rory.matrix_room_utils.readable_expiry_time_utc")] + public DateTime? ExpiryDateTime { + get => Expiry == null ? null : DateTimeOffset.FromUnixTimeMilliseconds(Expiry.Value).DateTime; + set => Expiry = ((DateTimeOffset)value).ToUnixTimeMilliseconds(); + } +} + +public static class PolicyRecommendationTypes { + /// + /// Ban this user + /// + public static string Ban = "m.ban"; + + /// + /// Mute this user + /// + public static string Mute = "support.feline.policy.recommendation_mute"; //stable prefix: m.mute, msc pending +} diff --git a/LibMatrix/EventTypes/Spec/State/PresenceStateEventData.cs b/LibMatrix/EventTypes/Spec/State/PresenceStateEventData.cs new file mode 100644 index 0000000..b12da5b --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/PresenceStateEventData.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.presence")] +public class PresenceEventContent : EventContent { + [JsonPropertyName("presence")] + public string Presence { get; set; } + [JsonPropertyName("last_active_ago")] + public long LastActiveAgo { get; set; } + [JsonPropertyName("currently_active")] + public bool CurrentlyActive { get; set; } + [JsonPropertyName("status_msg")] + public string StatusMessage { get; set; } + [JsonPropertyName("avatar_url")] + public string AvatarUrl { get; set; } + [JsonPropertyName("displayname")] + public string DisplayName { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/ProfileResponseEventData.cs b/LibMatrix/EventTypes/Spec/State/ProfileResponseEventData.cs new file mode 100644 index 0000000..893fce1 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/ProfileResponseEventData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +public class ProfileResponseEventContent : EventContent { + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } + + [JsonPropertyName("displayname")] + public string? DisplayName { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomAliasEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomAliasEventData.cs new file mode 100644 index 0000000..5b0e914 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomAliasEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.alias")] +public class RoomAliasEventContent : EventContent { + [JsonPropertyName("aliases")] + public List? Aliases { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomAvatarEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomAvatarEventData.cs new file mode 100644 index 0000000..601d014 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomAvatarEventData.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.avatar")] +public class RoomAvatarEventContent : EventContent { + [JsonPropertyName("url")] + public string? Url { get; set; } + + [JsonPropertyName("info")] + public RoomAvatarInfo? Info { get; set; } + + public class RoomAvatarInfo { + [JsonPropertyName("h")] + public int? Height { get; set; } + + [JsonPropertyName("w")] + public int? Width { get; set; } + + [JsonPropertyName("mimetype")] + public string? MimeType { get; set; } + + [JsonPropertyName("size")] + public int? Size { get; set; } + } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs new file mode 100644 index 0000000..e409f3a --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.create")] +public class RoomCreateEventContent : EventContent { + [JsonPropertyName("room_version")] + public string? RoomVersion { get; set; } + [JsonPropertyName("creator")] + public string? Creator { get; set; } + [JsonPropertyName("m.federate")] + public bool? Federate { get; set; } + [JsonPropertyName("predecessor")] + public RoomCreatePredecessor? Predecessor { get; set; } + [JsonPropertyName("type")] + public string? Type { get; set; } + + public class RoomCreatePredecessor { + [JsonPropertyName("room_id")] + public string? RoomId { get; set; } + + [JsonPropertyName("event_id")] + public string? EventId { get; set; } + } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomEncryptionEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomEncryptionEventData.cs new file mode 100644 index 0000000..6ffa4c5 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomEncryptionEventData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.encryption")] +public class RoomEncryptionEventContent : EventContent { + [JsonPropertyName("algorithm")] + public string? Algorithm { get; set; } + [JsonPropertyName("rotation_period_ms")] + public ulong? RotationPeriodMs { get; set; } + [JsonPropertyName("rotation_period_msgs")] + public ulong? RotationPeriodMsgs { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomMemberEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomMemberEventData.cs new file mode 100644 index 0000000..da158f1 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomMemberEventData.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.member")] +public class RoomMemberEventContent : EventContent { + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + [JsonPropertyName("membership")] + public string Membership { get; set; } = null!; + + [JsonPropertyName("displayname")] + public string? Displayname { get; set; } + + [JsonPropertyName("is_direct")] + public bool? IsDirect { get; set; } + + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } + + [JsonPropertyName("kind")] + public string? Kind { get; set; } + + [JsonPropertyName("join_authorised_via_users_server")] + public string? JoinAuthorisedViaUsersServer { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomNameEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomNameEventData.cs new file mode 100644 index 0000000..7cb881a --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomNameEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.name")] +public class RoomNameEventContent : EventContent { + [JsonPropertyName("name")] + public string? Name { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomPinnedEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomPinnedEventData.cs new file mode 100644 index 0000000..eb02cc7 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomPinnedEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.pinned_events")] +public class RoomPinnedEventContent : EventContent { + [JsonPropertyName("pinned")] + public string[]? PinnedEvents { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs new file mode 100644 index 0000000..1a5d5f5 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.power_levels")] +public class RoomPowerLevelEventContent : EventContent { + [JsonPropertyName("ban")] + public long? Ban { get; set; } // = 50; + + [JsonPropertyName("events_default")] + public long EventsDefault { get; set; } // = 0; + + [JsonPropertyName("events")] + public Dictionary? Events { get; set; } // = null!; + + [JsonPropertyName("invite")] + public long? Invite { get; set; } // = 50; + + [JsonPropertyName("kick")] + public long? Kick { get; set; } // = 50; + + [JsonPropertyName("notifications")] + public NotificationsPL? NotificationsPl { get; set; } // = null!; + + [JsonPropertyName("redact")] + public long? Redact { get; set; } // = 50; + + [JsonPropertyName("state_default")] + public long? StateDefault { get; set; } // = 50; + + [JsonPropertyName("users")] + public Dictionary? Users { get; set; } // = null!; + + [JsonPropertyName("users_default")] + public long? UsersDefault { get; set; } // = 0; + + [Obsolete("Historical was a key related to MSC2716, a spec change on backfill that was dropped!", true)] + [JsonIgnore] + [JsonPropertyName("historical")] + public long Historical { get; set; } // = 50; + + public class NotificationsPL { + [JsonPropertyName("room")] + public long Room { get; set; } = 50; + } + + public bool IsUserAdmin(string userId) { + return Users.TryGetValue(userId, out var level) && level >= Events.Max(x=>x.Value); + } + + public bool UserHasPermission(string userId, string eventType) { + return Users.TryGetValue(userId, out var level) && level >= Events.GetValueOrDefault(eventType, EventsDefault); + } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomTopicEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomTopicEventData.cs new file mode 100644 index 0000000..52c7e42 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomTopicEventData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.topic")] +[MatrixEvent(EventName = "org.matrix.msc3765.topic", Legacy = true)] +public class RoomTopicEventContent : EventContent { + [JsonPropertyName("topic")] + public string? Topic { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/RoomTypingEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomTypingEventData.cs new file mode 100644 index 0000000..01cfacf --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/RoomTypingEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.typing")] +public class RoomTypingEventContent : EventContent { + [JsonPropertyName("user_ids")] + public string[]? UserIds { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/ServerACLEventData.cs b/LibMatrix/EventTypes/Spec/State/ServerACLEventData.cs new file mode 100644 index 0000000..f18fe43 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/ServerACLEventData.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.room.server_acl")] +public class ServerACLEventContent : EventContent { + [JsonPropertyName("allow")] + public List Allow { get; set; } // = null!; + + [JsonPropertyName("deny")] + public List Deny { get; set; } // = null!; + + [JsonPropertyName("allow_ip_literals")] + public bool AllowIpLiterals { get; set; } // = false; +} diff --git a/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs b/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs new file mode 100644 index 0000000..a13ba2e --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.space.child")] +public class SpaceChildEventContent : EventContent { + [JsonPropertyName("auto_join")] + public bool? AutoJoin { get; set; } + [JsonPropertyName("via")] + public string[]? Via { get; set; } + [JsonPropertyName("suggested")] + public bool? Suggested { get; set; } +} diff --git a/LibMatrix/EventTypes/Spec/State/SpaceParentEventData.cs b/LibMatrix/EventTypes/Spec/State/SpaceParentEventData.cs new file mode 100644 index 0000000..0ffa193 --- /dev/null +++ b/LibMatrix/EventTypes/Spec/State/SpaceParentEventData.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using LibMatrix.Helpers; +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes.Spec.State; + +[MatrixEvent(EventName = "m.space.parent")] +public class SpaceParentEventContent : EventContent { + [JsonPropertyName("via")] + public string[]? Via { get; set; } + + [JsonPropertyName("canonical")] + public bool? Canonical { get; set; } +} diff --git a/LibMatrix/EventTypes/UnknownStateEventData.cs b/LibMatrix/EventTypes/UnknownStateEventData.cs new file mode 100644 index 0000000..9a276c8 --- /dev/null +++ b/LibMatrix/EventTypes/UnknownStateEventData.cs @@ -0,0 +1,7 @@ +using LibMatrix.Interfaces; + +namespace LibMatrix.EventTypes; + +public class UnknownEventContent : EventContent { + +} diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs index 31ae650..a5eb40f 100644 --- a/LibMatrix/Extensions/HttpClientExtensions.cs +++ b/LibMatrix/Extensions/HttpClientExtensions.cs @@ -1,6 +1,8 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Reflection; +using System.Text; using System.Text.Json; using ArcaneLibs.Extensions; @@ -28,7 +30,8 @@ public class MatrixHttpClient : HttpClient { if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); if (AssertedUserId is not null) request.RequestUri = request.RequestUri.AddQuery("user_id", AssertedUserId); - Console.WriteLine($"Sending request to {request.RequestUri}"); + // Console.WriteLine($"Sending request to {request.RequestUri}"); + try { var webAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); @@ -76,4 +79,11 @@ public class MatrixHttpClient : HttpClient { response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStreamAsync(cancellationToken); } + + public new async Task PutAsJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, T value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { + var request = new HttpRequestMessage(HttpMethod.Put, requestUri); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType()), Encoding.UTF8, "application/json"); + return await SendAsync(request, cancellationToken); + } } diff --git a/LibMatrix/Extensions/StringExtensions.cs b/LibMatrix/Extensions/StringExtensions.cs deleted file mode 100644 index 491fa77..0000000 --- a/LibMatrix/Extensions/StringExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace LibMatrix.Extensions; - -public static class StringExtensions { - // public static async Task GetMediaUrl(this string MxcUrl) - // { - // //MxcUrl: mxc://rory.gay/ocRVanZoUTCcifcVNwXgbtTg - // //target: https://matrix.rory.gay/_matrix/media/v3/download/rory.gay/ocRVanZoUTCcifcVNwXgbtTg - // - // var server = MxcUrl.Split('/')[2]; - // var mediaId = MxcUrl.Split('/')[3]; - // return $"{(await new RemoteHomeServer(server).Configure()).FullHomeServerDomain}/_matrix/media/v3/download/{server}/{mediaId}"; - // } -} diff --git a/LibMatrix/Helpers/MediaResolver.cs b/LibMatrix/Helpers/MediaResolver.cs deleted file mode 100644 index 5886618..0000000 --- a/LibMatrix/Helpers/MediaResolver.cs +++ /dev/null @@ -1,7 +0,0 @@ -using LibMatrix.Services; - -namespace LibMatrix.Helpers; - -public static class MediaResolver { - public static string ResolveMediaUri(string homeserver, string mxc) => mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/"); -} diff --git a/LibMatrix/Helpers/MessageFormatter.cs b/LibMatrix/Helpers/MessageFormatter.cs index 37d7004..ae02afc 100644 --- a/LibMatrix/Helpers/MessageFormatter.cs +++ b/LibMatrix/Helpers/MessageFormatter.cs @@ -1,5 +1,5 @@ using ArcaneLibs.Extensions; -using LibMatrix.StateEventTypes.Spec; +using LibMatrix.EventTypes.Spec; namespace LibMatrix.Helpers; diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs index d719184..386fd4d 100644 --- a/LibMatrix/Helpers/SyncHelper.cs +++ b/LibMatrix/Helpers/SyncHelper.cs @@ -9,17 +9,17 @@ using LibMatrix.Services; namespace LibMatrix.Helpers; -public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, TieredStorageService storageService) { +public class SyncHelper(AuthenticatedHomeserverGeneric homeserver) { public async Task Sync( string? since = null, int? timeout = 30000, string? setPresence = "online", SyncFilter? filter = null, CancellationToken? cancellationToken = null) { - var outFileName = "sync-" + - (await storageService.CacheStorageProvider.GetAllKeysAsync()).Count( - x => x.StartsWith("sync")) + - ".json"; + // var outFileName = "sync-" + + // (await storageService.CacheStorageProvider.GetAllKeysAsync()).Count( + // x => x.StartsWith("sync")) + + // ".json"; var url = $"/_matrix/client/v3/sync?timeout={timeout}&set_presence={setPresence}"; if (!string.IsNullOrWhiteSpace(since)) url += $"&since={since}"; if (filter is not null) url += $"&filter={filter.ToJson(ignoreNull: true, indent: false)}"; @@ -65,10 +65,10 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, TieredStorage SyncFilter? filter = null, CancellationToken? cancellationToken = null ) { - await Task.WhenAll((await storageService.CacheStorageProvider.GetAllKeysAsync()) - .Where(x => x.StartsWith("sync")) - .ToList() - .Select(x => storageService.CacheStorageProvider.DeleteObjectAsync(x))); + // await Task.WhenAll((await storageService.CacheStorageProvider.GetAllKeysAsync()) + // .Where(x => x.StartsWith("sync")) + // .ToList() + // .Select(x => storageService.CacheStorageProvider.DeleteObjectAsync(x))); var nextBatch = since; while (cancellationToken is null || !cancellationToken.Value.IsCancellationRequested) { var sync = await Sync(since: nextBatch, timeout: timeout, setPresence: setPresence, filter: filter, diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs index a280c54..b881e6c 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs @@ -12,25 +12,26 @@ using LibMatrix.Services; namespace LibMatrix.Homeservers; public class AuthenticatedHomeserverGeneric : RemoteHomeServer { - public AuthenticatedHomeserverGeneric(TieredStorageService storage, string canonicalHomeServerDomain, string accessToken) : base(canonicalHomeServerDomain) { - Storage = storage; + public AuthenticatedHomeserverGeneric(string canonicalHomeServerDomain, string accessToken) : base(canonicalHomeServerDomain) { AccessToken = accessToken.Trim(); - SyncHelper = new SyncHelper(this, storage); + SyncHelper = new SyncHelper(this); } - public virtual TieredStorageService Storage { get; set; } public virtual SyncHelper SyncHelper { get; init; } public virtual WhoAmIResponse WhoAmI { get; set; } = null!; public virtual string UserId => WhoAmI.UserId; public virtual string AccessToken { get; set; } - public virtual Task GetRoom(string roomId) => Task.FromResult(new(this, roomId)); + public virtual GenericRoom GetRoom(string roomId) { + if(roomId is null || !roomId.StartsWith("!")) throw new ArgumentException("Room ID must start with !", nameof(roomId)); + return new GenericRoom(this, roomId); + } public virtual async Task> GetJoinedRooms() { var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); var roomsJson = await roomQuery.Content.ReadFromJsonAsync(); - var rooms = roomsJson.GetProperty("joined_rooms").EnumerateArray().Select(room => new GenericRoom(this, room.GetString()!)).ToList(); + var rooms = roomsJson.GetProperty("joined_rooms").EnumerateArray().Select(room => GetRoom(room.GetString()!)).ToList(); Console.WriteLine($"Fetched {rooms.Count} rooms"); @@ -58,7 +59,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer { throw new InvalidDataException($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); } - var room = await GetRoom((await res.Content.ReadFromJsonAsync())!["room_id"]!.ToString()); + var room = GetRoom((await res.Content.ReadFromJsonAsync())!["room_id"]!.ToString()); foreach (var user in creationEvent.Invite) { await room.InviteUser(user); @@ -67,6 +68,14 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer { return room; } + public virtual async Task Logout() { + var res = await _httpClient.PostAsync("/_matrix/client/v3/logout", null); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to logout: {await res.Content.ReadAsStringAsync()}"); + throw new InvalidDataException($"Failed to logout: {await res.Content.ReadAsStringAsync()}"); + } + } + #region Utility Functions public virtual async IAsyncEnumerable GetJoinedRoomsByType(string type) { var rooms = await GetJoinedRooms(); diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs index e44d727..5319f46 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs @@ -4,5 +4,5 @@ using LibMatrix.Services; namespace LibMatrix.Homeservers; -public class AuthenticatedHomeserverMxApiExtended(TieredStorageService storage, string canonicalHomeServerDomain, string accessToken) : AuthenticatedHomeserverGeneric(storage, canonicalHomeServerDomain, +public class AuthenticatedHomeserverMxApiExtended(string canonicalHomeServerDomain, string accessToken) : AuthenticatedHomeserverGeneric(canonicalHomeServerDomain, accessToken); diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs index 218ded0..ae26f69 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs @@ -102,7 +102,7 @@ public class AuthenticatedHomeserverSynapse : AuthenticatedHomeserverGeneric { } } - public AuthenticatedHomeserverSynapse(TieredStorageService storage, string canonicalHomeServerDomain, string accessToken) : base(storage, canonicalHomeServerDomain, accessToken) { + public AuthenticatedHomeserverSynapse(string canonicalHomeServerDomain, string accessToken) : base(canonicalHomeServerDomain, accessToken) { Admin = new(this); } } diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs index caed397..ab3ab51 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs @@ -1,7 +1,9 @@ using System.Net.Http.Json; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Extensions; using LibMatrix.Responses; -using LibMatrix.StateEventTypes.Spec; namespace LibMatrix.Homeservers; @@ -13,12 +15,13 @@ public class RemoteHomeServer(string canonicalHomeServerDomain) { public string FullHomeServerDomain { get; set; } public MatrixHttpClient _httpClient { get; set; } = new(); - public async Task GetProfile(string mxid) { - if(mxid is null) throw new ArgumentNullException(nameof(mxid)); + public async Task GetProfileAsync(string mxid) { + if (mxid is null) throw new ArgumentNullException(nameof(mxid)); if (_profileCache.TryGetValue(mxid, out var value)) { if (value is SemaphoreSlim s) await s.WaitAsync(); if (value is ProfileResponseEventContent p) return p; } + _profileCache[mxid] = new SemaphoreSlim(1); var resp = await _httpClient.GetAsync($"/_matrix/client/v3/profile/{mxid}"); @@ -29,10 +32,26 @@ public class RemoteHomeServer(string canonicalHomeServerDomain) { return data; } - public async Task GetClientVersions() { + public async Task GetClientVersionsAsync() { var resp = await _httpClient.GetAsync($"/_matrix/client/versions"); var data = await resp.Content.ReadFromJsonAsync(); if (!resp.IsSuccessStatusCode) Console.WriteLine("ClientVersions: " + data); return data; } + + public async Task ResolveRoomAliasAsync(string alias) { + var resp = await _httpClient.GetAsync($"/_matrix/client/v3/directory/room/{alias.Replace("#", "%23")}"); + var data = await resp.Content.ReadFromJsonAsync(); + var text = await resp.Content.ReadAsStringAsync(); + if (!resp.IsSuccessStatusCode) Console.WriteLine("ResolveAlias: " + data.ToJson()); + return data; + } +} + +public class AliasResult { + [JsonPropertyName("room_id")] + public string RoomId { get; set; } = null!; + + [JsonPropertyName("servers")] + public List Servers { get; set; } = null!; } diff --git a/LibMatrix/Interfaces/IStateEventType.cs b/LibMatrix/Interfaces/IStateEventType.cs index f2e4a3b..b187970 100644 --- a/LibMatrix/Interfaces/IStateEventType.cs +++ b/LibMatrix/Interfaces/IStateEventType.cs @@ -4,10 +4,10 @@ namespace LibMatrix.Interfaces; public abstract class EventContent { [JsonPropertyName("m.relates_to")] - public virtual MessageRelatesTo? RelatesTo { get; set; } + public MessageRelatesTo? RelatesTo { get; set; } [JsonPropertyName("m.new_content")] - public virtual EventContent? NewContent { get; set; } + public EventContent? NewContent { get; set; } public abstract class MessageRelatesTo { [JsonPropertyName("m.in_reply_to")] diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs index 82a4b12..381271b 100644 --- a/LibMatrix/Responses/CreateRoomRequest.cs +++ b/LibMatrix/Responses/CreateRoomRequest.cs @@ -2,10 +2,10 @@ using System.Reflection; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.RegularExpressions; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.Interfaces; -using LibMatrix.StateEventTypes.Spec; namespace LibMatrix.Responses; diff --git a/LibMatrix/Responses/StateEventResponse.cs b/LibMatrix/Responses/StateEventResponse.cs index c60d71c..7ca6bab 100644 --- a/LibMatrix/Responses/StateEventResponse.cs +++ b/LibMatrix/Responses/StateEventResponse.cs @@ -45,3 +45,8 @@ public class StateEventResponse : StateEvent { public JsonObject? PrevContent { get; set; } } } + +public class ChunkedStateEventResponse { + [JsonPropertyName("chunk")] + public List? Chunk { get; set; } +} diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs index 146b5dd..ab748fe 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs @@ -2,10 +2,12 @@ using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using System.Web; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; using LibMatrix.Extensions; using LibMatrix.Homeservers; +using LibMatrix.Interfaces; using LibMatrix.Responses; -using LibMatrix.StateEventTypes.Spec; using Microsoft.Extensions.Logging; namespace LibMatrix.RoomTypes; @@ -65,12 +67,12 @@ public class GenericRoom { #endif } catch (MatrixException e) { - if (e is not { ErrorCode: "M_NOT_FOUND" }) { + // if (e is not { ErrorCodode: "M_NOT_FOUND" }) { throw; - } + // } - Console.WriteLine(e); - return default; + // Console.WriteLine(e); + // return default; } } @@ -93,7 +95,7 @@ public class GenericRoom { } } - public async Task JoinAsync(string[]? homeservers = null, string? reason = null) { + public async Task JoinAsync(string[]? homeservers = null, string? reason = null) { var join_url = $"/_matrix/client/v3/join/{HttpUtility.UrlEncode(RoomId)}"; Console.WriteLine($"Calling {join_url} with {homeservers?.Length ?? 0} via's..."); if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] }; @@ -101,6 +103,7 @@ public class GenericRoom { var res = await _httpClient.PostAsJsonAsync(fullJoinUrl, new { reason }); + return await res.Content.ReadFromJsonAsync() ?? throw new Exception("Failed to join room?"); } // TODO: rewrite (members endpoint?) @@ -111,10 +114,10 @@ public class GenericRoom { // if (joinedOnly && (member.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue; // yield return member; // } - var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members"); - var result = - JsonSerializer.DeserializeAsyncEnumerable(await res.Content.ReadAsStreamAsync()); - await foreach (var resp in result) { + var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members?limit=2"); + var resText = await res.Content.ReadAsStringAsync(); + var result = await JsonSerializer.DeserializeAsync(await res.Content.ReadAsStreamAsync()); + foreach (var resp in result.Chunk) { if (resp?.Type != "m.room.member") continue; if (joinedOnly && (resp.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue; yield return resp; @@ -123,6 +126,9 @@ public class GenericRoom { #region Utility shortcuts + public async Task SendMessageEventAsync(RoomMessageEventContent content) => + await SendTimelineEventAsync("m.room.message", content); + public async Task> GetAliasesAsync() { var res = await GetStateAsync("m.room.aliases"); return res.Aliases; @@ -187,7 +193,7 @@ public class GenericRoom { await (await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}/{stateKey}", content)) .Content.ReadFromJsonAsync(); - public async Task SendMessageEventAsync(string eventType, RoomMessageEventContent content) { + public async Task SendTimelineEventAsync(string eventType, EventContent content) { var res = await _httpClient.PutAsJsonAsync( $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull @@ -244,3 +250,8 @@ public class GenericRoom { await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/invite", new UserIdAndReason(userId, reason)); } } + +public class RoomIdResponse { + [JsonPropertyName("room_id")] + public string RoomId { get; set; } = null!; +} diff --git a/LibMatrix/RoomTypes/SpaceRoom.cs b/LibMatrix/RoomTypes/SpaceRoom.cs index 0a4447a..a43ae82 100644 --- a/LibMatrix/RoomTypes/SpaceRoom.cs +++ b/LibMatrix/RoomTypes/SpaceRoom.cs @@ -13,14 +13,14 @@ public class SpaceRoom : GenericRoom { private static SemaphoreSlim _semaphore = new(1, 1); public async IAsyncEnumerable GetChildrenAsync(bool includeRemoved = false) { - await _semaphore.WaitAsync(); + // await _semaphore.WaitAsync(); var rooms = new List(); var state = GetFullStateAsync(); await foreach (var stateEvent in state) { if (stateEvent.Type != "m.space.child") continue; if (stateEvent.RawContent.ToJson() != "{}" || includeRemoved) - yield return await _homeserver.GetRoom(stateEvent.StateKey); + yield return _homeserver.GetRoom(stateEvent.StateKey); } - _semaphore.Release(); + // _semaphore.Release(); } } diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs index 71d9860..49167fa 100644 --- a/LibMatrix/Services/HomeserverProviderService.cs +++ b/LibMatrix/Services/HomeserverProviderService.cs @@ -39,10 +39,10 @@ public class HomeserverProviderService { AuthenticatedHomeserverGeneric hs; if (true) { - hs = new AuthenticatedHomeserverMxApiExtended(_tieredStorageService, homeserver, accessToken); + hs = new AuthenticatedHomeserverMxApiExtended(homeserver, accessToken); } else { - hs = new AuthenticatedHomeserverGeneric(_tieredStorageService, homeserver, accessToken); + hs = new AuthenticatedHomeserverGeneric(homeserver, accessToken); } hs.FullHomeServerDomain = domain; diff --git a/LibMatrix/Services/HomeserverResolverService.cs b/LibMatrix/Services/HomeserverResolverService.cs index f2c0781..685724b 100644 --- a/LibMatrix/Services/HomeserverResolverService.cs +++ b/LibMatrix/Services/HomeserverResolverService.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; namespace LibMatrix.Services; -public class HomeserverResolverService(ILogger? logger) { +public class HomeserverResolverService(ILogger? logger = null) { private readonly MatrixHttpClient _httpClient = new(); private static readonly Dictionary _wellKnownCache = new(); diff --git a/LibMatrix/Services/TieredStorageService.cs b/LibMatrix/Services/TieredStorageService.cs index 954a2ce..f242785 100644 --- a/LibMatrix/Services/TieredStorageService.cs +++ b/LibMatrix/Services/TieredStorageService.cs @@ -3,10 +3,10 @@ using LibMatrix.Interfaces.Services; namespace LibMatrix.Services; public class TieredStorageService { - public IStorageProvider CacheStorageProvider { get; } - public IStorageProvider DataStorageProvider { get; } + public IStorageProvider? CacheStorageProvider { get; } + public IStorageProvider? DataStorageProvider { get; } - public TieredStorageService(IStorageProvider cacheStorageProvider, IStorageProvider dataStorageProvider) { + public TieredStorageService(IStorageProvider? cacheStorageProvider, IStorageProvider? dataStorageProvider) { CacheStorageProvider = cacheStorageProvider; DataStorageProvider = dataStorageProvider; } diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs index 9ca9141..97348a5 100644 --- a/LibMatrix/StateEvent.cs +++ b/LibMatrix/StateEvent.cs @@ -4,15 +4,14 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization; using ArcaneLibs; using ArcaneLibs.Extensions; +using LibMatrix.EventTypes; using LibMatrix.Helpers; using LibMatrix.Interfaces; -using LibMatrix.StateEventTypes; namespace LibMatrix; public class StateEvent { - public static readonly List KnownStateEventTypes = - new ClassCollector().ResolveFromAllAccessibleAssemblies(); + public static List KnownStateEventTypes { get; } = new ClassCollector().ResolveFromAllAccessibleAssemblies(); public static readonly Dictionary KnownStateEventTypesByName = KnownStateEventTypes.Aggregate( new Dictionary(), diff --git a/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs b/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs deleted file mode 100644 index ff11be7..0000000 --- a/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Common; - -[MatrixEvent(EventName = "org.matrix.mjolnir.shortcode")] -public class MjolnirShortcodeEventContent : EventContent { - [JsonPropertyName("shortcode")] - public string? Shortcode { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs b/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs deleted file mode 100644 index a056eda..0000000 --- a/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Common; - -[MatrixEvent(EventName = "im.ponies.room_emotes")] -public class RoomEmotesEventContent : EventContent { - [JsonPropertyName("emoticons")] - public Dictionary? Emoticons { get; set; } - - [JsonPropertyName("images")] - public Dictionary? Images { get; set; } - - [JsonPropertyName("pack")] - public PackInfo? Pack { get; set; } - - public class EmoticonData { - [JsonPropertyName("url")] - public string? Url { get; set; } - } - - public class PackInfo { - - } -} diff --git a/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventContent.cs b/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventContent.cs deleted file mode 100644 index 7a0e84c..0000000 --- a/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventContent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.canonical_alias")] -public class CanonicalAliasEventContent : EventContent { - [JsonPropertyName("alias")] - public string? Alias { get; set; } - [JsonPropertyName("alt_aliases")] - public string[]? AltAliases { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs b/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs deleted file mode 100644 index 0709b86..0000000 --- a/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.guest_access")] -public class GuestAccessEventContent : EventContent { - [JsonPropertyName("guest_access")] - public string GuestAccess { get; set; } - - public bool IsGuestAccessEnabled { - get => GuestAccess == "can_join"; - set => GuestAccess = value ? "can_join" : "forbidden"; - } -} diff --git a/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs b/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs deleted file mode 100644 index b19dd32..0000000 --- a/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.history_visibility")] -public class HistoryVisibilityEventContent : EventContent { - [JsonPropertyName("history_visibility")] - public string HistoryVisibility { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs b/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs deleted file mode 100644 index 8c0772f..0000000 --- a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.join_rules")] -public class JoinRulesEventContent : EventContent { - private static string Public = "public"; - private static string Invite = "invite"; - private static string Knock = "knock"; - - /// - /// one of ["public", "invite", "knock", "restricted", "knock_restricted"] - /// "private" is reserved without implementation! - /// - [JsonPropertyName("join_rule")] - public string JoinRule { get; set; } - - [JsonPropertyName("allow")] - public List Allow { get; set; } - - public class AllowEntry { - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("room_id")] - public string RoomId { get; set; } - } -} diff --git a/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs b/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs deleted file mode 100644 index 539e371..0000000 --- a/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.policy.rule.user")] -[MatrixEvent(EventName = "m.policy.rule.server")] -[MatrixEvent(EventName = "org.matrix.mjolnir.rule.server")] -public class PolicyRuleEventContent : EventContent { - /// - /// Entity this ban applies to, can use * and ? as globs. - /// - [JsonPropertyName("entity")] - public string Entity { get; set; } - - /// - /// Reason this user is banned - /// - [JsonPropertyName("reason")] - public string? Reason { get; set; } - - /// - /// Suggested action to take - /// - [JsonPropertyName("recommendation")] - public string? Recommendation { get; set; } - - /// - /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry. - /// - [JsonPropertyName("support.feline.policy.expiry.rev.2")] //stable prefix: expiry, msc pending - public long? Expiry { get; set; } - - //utils - /// - /// Readable expiry time, provided for easy interaction - /// - [JsonPropertyName("gay.rory.matrix_room_utils.readable_expiry_time_utc")] - public DateTime? ExpiryDateTime { - get => Expiry == null ? null : DateTimeOffset.FromUnixTimeMilliseconds(Expiry.Value).DateTime; - set => Expiry = ((DateTimeOffset)value).ToUnixTimeMilliseconds(); - } -} - -public static class PolicyRecommendationTypes { - /// - /// Ban this user - /// - public static string Ban = "m.ban"; - - /// - /// Mute this user - /// - public static string Mute = "support.feline.policy.recommendation_mute"; //stable prefix: m.mute, msc pending -} diff --git a/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs b/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs deleted file mode 100644 index b897ff0..0000000 --- a/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.presence")] -public class PresenceEventContent : EventContent { - [JsonPropertyName("presence")] - public string Presence { get; set; } - [JsonPropertyName("last_active_ago")] - public long LastActiveAgo { get; set; } - [JsonPropertyName("currently_active")] - public bool CurrentlyActive { get; set; } - [JsonPropertyName("status_msg")] - public string StatusMessage { get; set; } - [JsonPropertyName("avatar_url")] - public string AvatarUrl { get; set; } - [JsonPropertyName("displayname")] - public string DisplayName { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/ProfileResponseEventData.cs b/LibMatrix/StateEventTypes/Spec/ProfileResponseEventData.cs deleted file mode 100644 index 9b4f1d0..0000000 --- a/LibMatrix/StateEventTypes/Spec/ProfileResponseEventData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -public class ProfileResponseEventContent : EventContent { - [JsonPropertyName("avatar_url")] - public string? AvatarUrl { get; set; } - - [JsonPropertyName("displayname")] - public string? DisplayName { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs deleted file mode 100644 index d3960a1..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.alias")] -public class RoomAliasEventContent : EventContent { - [JsonPropertyName("aliases")] - public List? Aliases { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs deleted file mode 100644 index e2263c8..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.avatar")] -public class RoomAvatarEventContent : EventContent { - [JsonPropertyName("url")] - public string? Url { get; set; } - - [JsonPropertyName("info")] - public RoomAvatarInfo? Info { get; set; } - - public class RoomAvatarInfo { - [JsonPropertyName("h")] - public int? Height { get; set; } - - [JsonPropertyName("w")] - public int? Width { get; set; } - - [JsonPropertyName("mimetype")] - public string? MimeType { get; set; } - - [JsonPropertyName("size")] - public int? Size { get; set; } - } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs deleted file mode 100644 index 22df784..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.create")] -public class RoomCreateEventContent : EventContent { - [JsonPropertyName("room_version")] - public string? RoomVersion { get; set; } - [JsonPropertyName("creator")] - public string? Creator { get; set; } - [JsonPropertyName("m.federate")] - public bool? Federate { get; set; } - [JsonPropertyName("predecessor")] - public RoomCreatePredecessor? Predecessor { get; set; } - [JsonPropertyName("type")] - public string? Type { get; set; } - - public class RoomCreatePredecessor { - [JsonPropertyName("room_id")] - public string? RoomId { get; set; } - - [JsonPropertyName("event_id")] - public string? EventId { get; set; } - } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs deleted file mode 100644 index 1d5ec2c..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.encryption")] -public class RoomEncryptionEventContent : EventContent { - [JsonPropertyName("algorithm")] - public string? Algorithm { get; set; } - [JsonPropertyName("rotation_period_ms")] - public ulong? RotationPeriodMs { get; set; } - [JsonPropertyName("rotation_period_msgs")] - public ulong? RotationPeriodMsgs { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs deleted file mode 100644 index a9d4710..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.member")] -public class RoomMemberEventContent : EventContent { - [JsonPropertyName("reason")] - public string? Reason { get; set; } - - [JsonPropertyName("membership")] - public string Membership { get; set; } = null!; - - [JsonPropertyName("displayname")] - public string? Displayname { get; set; } - - [JsonPropertyName("is_direct")] - public bool? IsDirect { get; set; } - - [JsonPropertyName("avatar_url")] - public string? AvatarUrl { get; set; } - - [JsonPropertyName("kind")] - public string? Kind { get; set; } - - [JsonPropertyName("join_authorised_via_users_server")] - public string? JoinAuthorisedViaUsersServer { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs deleted file mode 100644 index a15efe8..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json.Serialization; -using ArcaneLibs.Extensions; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.message")] -public class RoomMessageEventContent : EventContent { - public RoomMessageEventContent(string? messageType = "m.notice", string? body = null) { - MessageType = messageType; - Body = body; - } - - [JsonPropertyName("body")] - public string Body { get; set; } - - [JsonPropertyName("msgtype")] - public string MessageType { get; set; } = "m.notice"; - - [JsonPropertyName("formatted_body")] - public string FormattedBody { get; set; } - - [JsonPropertyName("format")] - public string Format { get; set; } - - /// - /// Media URI for this message, if any - /// - [JsonPropertyName("url")] - public string? Url { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs deleted file mode 100644 index 3002102..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.name")] -public class RoomNameEventContent : EventContent { - [JsonPropertyName("name")] - public string? Name { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs deleted file mode 100644 index 16144bc..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.pinned_events")] -public class RoomPinnedEventContent : EventContent { - [JsonPropertyName("pinned")] - public string[]? PinnedEvents { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs deleted file mode 100644 index 960c198..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.power_levels")] -public class RoomPowerLevelEventContent : EventContent { - [JsonPropertyName("ban")] - public long Ban { get; set; } // = 50; - - [JsonPropertyName("events_default")] - public long EventsDefault { get; set; } // = 0; - - [JsonPropertyName("events")] - public Dictionary Events { get; set; } // = null!; - - [JsonPropertyName("invite")] - public long Invite { get; set; } // = 50; - - [JsonPropertyName("kick")] - public long Kick { get; set; } // = 50; - - [JsonPropertyName("notifications")] - public NotificationsPL NotificationsPl { get; set; } // = null!; - - [JsonPropertyName("redact")] - public long Redact { get; set; } // = 50; - - [JsonPropertyName("state_default")] - public long StateDefault { get; set; } // = 50; - - [JsonPropertyName("users")] - public Dictionary Users { get; set; } // = null!; - - [JsonPropertyName("users_default")] - public long UsersDefault { get; set; } // = 0; - - [Obsolete("Historical was a key related to MSC2716, a spec change on backfill that was dropped!", true)] - [JsonIgnore] - [JsonPropertyName("historical")] - public long Historical { get; set; } // = 50; - - public class NotificationsPL { - [JsonPropertyName("room")] - public long Room { get; set; } = 50; - } - - public bool IsUserAdmin(string userId) { - return Users.TryGetValue(userId, out var level) && level >= Events.Max(x=>x.Value); - } - - public bool UserHasPermission(string userId, string eventType) { - return Users.TryGetValue(userId, out var level) && level >= Events.GetValueOrDefault(eventType, EventsDefault); - } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs deleted file mode 100644 index 61d1a01..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.topic")] -[MatrixEvent(EventName = "org.matrix.msc3765.topic", Legacy = true)] -public class RoomTopicEventContent : EventContent { - [JsonPropertyName("topic")] - public string? Topic { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs deleted file mode 100644 index e935cb2..0000000 --- a/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.typing")] -public class RoomTypingEventContent : EventContent { - [JsonPropertyName("user_ids")] - public string[]? UserIds { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs b/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs deleted file mode 100644 index 031d113..0000000 --- a/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.room.server_acl")] -public class ServerACLEventContent : EventContent { - [JsonPropertyName("allow")] - public List Allow { get; set; } // = null!; - - [JsonPropertyName("deny")] - public List Deny { get; set; } // = null!; - - [JsonPropertyName("allow_ip_literals")] - public bool AllowIpLiterals { get; set; } // = false; -} diff --git a/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs b/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs deleted file mode 100644 index 80fc771..0000000 --- a/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.space.child")] -public class SpaceChildEventContent : EventContent { - [JsonPropertyName("auto_join")] - public bool? AutoJoin { get; set; } - [JsonPropertyName("via")] - public string[]? Via { get; set; } - [JsonPropertyName("suggested")] - public bool? Suggested { get; set; } -} diff --git a/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs b/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs deleted file mode 100644 index 1bc89ab..0000000 --- a/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; -using LibMatrix.Helpers; -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes.Spec; - -[MatrixEvent(EventName = "m.space.parent")] -public class SpaceParentEventContent : EventContent { - [JsonPropertyName("via")] - public string[]? Via { get; set; } - - [JsonPropertyName("canonical")] - public bool? Canonical { get; set; } -} diff --git a/LibMatrix/StateEventTypes/UnknownStateEventData.cs b/LibMatrix/StateEventTypes/UnknownStateEventData.cs deleted file mode 100644 index 59d8fd4..0000000 --- a/LibMatrix/StateEventTypes/UnknownStateEventData.cs +++ /dev/null @@ -1,7 +0,0 @@ -using LibMatrix.Interfaces; - -namespace LibMatrix.StateEventTypes; - -public class UnknownEventContent : EventContent { - -} diff --git a/Tests/LibMatrix.Tests/Config.cs b/Tests/LibMatrix.Tests/Config.cs new file mode 100644 index 0000000..fb4ef02 --- /dev/null +++ b/Tests/LibMatrix.Tests/Config.cs @@ -0,0 +1,17 @@ +namespace LibMatrix.Tests; + +public class Config { + public string? TestHomeserver { get; set; } = Environment.GetEnvironmentVariable("LIBMATRIX_TEST_HOMESERVER") ?? null; + public string? TestUsername { get; set; } = Environment.GetEnvironmentVariable("LIBMATRIX_TEST_USERNAME") ?? null; + public string? TestPassword { get; set; } = Environment.GetEnvironmentVariable("LIBMATRIX_TEST_PASSWORD") ?? null; + public string? TestRoomId { get; set; } = Environment.GetEnvironmentVariable("LIBMATRIX_TEST_ROOM_ID") ?? null; + public string? TestRoomAlias { get; set; } = Environment.GetEnvironmentVariable("LIBMATRIX_TEST_ROOM_ALIAS") ?? null; + + public Dictionary ExpectedHomeserverMappings { get; set; } = new() { + {"matrix.org", "https://matrix-client.matrix.org"}, + {"rory.gay", "https://matrix.rory.gay"} + }; + public Dictionary ExpectedAliasMappings { get; set; } = new() { + {"#libmatrix:rory.gay", "!tuiLEoMqNOQezxILzt:rory.gay"} + }; +} diff --git a/Tests/LibMatrix.Tests/Fixtures/TestFixture.cs b/Tests/LibMatrix.Tests/Fixtures/TestFixture.cs new file mode 100644 index 0000000..ef49b3e --- /dev/null +++ b/Tests/LibMatrix.Tests/Fixtures/TestFixture.cs @@ -0,0 +1,37 @@ +using ArcaneLibs.Extensions; +using LibMatrix.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Microsoft.DependencyInjection; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace LibMatrix.Tests.Fixtures; + +public class TestFixture : TestBedFixture { + protected override void AddServices(IServiceCollection services, IConfiguration? configuration) { + services.AddSingleton(x => + new TieredStorageService( + cacheStorageProvider: null, + dataStorageProvider: null + ) + ); + + services.AddRoryLibMatrixServices(); + + services.AddSingleton(config => { + var conf = new Config(); + configuration?.GetSection("Configuration").Bind(conf); + + File.WriteAllText("configuration.json", conf.ToJson()); + + return conf; + }); + } + + protected override ValueTask DisposeAsyncCore() + => new(); + + protected override IEnumerable GetTestAppSettings() { + yield return new TestAppSettings { Filename = "appsettings.json", IsOptional = true }; + } +} diff --git a/Tests/LibMatrix.Tests/LibMatrix.Tests.csproj b/Tests/LibMatrix.Tests/LibMatrix.Tests.csproj index ad9b2fa..630273b 100644 --- a/Tests/LibMatrix.Tests/LibMatrix.Tests.csproj +++ b/Tests/LibMatrix.Tests/LibMatrix.Tests.csproj @@ -10,11 +10,11 @@ + + - + - - runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -23,10 +23,17 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + Always + + + diff --git a/Tests/LibMatrix.Tests/ResolverTest.cs b/Tests/LibMatrix.Tests/ResolverTest.cs deleted file mode 100644 index b1191fe..0000000 --- a/Tests/LibMatrix.Tests/ResolverTest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using LibMatrix.Services; - -namespace LibMatrix.Tests; - -public class ResolverTest { - [Fact] - public void ResolveServer() { - - } -} diff --git a/Tests/LibMatrix.Tests/Tests/AuthTests.cs b/Tests/LibMatrix.Tests/Tests/AuthTests.cs new file mode 100644 index 0000000..72a509d --- /dev/null +++ b/Tests/LibMatrix.Tests/Tests/AuthTests.cs @@ -0,0 +1,52 @@ +using LibMatrix.Services; +using LibMatrix.Tests.Fixtures; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace LibMatrix.Tests.Tests; + +public class AuthTests : TestBed { + private readonly TestFixture _fixture; + private readonly HomeserverResolverService _resolver; + private readonly Config _config; + private readonly HomeserverProviderService _provider; + + public AuthTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : base(testOutputHelper, fixture) { + _fixture = fixture; + _resolver = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverResolverService)}"); + _config = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + _provider = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverProviderService)}"); + } + + [Fact] + public async Task LoginWithPassword() { + Assert.False(string.IsNullOrWhiteSpace(_config.TestHomeserver), $"{nameof(_config.TestHomeserver)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestUsername), $"{nameof(_config.TestUsername)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestPassword), $"{nameof(_config.TestPassword)} must be set in appsettings!"); + + // var server = await _resolver.ResolveHomeserverFromWellKnown(_config.TestHomeserver!); + var login = await _provider.Login(_config.TestHomeserver!, _config.TestUsername!, _config.TestPassword!); + Assert.NotNull(login); + var hs = await _provider.GetAuthenticatedWithToken(_config.TestHomeserver!, login.AccessToken); + Assert.NotNull(hs); + await hs.Logout(); + } + + [Fact] + public async Task LoginWithToken() { + Assert.False(string.IsNullOrWhiteSpace(_config.TestHomeserver), $"{nameof(_config.TestHomeserver)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestUsername), $"{nameof(_config.TestUsername)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestPassword), $"{nameof(_config.TestPassword)} must be set in appsettings!"); + + // var server = await _resolver.ResolveHomeserverFromWellKnown(_config.TestHomeserver!); + var login = await _provider.Login(_config.TestHomeserver!, _config.TestUsername!, _config.TestPassword!); + Assert.NotNull(login); + + var hs = await _provider.GetAuthenticatedWithToken(_config.TestHomeserver!, login.AccessToken); + Assert.NotNull(hs); + Assert.NotNull(hs.WhoAmI); + Assert.NotNull(hs.UserId); + Assert.NotNull(hs.AccessToken); + await hs.Logout(); + } +} diff --git a/Tests/LibMatrix.Tests/Tests/ResolverTest.cs b/Tests/LibMatrix.Tests/Tests/ResolverTest.cs new file mode 100644 index 0000000..345508a --- /dev/null +++ b/Tests/LibMatrix.Tests/Tests/ResolverTest.cs @@ -0,0 +1,54 @@ +using LibMatrix.Services; +using LibMatrix.Tests.Fixtures; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace LibMatrix.Tests.Tests; + +public class ResolverTest : TestBed { + private readonly TestFixture _fixture; + private readonly HomeserverResolverService _resolver; + private readonly Config _config; + private readonly HomeserverProviderService _provider; + public ResolverTest(ITestOutputHelper testOutputHelper, TestFixture fixture) : base(testOutputHelper, fixture) { + _fixture = fixture; + _resolver = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverResolverService)}"); + _config = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + _provider = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverProviderService)}"); + } + + [Fact] + public async Task ResolveServer() { + foreach (var (domain, expected) in _config.ExpectedHomeserverMappings) { + var server = await _resolver.ResolveHomeserverFromWellKnown(domain); + Assert.Equal(expected, server); + } + } + + [Fact] + public async Task ResolveMedia() { + var media = await _resolver.ResolveMediaUri("matrix.org", "mxc://matrix.org/eqwrRZRoPpNbcMeUwyXAuVRo"); + Assert.Equal("https://matrix-client.matrix.org/_matrix/media/v3/download/matrix.org/eqwrRZRoPpNbcMeUwyXAuVRo", media); + } + + [Fact] + public async Task ResolveRoomAliasAsync() { + var hs = await _provider.GetRemoteHomeserver("matrix.org"); + var alias = await hs.ResolveRoomAliasAsync("#matrix:matrix.org"); + Assert.Equal("!OGEhHVWSdvArJzumhm:matrix.org", alias.RoomId); + } + + [Fact] + public async Task GetClientVersionsAsync() { + var hs = await _provider.GetRemoteHomeserver("matrix.org"); + var versions = await hs.GetClientVersionsAsync(); + Assert.NotNull(versions); + } + + [Fact] + public async Task GetProfileAsync() { + var hs = await _provider.GetRemoteHomeserver("matrix.org"); + var profile = await hs.GetProfileAsync("@alice-is-:matrix.org"); + Assert.NotNull(profile); + } +} diff --git a/Tests/LibMatrix.Tests/Tests/RoomTests.cs b/Tests/LibMatrix.Tests/Tests/RoomTests.cs new file mode 100644 index 0000000..ef63ec9 --- /dev/null +++ b/Tests/LibMatrix.Tests/Tests/RoomTests.cs @@ -0,0 +1,332 @@ +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using LibMatrix.Tests.Fixtures; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace LibMatrix.Tests.Tests; + +public class RoomTests : TestBed { + private readonly TestFixture _fixture; + private readonly HomeserverResolverService _resolver; + private readonly Config _config; + private readonly HomeserverProviderService _provider; + + public RoomTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : base(testOutputHelper, fixture) { + _fixture = fixture; + _resolver = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverResolverService)}"); + _config = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + _provider = _fixture.GetService(_testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(HomeserverProviderService)}"); + } + + private async Task GetHomeserver() { + Assert.False(string.IsNullOrWhiteSpace(_config.TestHomeserver), $"{nameof(_config.TestHomeserver)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestUsername), $"{nameof(_config.TestUsername)} must be set in appsettings!"); + Assert.False(string.IsNullOrWhiteSpace(_config.TestPassword), $"{nameof(_config.TestPassword)} must be set in appsettings!"); + + // var server = await _resolver.ResolveHomeserverFromWellKnown(_config.TestHomeserver!); + var login = await _provider.Login(_config.TestHomeserver!, _config.TestUsername!, _config.TestPassword!); + Assert.NotNull(login); + + var hs = await _provider.GetAuthenticatedWithToken(_config.TestHomeserver!, login.AccessToken); + return hs; + } + + [Fact] + public async Task GetJoinedRoomsAsync() { + var hs = await GetHomeserver(); + + var rooms = await hs.GetJoinedRooms(); + Assert.NotNull(rooms); + Assert.NotEmpty(rooms); + Assert.All(rooms, Assert.NotNull); + + await hs.Logout(); + } + + [Fact] + public async Task GetNameAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var name = await room.GetNameAsync(); + Assert.NotNull(name); + Assert.NotEmpty(name); + } + + [SkippableFact(typeof(MatrixException))] + public async Task GetTopicAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var topic = await room.GetTopicAsync(); + Assert.NotNull(topic); + Assert.NotNull(topic.Topic); + Assert.NotEmpty(topic.Topic); + } + + [Fact] + public async Task GetMembersAsync() { + Assert.True(StateEvent.KnownStateEventTypes is { Count: > 0 }, "StateEvent.KnownStateEventTypes is empty!"); + Assert.True(StateEvent.KnownStateEventTypesByName is { Count: > 0 }, "StateEvent.KnownStateEventTypesByName is empty!"); + + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var members = room.GetMembersAsync(); + Assert.NotNull(members); + bool hitMembers = false; + await foreach (var member in members) { + Assert.NotNull(member); + Assert.NotNull(member.StateKey); + Assert.NotEmpty(member.StateKey); + Assert.NotNull(member.Sender); + Assert.NotEmpty(member.Sender); + Assert.NotNull(member.RawContent); + Assert.NotEmpty(member.RawContent); + Assert.NotNull(member.TypedContent); + Assert.IsType(member.TypedContent); + var content = (RoomMemberEventContent)member.TypedContent; + Assert.NotNull(content); + Assert.NotNull(content.Membership); + Assert.NotEmpty(content.Membership); + hitMembers = true; + } + + Assert.True(hitMembers, "No members were found in the room"); + } + + /* + tests remaining: + GetStateAsync(string,string) 0% 8/8 + GetMessagesAsync(string,int,string,string) 0% 7/7 + JoinAsync(string[],string) 0% 8/8 + SendMessageEventAsync(RoomMessageEventContent) 0% 1/1 + GetAliasesAsync() 0% 4/4 + GetCanonicalAliasAsync() 0% 1/1 + GetAvatarUrlAsync() 0% 1/1 + GetJoinRuleAsync() 0% 1/1 + GetHistoryVisibilityAsync() 0% 1/1 + GetGuestAccessAsync() 0% 1/1 + GetCreateEventAsync() 0% 1/1 + GetRoomType() 0% 4/4 + GetPowerLevelsAsync() 0% 1/1 + ForgetAsync() 0% 1/1 + LeaveAsync(string) 0% 1/1 + KickAsync(string,string) 0% 1/1 + BanAsync(string,string) 0% 1/1 + UnbanAsync(string) 0% 1/1 + SendStateEventAsync(string,object) 0% 1/1 + SendStateEventAsync(string,string,object) 0% 1/1 + SendTimelineEventAsync(string,EventContent) 0% 5/5 + SendFileAsync(string,string,Stream) 0% 6/6 + GetRoomAccountData(string) 0% 8/8 + SetRoomAccountData(string,object) 0% 7/7 + GetEvent(string) 0% 3/3 + RedactEventAsync(string,string) 0% 4/4 + InviteUser(string,string) 0% 3/3 + */ + + [Fact] + public async Task JoinAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var id = await room.JoinAsync(); + Assert.NotNull(id); + Assert.NotNull(id.RoomId); + Assert.NotEmpty(id.RoomId); + } + + [SkippableFact(typeof(MatrixException))] + public async Task GetAliasesAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var aliases = await room.GetAliasesAsync(); + Assert.NotNull(aliases); + Assert.NotEmpty(aliases); + Assert.All(aliases, Assert.NotNull); + } + + [SkippableFact(typeof(MatrixException))] + public async Task GetCanonicalAliasAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var alias = await room.GetCanonicalAliasAsync(); + Assert.NotNull(alias); + Assert.NotNull(alias.Alias); + Assert.NotEmpty(alias.Alias); + } + + [SkippableFact(typeof(MatrixException))] + public async Task GetAvatarUrlAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var url = await room.GetAvatarUrlAsync(); + Assert.NotNull(url); + Assert.NotNull(url.Url); + Assert.NotEmpty(url.Url); + } + + [Fact] + public async Task GetJoinRuleAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var rule = await room.GetJoinRuleAsync(); + Assert.NotNull(rule); + Assert.NotNull(rule.JoinRule); + Assert.NotEmpty(rule.JoinRule); + } + + [Fact] + public async Task GetHistoryVisibilityAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var visibility = await room.GetHistoryVisibilityAsync(); + Assert.NotNull(visibility); + Assert.NotNull(visibility.HistoryVisibility); + Assert.NotEmpty(visibility.HistoryVisibility); + } + + [Fact] + public async Task GetGuestAccessAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + try { + var access = await room.GetGuestAccessAsync(); + Assert.NotNull(access); + Assert.NotNull(access.GuestAccess); + Assert.NotEmpty(access.GuestAccess); + } + catch (Exception e) { + if(e is not MatrixException exception) throw; + Assert.Equal("M_NOT_FOUND", exception.ErrorCode); + } + } + + [Fact] + public async Task GetCreateEventAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var create = await room.GetCreateEventAsync(); + Assert.NotNull(create); + Assert.NotNull(create.Creator); + Assert.NotEmpty(create.RoomVersion!); + } + + [Fact] + public async Task GetRoomType() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + await room.GetRoomType(); + } + + [Fact] + public async Task GetPowerLevelsAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var power = await room.GetPowerLevelsAsync(); + Assert.NotNull(power); + Assert.NotNull(power.Ban); + Assert.NotNull(power.Kick); + Assert.NotNull(power.Invite); + Assert.NotNull(power.Redact); + Assert.NotNull(power.StateDefault); + Assert.NotNull(power.EventsDefault); + Assert.NotNull(power.UsersDefault); + Assert.NotNull(power.Users); + Assert.NotNull(power.Events); + } + + [Fact(Skip = "This test is destructive!")] + public async Task ForgetAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + await room.ForgetAsync(); + } + + [Fact(Skip = "This test is destructive!")] + public async Task LeaveAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + await room.LeaveAsync(); + } + + [Fact(Skip = "This test is destructive!")] + public async Task KickAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + // await room.KickAsync(_config.TestUserId, "test"); + } + + [Fact(Skip = "This test is destructive!")] + public async Task BanAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + // await room.BanAsync(_config.TestUserId, "test"); + } + + [Fact(Skip = "This test is destructive!")] + public async Task UnbanAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + // await room.UnbanAsync(_config.TestUserId); + } + + [SkippableFact(typeof(MatrixException))] + public async Task SendStateEventAsync() { + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + await room.SendStateEventAsync("gay.rory.libmatrix.unit_tests", new ProfileResponseEventContent() { + DisplayName = "wee_woo", + AvatarUrl = "no" + }); + await room.SendStateEventAsync("gay.rory.libmatrix.unit_tests", "state_key_maybe", new ProfileResponseEventContent() { + DisplayName = "wee_woo", + AvatarUrl = "yes" + }); + } + + [SkippableFact(typeof(MatrixException))] + public async Task GetStateEventAsync() { + await SendStateEventAsync(); + + var hs = await GetHomeserver(); + var room = hs.GetRoom(_config.TestRoomId); + Assert.NotNull(room); + var state1 = await room.GetStateAsync("gay.rory.libmatrix.unit_tests"); + Assert.NotNull(state1); + Assert.NotNull(state1.DisplayName); + Assert.NotEmpty(state1.DisplayName); + Assert.NotNull(state1.AvatarUrl); + Assert.NotEmpty(state1.AvatarUrl); + Assert.Equal("wee_woo", state1.DisplayName); + Assert.Equal("no", state1.AvatarUrl); + + var state2 = await room.GetStateAsync("gay.rory.libmatrix.unit_tests", "state_key_maybe"); + Assert.NotNull(state2); + Assert.NotNull(state2.DisplayName); + Assert.NotEmpty(state2.DisplayName); + Assert.NotNull(state2.AvatarUrl); + Assert.NotEmpty(state2.AvatarUrl); + Assert.Equal("wee_woo", state2.DisplayName); + Assert.Equal("yes", state2.AvatarUrl); + } +} diff --git a/Tests/TestDataGenerator/Bot/DataFetcher.cs b/Tests/TestDataGenerator/Bot/DataFetcher.cs new file mode 100644 index 0000000..b1f8402 --- /dev/null +++ b/Tests/TestDataGenerator/Bot/DataFetcher.cs @@ -0,0 +1,69 @@ +using System.Text; +using System.Threading.Channels; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.Interfaces; +using LibMatrix.RoomTypes; +using LibMatrix.Services; +using LibMatrix.Tests; +using LibMatrix.Utilities.Bot; +using LibMatrix.Utilities.Bot.Interfaces; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace PluralContactBotPoC.Bot; + +public class DataFetcher(AuthenticatedHomeserverGeneric hs, ILogger logger, LibMatrixBotConfiguration botConfiguration, + // DataFetcherConfiguration configuration, + HomeserverResolverService hsResolver) : IHostedService { + private Task _listenerTask; + + private GenericRoom? _logRoom; + + /// Triggered when the application host is ready to start the service. + /// Indicates that the start process has been aborted. + public async Task StartAsync(CancellationToken cancellationToken) { + _listenerTask = Run(cancellationToken); + logger.LogInformation("Bot started!"); + } + + private async Task Run(CancellationToken cancellationToken) { + Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); + _logRoom = hs.GetRoom(botConfiguration.LogRoom); + + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: "Test data collector started!")); + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: "Fetching rooms...")); + + var rooms = await hs.GetJoinedRooms(); + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: $"Fetched {rooms.Count} rooms!")); + + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: "Fetching room data...")); + + Config cfg = new Config(); + + var roomAliasTasks = rooms.Select(room => room.GetCanonicalAliasAsync()).ToAsyncEnumerable(); + List> aliasResolutionTasks = new(); + await foreach (var @event in roomAliasTasks) { + if (@event?.Alias != null) { + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: $"Fetched room alias {(@event).Alias}!")); + aliasResolutionTasks.Add(Task<(string, string)>.Run(async () => { + var alias = await hs.ResolveRoomAliasAsync(@event.Alias); + return (@event.Alias, @alias.RoomId); + }, cancellationToken)); + } + } + var aliasResolutionTaskEnumerator = aliasResolutionTasks.ToAsyncEnumerable(); + await foreach (var result in aliasResolutionTaskEnumerator) { + await _logRoom.SendMessageEventAsync(new RoomMessageEventContent(body: $"Resolved room alias {result.Item1} to {result.Item2}!")); + } + } + + /// Triggered when the application host is performing a graceful shutdown. + /// Indicates that the shutdown process should no longer be graceful. + public async Task StopAsync(CancellationToken cancellationToken) { + logger.LogInformation("Shutting down bot!"); + } +} diff --git a/Tests/TestDataGenerator/Bot/DataFetcherConfiguration.cs b/Tests/TestDataGenerator/Bot/DataFetcherConfiguration.cs new file mode 100644 index 0000000..06df0eb --- /dev/null +++ b/Tests/TestDataGenerator/Bot/DataFetcherConfiguration.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Configuration; + +namespace PluralContactBotPoC.Bot; + +public class DataFetcherConfiguration { + public DataFetcherConfiguration(IConfiguration config) => config.GetRequiredSection("DataFetcher").Bind(this); + + // public string +} diff --git a/Tests/TestDataGenerator/Program.cs b/Tests/TestDataGenerator/Program.cs new file mode 100644 index 0000000..9bd091b --- /dev/null +++ b/Tests/TestDataGenerator/Program.cs @@ -0,0 +1,31 @@ +// See https://aka.ms/new-console-template for more information + +using System.Text.Json; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; +using LibMatrix.Services; +using LibMatrix.Utilities.Bot; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using PluralContactBotPoC; +using PluralContactBotPoC.Bot; + +Console.WriteLine("Hello, World!"); + +var host = Host.CreateDefaultBuilder(args).ConfigureServices((_, services) => { + services.AddScoped(x => + new TieredStorageService( + cacheStorageProvider: new FileStorageProvider("bot_data/cache/"), + dataStorageProvider: new FileStorageProvider("bot_data/data/") + ) + ); + // services.AddSingleton(); + services.AddSingleton(); + + services.AddRoryLibMatrixServices(); + services.AddBot(withCommands: false); + + services.AddHostedService(); +}).UseConsoleLifetime().Build(); + +await host.RunAsync(); diff --git a/Tests/TestDataGenerator/Properties/launchSettings.json b/Tests/TestDataGenerator/Properties/launchSettings.json new file mode 100644 index 0000000..997e294 --- /dev/null +++ b/Tests/TestDataGenerator/Properties/launchSettings.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Default": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + + } + }, + "Development": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + }, + "Local config": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Local" + } + } + } +} diff --git a/Tests/TestDataGenerator/TestDataGenerator.csproj b/Tests/TestDataGenerator/TestDataGenerator.csproj new file mode 100644 index 0000000..1b5a4a9 --- /dev/null +++ b/Tests/TestDataGenerator/TestDataGenerator.csproj @@ -0,0 +1,32 @@ + + + + Exe + net7.0 + preview + enable + enable + false + true + + + + + + + + + + + + + + Always + + + + + + + + diff --git a/Tests/TestDataGenerator/appsettings.Development.json b/Tests/TestDataGenerator/appsettings.Development.json new file mode 100644 index 0000000..38c45c4 --- /dev/null +++ b/Tests/TestDataGenerator/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "LibMatrixBot": { + // The homeserver to connect to + "Homeserver": "rory.gay", + // The access token to use + "AccessToken": "syt_xxxxxxxxxxxxxxxxx", + // The command prefix + "Prefix": "?", + "LogRoom": "!xxxxxxxxxxxxxxxxxxxxxx:example.com" + } +} diff --git a/Tests/TestDataGenerator/appsettings.json b/Tests/TestDataGenerator/appsettings.json new file mode 100644 index 0000000..6ba02f3 --- /dev/null +++ b/Tests/TestDataGenerator/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs index 4de139e..99a789a 100644 --- a/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs +++ b/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs @@ -1,4 +1,4 @@ -namespace PluralContactBotPoC; +namespace LibMatrix.Utilities.Bot; public class AppServiceConfiguration { public string Id { get; set; } = null!; diff --git a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs index a13f1bd..16e1444 100644 --- a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs +++ b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs @@ -3,9 +3,8 @@ using ArcaneLibs.Extensions; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.Services; -using LibMatrix.StateEventTypes.Spec; +using LibMatrix.Utilities.Bot.Interfaces; using LibMatrix.Utilities.Bot.Services; -using MediaModeratorPoC.Bot.Interfaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/Utilities/LibMatrix.Utilities.Bot/Commands/HelpCommand.cs b/Utilities/LibMatrix.Utilities.Bot/Commands/HelpCommand.cs index de033ef..4fe1038 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Commands/HelpCommand.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Commands/HelpCommand.cs @@ -1,9 +1,9 @@ using System.Text; -using LibMatrix.StateEventTypes.Spec; -using MediaModeratorPoC.Bot.Interfaces; +using LibMatrix.EventTypes.Spec; +using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.DependencyInjection; -namespace MediaModeratorPoC.Bot.Commands; +namespace LibMatrix.Utilities.Bot.Commands; public class HelpCommand(IServiceProvider services) : ICommand { public string Name { get; } = "help"; @@ -17,6 +17,6 @@ public class HelpCommand(IServiceProvider services) : ICommand { sb.AppendLine($"- {command.Name}: {command.Description}"); } - await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent(messageType: "m.notice", body: sb.ToString())); + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(messageType: "m.notice", body: sb.ToString())); } } diff --git a/Utilities/LibMatrix.Utilities.Bot/Commands/PingCommand.cs b/Utilities/LibMatrix.Utilities.Bot/Commands/PingCommand.cs index b008be9..16712ea 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Commands/PingCommand.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Commands/PingCommand.cs @@ -1,13 +1,13 @@ -using LibMatrix.StateEventTypes.Spec; -using MediaModeratorPoC.Bot.Interfaces; +using LibMatrix.EventTypes.Spec; +using LibMatrix.Utilities.Bot.Interfaces; -namespace MediaModeratorPoC.Bot.Commands; +namespace LibMatrix.Utilities.Bot.Commands; public class PingCommand : ICommand { public string Name { get; } = "ping"; public string Description { get; } = "Pong!"; public async Task Invoke(CommandContext ctx) { - await ctx.Room.SendMessageEventAsync("m.room.message", new RoomMessageEventContent(body: "pong!")); + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "pong!")); } } diff --git a/Utilities/LibMatrix.Utilities.Bot/FileStorageProvider.cs b/Utilities/LibMatrix.Utilities.Bot/FileStorageProvider.cs index d5b991a..39b66e3 100644 --- a/Utilities/LibMatrix.Utilities.Bot/FileStorageProvider.cs +++ b/Utilities/LibMatrix.Utilities.Bot/FileStorageProvider.cs @@ -3,7 +3,7 @@ using ArcaneLibs.Extensions; using LibMatrix.Interfaces.Services; using Microsoft.Extensions.Logging; -namespace MediaModeratorPoC.Bot; +namespace LibMatrix.Utilities.Bot; public class FileStorageProvider : IStorageProvider { private readonly ILogger _logger; diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs index bdb93d5..97984fd 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/CommandContext.cs @@ -1,10 +1,9 @@ -using LibMatrix; +using LibMatrix.EventTypes.Spec; using LibMatrix.Homeservers; using LibMatrix.Responses; using LibMatrix.RoomTypes; -using LibMatrix.StateEventTypes.Spec; -namespace MediaModeratorPoC.Bot.Interfaces; +namespace LibMatrix.Utilities.Bot.Interfaces; public class CommandContext { public GenericRoom Room { get; set; } @@ -20,5 +19,5 @@ public class CommandContext { public string[] Args => MessageContentWithoutReply.Split(' ')[1..]; public AuthenticatedHomeserverGeneric Homeserver { get; set; } - public async Task Reply(string eventType, RoomMessageEventContent content) => await Room.SendMessageEventAsync(eventType, content); + public async Task Reply(RoomMessageEventContent content) => await Room.SendMessageEventAsync(content); } diff --git a/Utilities/LibMatrix.Utilities.Bot/Interfaces/ICommand.cs b/Utilities/LibMatrix.Utilities.Bot/Interfaces/ICommand.cs index a8fce94..7065683 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Interfaces/ICommand.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Interfaces/ICommand.cs @@ -1,4 +1,4 @@ -namespace MediaModeratorPoC.Bot.Interfaces; +namespace LibMatrix.Utilities.Bot.Interfaces; public interface ICommand { public string Name { get; } diff --git a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs index df702f4..910db0a 100644 --- a/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs +++ b/Utilities/LibMatrix.Utilities.Bot/Services/CommandListenerHostedService.cs @@ -1,7 +1,7 @@ +using LibMatrix.EventTypes.Spec; using LibMatrix.Helpers; using LibMatrix.Homeservers; -using LibMatrix.StateEventTypes.Spec; -using MediaModeratorPoC.Bot.Interfaces; +using LibMatrix.Utilities.Bot.Interfaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -39,7 +39,7 @@ public class CommandListenerHostedService : IHostedService { _logger.LogInformation("Starting command listener!"); _hs.SyncHelper.TimelineEventHandlers.Add(async @event => { try { - var room = await _hs.GetRoom(@event.RoomId); + 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" }) { @@ -48,7 +48,7 @@ public class CommandListenerHostedService : IHostedService { if (messageContentWithoutReply.StartsWith(_config.Prefix)) { var command = _commands.FirstOrDefault(x => x.Name == messageContentWithoutReply.Split(' ')[0][_config.Prefix.Length..]); if (command == null) { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent(messageType: "m.notice", body: "Command not found!")); return; } @@ -64,12 +64,12 @@ public class CommandListenerHostedService : IHostedService { await command.Invoke(ctx); } catch (Exception e) { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( MessageFormatter.FormatException("An error occurred during the execution of this command", e)); } } else { - await room.SendMessageEventAsync("m.room.message", + await room.SendMessageEventAsync( new RoomMessageEventContent(messageType: "m.notice", body: "You do not have permission to run this command!")); } } -- cgit 1.4.1