From 3ed00f732a284b5a3e96e52d4e3a71869135869b Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Mon, 26 Jun 2023 02:43:54 +0200 Subject: Dependency injection stuff --- .gitignore | 2 +- MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs | 49 +++++ MatrixRoomUtils.Bot/Bot/Commands/HelpCommand.cs | 28 +++ MatrixRoomUtils.Bot/Bot/Commands/PingCommand.cs | 19 ++ MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs | 31 +++ .../Bot/Interfaces/CommandContext.cs | 11 + MatrixRoomUtils.Bot/Bot/Interfaces/ICommand.cs | 12 + MatrixRoomUtils.Bot/Bot/MRUBot.cs | 115 ++++++++++ MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs | 12 + MatrixRoomUtils.Bot/FileStorageProvider.cs | 31 --- MatrixRoomUtils.Bot/MRUBot.cs | 87 -------- MatrixRoomUtils.Bot/MRUBotConfiguration.cs | 11 - MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj | 5 + MatrixRoomUtils.Bot/Program.cs | 6 + MatrixRoomUtils.Core/AuthenticatedHomeServer.cs | 10 +- MatrixRoomUtils.Core/Authentication/MatrixAuth.cs | 7 +- .../Extensions/IEnumerableExtensions.cs | 31 +-- .../Extensions/JsonElementExtensions.cs | 8 +- MatrixRoomUtils.Core/Helpers/SyncHelper.cs | 5 +- MatrixRoomUtils.Core/Interfaces/IHomeServer.cs | 20 -- MatrixRoomUtils.Core/JoinRules.cs | 15 -- MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj | 7 +- MatrixRoomUtils.Core/RemoteHomeServer.cs | 16 -- .../Responses/StateEventResponse.cs | 4 +- MatrixRoomUtils.Core/Room.cs | 216 ------------------ MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs | 243 +++++++++++++++++++++ MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs | 14 +- MatrixRoomUtils.Core/RuntimeCache.cs | 99 --------- MatrixRoomUtils.Core/StateEvent.cs | 66 +++++- .../StateEventTypes/CanonicalAliasEventData.cs | 11 - .../StateEventTypes/Common/RoomEmotesEventData.cs | 26 +++ .../StateEventTypes/GuestAccessData.cs | 16 -- .../StateEventTypes/HistoryVisibilityData.cs | 9 - .../StateEventTypes/MemberEventData.cs | 23 -- .../StateEventTypes/MessageEventData.cs | 11 - .../StateEventTypes/PolicyRuleStateEventData.cs | 52 ----- .../StateEventTypes/PowerLevelEvent.cs | 43 ---- .../StateEventTypes/PresenceStateEventData.cs | 17 -- .../StateEventTypes/ProfileResponse.cs | 13 -- .../StateEventTypes/RoomAvatarEventData.cs | 18 -- .../StateEventTypes/RoomTopicEventData.cs | 11 - .../StateEventTypes/ServerACLData.cs | 17 -- .../Spec/CanonicalAliasEventData.cs | 13 ++ .../StateEventTypes/Spec/GuestAccessData.cs | 16 ++ .../StateEventTypes/Spec/HistoryVisibilityData.cs | 11 + .../StateEventTypes/Spec/JoinRulesEventData.cs | 18 ++ .../StateEventTypes/Spec/MessageEventData.cs | 17 ++ .../Spec/PolicyRuleStateEventData.cs | 56 +++++ .../StateEventTypes/Spec/PowerLevelEvent.cs | 48 ++++ .../StateEventTypes/Spec/PresenceStateEventData.cs | 17 ++ .../StateEventTypes/Spec/ProfileResponse.cs | 13 ++ .../StateEventTypes/Spec/RoomAvatarEventData.cs | 18 ++ .../StateEventTypes/Spec/RoomCreateEventData.cs | 21 ++ .../Spec/RoomEncryptionEventData.cs | 15 ++ .../StateEventTypes/Spec/RoomMemberEventData.cs | 29 +++ .../StateEventTypes/Spec/RoomNameEventData.cs | 11 + .../StateEventTypes/Spec/RoomPinnedEventData.cs | 11 + .../StateEventTypes/Spec/RoomTopicEventData.cs | 12 + .../StateEventTypes/Spec/RoomTypingEventData.cs | 11 + .../StateEventTypes/Spec/ServerACLData.cs | 17 ++ .../StateEventTypes/Spec/SpaceChildEventData.cs | 15 ++ .../StateEventTypes/Spec/SpaceParentEventData.cs | 14 ++ MatrixRoomUtils.sln.DotSettings.user | 1 + 63 files changed, 1027 insertions(+), 804 deletions(-) create mode 100644 MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs create mode 100644 MatrixRoomUtils.Bot/Bot/Commands/HelpCommand.cs create mode 100644 MatrixRoomUtils.Bot/Bot/Commands/PingCommand.cs create mode 100644 MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs create mode 100644 MatrixRoomUtils.Bot/Bot/Interfaces/CommandContext.cs create mode 100644 MatrixRoomUtils.Bot/Bot/Interfaces/ICommand.cs create mode 100644 MatrixRoomUtils.Bot/Bot/MRUBot.cs create mode 100644 MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs delete mode 100644 MatrixRoomUtils.Bot/FileStorageProvider.cs delete mode 100644 MatrixRoomUtils.Bot/MRUBot.cs delete mode 100644 MatrixRoomUtils.Bot/MRUBotConfiguration.cs delete mode 100644 MatrixRoomUtils.Core/JoinRules.cs delete mode 100644 MatrixRoomUtils.Core/Room.cs create mode 100644 MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs delete mode 100644 MatrixRoomUtils.Core/RuntimeCache.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs delete mode 100644 MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomEncryptionEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomNameEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPinnedEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTypingEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceChildEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceParentEventData.cs diff --git a/.gitignore b/.gitignore index 33be3d4..5350fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ MatrixRoomUtils.Web/wwwroot/MRU.tar.xz matrix-sync.json /patches/ MatrixRoomUtils.Bot/bot_data/ -appsettings.Local.json +appsettings.Local*.json diff --git a/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs new file mode 100644 index 0000000..c267298 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs @@ -0,0 +1,49 @@ +using System.Runtime.InteropServices; +using System.Text; +using MatrixRoomUtils.Bot.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixRoomUtils.Bot.Commands; + +public class CmdCommand : ICommand { + public string Name { get; } = "cmd"; + public string Description { get; } = "Runs a command on the host system"; + + public async Task CanInvoke(CommandContext ctx) { + return ctx.MessageEvent.Sender.EndsWith(":rory.gay"); + } + + public async Task Invoke(CommandContext ctx) { + var cmd = "\""; + foreach (var arg in ctx.Args) cmd += arg + " "; + + cmd = cmd.Trim(); + cmd += "\""; + + await ctx.Room.SendMessageEventAsync("m.room.message", new() { + Body = $"Command being executed: `{cmd}`" + }); + + var output = ArcaneLibs.Util.GetCommandOutputSync( + Environment.OSVersion.Platform == PlatformID.Unix ? "/bin/sh" : "cmd.exe", + (Environment.OSVersion.Platform == PlatformID.Unix ? "-c " : "/c ") + cmd) + .Replace("`", "\\`") + .Split("\n").ToList(); + foreach (var _out in output) Console.WriteLine($"{_out.Length:0000} {_out}"); + + var msg = ""; + while (output.Count > 0) { + Console.WriteLine("Adding: " + output[0]); + msg += output[0] + "\n"; + output.RemoveAt(0); + if ((output.Count > 0 && (msg + output[0]).Length > 64000) || output.Count == 0) { + await ctx.Room.SendMessageEventAsync("m.room.message", new() { + FormattedBody = $"```ansi\n{msg}\n```", + Body = Markdig.Markdown.ToHtml(msg), + Format = "org.matrix.custom.html" + }); + msg = ""; + } + } + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/Commands/HelpCommand.cs b/MatrixRoomUtils.Bot/Bot/Commands/HelpCommand.cs new file mode 100644 index 0000000..af41563 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/Commands/HelpCommand.cs @@ -0,0 +1,28 @@ +using System.Text; +using MatrixRoomUtils.Bot.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixRoomUtils.Bot.Commands; + +public class HelpCommand : ICommand { + private readonly IServiceProvider _services; + public HelpCommand(IServiceProvider services) { + _services = services; + } + + public string Name { get; } = "help"; + public string Description { get; } = "Displays this help message"; + + public async Task Invoke(CommandContext ctx) { + var sb = new StringBuilder(); + sb.AppendLine("Available commands:"); + var commands = _services.GetServices().ToList(); + foreach (var command in commands) { + sb.AppendLine($"- {command.Name}: {command.Description}"); + } + + await ctx.Room.SendMessageEventAsync("m.room.message", new() { + Body = sb.ToString(), + }); + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/Commands/PingCommand.cs b/MatrixRoomUtils.Bot/Bot/Commands/PingCommand.cs new file mode 100644 index 0000000..a00cc8b --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/Commands/PingCommand.cs @@ -0,0 +1,19 @@ +using System.Text; +using MatrixRoomUtils.Bot.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MatrixRoomUtils.Bot.Commands; + +public class PingCommand : ICommand { + public PingCommand() { + } + + public string Name { get; } = "ping"; + public string Description { get; } = "Pong!"; + + public async Task Invoke(CommandContext ctx) { + await ctx.Room.SendMessageEventAsync("m.room.message", new() { + Body = "pong!" + }); + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs new file mode 100644 index 0000000..8d99828 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs @@ -0,0 +1,31 @@ +using System.Text.Json; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces.Services; + +namespace MatrixRoomUtils.Bot; + +public class FileStorageProvider : IStorageProvider { + public string TargetPath { get; } + + /// + /// Creates a new instance of . + /// + /// + public FileStorageProvider(string targetPath) { + Console.WriteLine($"Initialised FileStorageProvider with path {targetPath}"); + TargetPath = targetPath; + if(!Directory.Exists(targetPath)) { + Directory.CreateDirectory(targetPath); + } + } + + public async Task SaveObject(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), ObjectExtensions.ToJson(value)); + + public async Task LoadObject(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key))); + + public async Task ObjectExists(string key) => File.Exists(Path.Join(TargetPath, key)); + + public async Task> GetAllKeys() => Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList(); + + public async Task DeleteObject(string key) => File.Delete(Path.Join(TargetPath, key)); +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/Interfaces/CommandContext.cs b/MatrixRoomUtils.Bot/Bot/Interfaces/CommandContext.cs new file mode 100644 index 0000000..ab29554 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/Interfaces/CommandContext.cs @@ -0,0 +1,11 @@ +using MatrixRoomUtils.Core; +using MatrixRoomUtils.Core.Responses; + +namespace MatrixRoomUtils.Bot.Interfaces; + +public class CommandContext { + public GenericRoom Room { get; set; } + public StateEventResponse MessageEvent { get; set; } + public string CommandName => (MessageEvent.TypedContent as MessageEventData).Body.Split(' ')[0][1..]; + public string[] Args => (MessageEvent.TypedContent as MessageEventData).Body.Split(' ')[1..]; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/Interfaces/ICommand.cs b/MatrixRoomUtils.Bot/Bot/Interfaces/ICommand.cs new file mode 100644 index 0000000..b57d8c9 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/Interfaces/ICommand.cs @@ -0,0 +1,12 @@ +namespace MatrixRoomUtils.Bot.Interfaces; + +public interface ICommand { + public string Name { get; } + public string Description { get; } + + public Task CanInvoke(CommandContext ctx) { + return Task.FromResult(true); + } + + public Task Invoke(CommandContext ctx); +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/MRUBot.cs b/MatrixRoomUtils.Bot/Bot/MRUBot.cs new file mode 100644 index 0000000..81123e0 --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/MRUBot.cs @@ -0,0 +1,115 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using MatrixRoomUtils.Bot; +using MatrixRoomUtils.Bot.Interfaces; +using MatrixRoomUtils.Core; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Helpers; +using MatrixRoomUtils.Core.Services; +using MatrixRoomUtils.Core.StateEventTypes; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +public class MRUBot : IHostedService { + private readonly HomeserverProviderService _homeserverProviderService; + private readonly ILogger _logger; + private readonly MRUBotConfiguration _configuration; + private readonly IEnumerable _commands; + + public MRUBot(HomeserverProviderService homeserverProviderService, ILogger logger, + MRUBotConfiguration configuration, IServiceProvider services) { + Console.WriteLine("MRUBot hosted service instantiated!"); + _homeserverProviderService = homeserverProviderService; + _logger = logger; + _configuration = configuration; + Console.WriteLine("Getting commands..."); + _commands = services.GetServices(); + Console.WriteLine($"Got {_commands.Count()} commands!"); + } + + /// Triggered when the application host is ready to start the service. + /// Indicates that the start process has been aborted. + [SuppressMessage("ReSharper", "FunctionNeverReturns")] + public async Task StartAsync(CancellationToken cancellationToken) { + Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); + AuthenticatedHomeServer hs; + try { + hs = await _homeserverProviderService.GetAuthenticatedWithToken(_configuration.Homeserver, + _configuration.AccessToken); + } + catch (Exception e) { + _logger.LogError(e.Message); + throw; + } + + await (await hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); + + // foreach (var room in await hs.GetJoinedRooms()) { + // if(room.RoomId is "!OGEhHVWSdvArJzumhm:matrix.org") continue; + // foreach (var stateEvent in await room.GetStateAsync>("")) { + // var _ = stateEvent.GetType; + // } + // Console.WriteLine($"Got room state for {room.RoomId}!"); + // } + + hs.SyncHelper.InviteReceived += async (_, args) => { + var inviteEvent = + args.Value.InviteState.Events.FirstOrDefault(x => + x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId); + Console.WriteLine( + $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as RoomMemberEventData).Reason}"); + if (inviteEvent.Sender == "@emma:rory.gay") { + try { + await (await hs.GetRoom(args.Key)).JoinAsync(reason: "I was invited by Emma (Rory&)!"); + } + catch (Exception e) { + Console.WriteLine(e); + await (await hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); + } + } + }; + hs.SyncHelper.TimelineEventReceived += async (_, @event) => { + Console.WriteLine( + $"Got timeline event in {@event.RoomId}: {@event.ToJson(indent: false, ignoreNull: true)}"); + + var room = await hs.GetRoom(@event.RoomId); + // Console.WriteLine(eventResponse.ToJson(indent: false)); + if (@event is { Type: "m.room.message", TypedContent: MessageEventData 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", + new MessageEventData() { + MessageType = "m.text", + Body = "Command not found!" + }); + return; + } + var ctx = new CommandContext() { + Room = room, + MessageEvent = @event + }; + if (await command.CanInvoke(ctx)) { + await command.Invoke(ctx); + } + else { + await room.SendMessageEventAsync("m.room.message", + new MessageEventData() { + MessageType = "m.text", + Body = "You do not have permission to run this command!" + }); + } + } + } + }; + await hs.SyncHelper.RunSyncLoop(cancellationToken); + } + + /// 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) { + Console.WriteLine("Shutting down bot!"); + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs b/MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs new file mode 100644 index 0000000..c91698a --- /dev/null +++ b/MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Configuration; + +namespace MatrixRoomUtils.Bot; + +public class MRUBotConfiguration { + public MRUBotConfiguration(IConfiguration config) { + config.GetRequiredSection("Bot").Bind(this); + } + public string Homeserver { get; set; } = ""; + public string AccessToken { get; set; } = ""; + public string Prefix { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/FileStorageProvider.cs b/MatrixRoomUtils.Bot/FileStorageProvider.cs deleted file mode 100644 index 8d99828..0000000 --- a/MatrixRoomUtils.Bot/FileStorageProvider.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Text.Json; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces.Services; - -namespace MatrixRoomUtils.Bot; - -public class FileStorageProvider : IStorageProvider { - public string TargetPath { get; } - - /// - /// Creates a new instance of . - /// - /// - public FileStorageProvider(string targetPath) { - Console.WriteLine($"Initialised FileStorageProvider with path {targetPath}"); - TargetPath = targetPath; - if(!Directory.Exists(targetPath)) { - Directory.CreateDirectory(targetPath); - } - } - - public async Task SaveObject(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), ObjectExtensions.ToJson(value)); - - public async Task LoadObject(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key))); - - public async Task ObjectExists(string key) => File.Exists(Path.Join(TargetPath, key)); - - public async Task> GetAllKeys() => Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList(); - - public async Task DeleteObject(string key) => File.Delete(Path.Join(TargetPath, key)); -} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/MRUBot.cs b/MatrixRoomUtils.Bot/MRUBot.cs deleted file mode 100644 index 157673d..0000000 --- a/MatrixRoomUtils.Bot/MRUBot.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using MatrixRoomUtils.Bot; -using MatrixRoomUtils.Core; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Helpers; -using MatrixRoomUtils.Core.Services; -using MatrixRoomUtils.Core.StateEventTypes; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -public class MRUBot : IHostedService { - private readonly HomeserverProviderService _homeserverProviderService; - private readonly ILogger _logger; - private readonly MRUBotConfiguration _configuration; - - public MRUBot(HomeserverProviderService homeserverProviderService, ILogger logger, - MRUBotConfiguration configuration) { - Console.WriteLine("MRUBot hosted service instantiated!"); - _homeserverProviderService = homeserverProviderService; - _logger = logger; - _configuration = configuration; - } - - /// Triggered when the application host is ready to start the service. - /// Indicates that the start process has been aborted. - [SuppressMessage("ReSharper", "FunctionNeverReturns")] - public async Task StartAsync(CancellationToken cancellationToken) { - Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); - AuthenticatedHomeServer hs; - try { - hs = await _homeserverProviderService.GetAuthenticatedWithToken(_configuration.Homeserver, - _configuration.AccessToken); - } - catch (Exception e) { - _logger.LogError(e.Message); - throw; - } - - await (await hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); - - hs.SyncHelper.InviteReceived += async (_, args) => { - // Console.WriteLine($"Got invite to {args.Key}:"); - // foreach (var stateEvent in args.Value.InviteState.Events) { - // Console.WriteLine($"[{stateEvent.Sender}: {stateEvent.StateKey}::{stateEvent.Type}] " + - // ObjectExtensions.ToJson(stateEvent.Content, indent: false, ignoreNull: true)); - // } - - var inviteEvent = - args.Value.InviteState.Events.FirstOrDefault(x => - x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId); - Console.WriteLine( - $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as MemberEventData).Reason}"); - if (inviteEvent.Sender == "@emma:rory.gay") { - try { - await (await hs.GetRoom(args.Key)).JoinAsync(reason: "I was invited by Emma (Rory&)!"); - } - catch (Exception e) { - Console.WriteLine(e); - await (await hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); - } - } - }; - hs.SyncHelper.TimelineEventReceived += async (_, @event) => { - Console.WriteLine( - $"Got timeline event in {@event.RoomId}: {@event.ToJson(indent: false, ignoreNull: true)}"); - - // Console.WriteLine(eventResponse.ToJson(indent: false)); - if (@event is { Type: "m.room.message", TypedContent: MessageEventData message }) { - if (message is { MessageType: "m.text", Body: "!ping" }) { - Console.WriteLine( - $"Got ping from {@event.Sender} in {@event.RoomId} with message id {@event.EventId}!"); - await (await hs.GetRoom(@event.RoomId)).SendMessageEventAsync("m.room.message", - new MessageEventData() { MessageType = "m.text", Body = "pong!" }); - } - } - }; - - await hs.SyncHelper.RunSyncLoop(cancellationToken); - } - - /// 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) { - Console.WriteLine("Shutting down bot!"); - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/MRUBotConfiguration.cs b/MatrixRoomUtils.Bot/MRUBotConfiguration.cs deleted file mode 100644 index 14d9b60..0000000 --- a/MatrixRoomUtils.Bot/MRUBotConfiguration.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Configuration; - -namespace MatrixRoomUtils.Bot; - -public class MRUBotConfiguration { - public MRUBotConfiguration(IConfiguration config) { - config.GetRequiredSection("Bot").Bind(this); - } - public string Homeserver { get; set; } = ""; - public string AccessToken { get; set; } = ""; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj index a82b6aa..7012647 100644 --- a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj +++ b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj @@ -21,11 +21,16 @@ + + Always + + Always + diff --git a/MatrixRoomUtils.Bot/Program.cs b/MatrixRoomUtils.Bot/Program.cs index e8a5b96..0e27286 100644 --- a/MatrixRoomUtils.Bot/Program.cs +++ b/MatrixRoomUtils.Bot/Program.cs @@ -1,6 +1,8 @@ // See https://aka.ms/new-console-template for more information using MatrixRoomUtils.Bot; +using MatrixRoomUtils.Bot.Interfaces; +using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -16,6 +18,10 @@ var host = Host.CreateDefaultBuilder(args).ConfigureServices((_, services) => { ); services.AddScoped(); services.AddRoryLibMatrixServices(); + foreach (var commandClass in new ClassCollector().ResolveFromAllAccessibleAssemblies()) { + Console.WriteLine($"Adding command {commandClass.Name}"); + services.AddScoped(typeof(ICommand), commandClass); + } services.AddHostedService(); }).UseConsoleLifetime().Build(); diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs index 23e98ae..b7e01dd 100644 --- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs +++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -42,14 +42,14 @@ public class AuthenticatedHomeServer : IHomeServer { return this; } - public async Task GetRoom(string roomId) => new Room(_httpClient, roomId); + public async Task GetRoom(string roomId) => new(this, roomId); - public async Task> GetJoinedRooms() { - var rooms = new List(); + public async Task> GetJoinedRooms() { + var rooms = new List(); var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); var roomsJson = await roomQuery.Content.ReadFromJsonAsync(); - foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString())); + foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new GenericRoom(this, room.GetString())); Console.WriteLine($"Fetched {rooms.Count} rooms"); @@ -67,7 +67,7 @@ public class AuthenticatedHomeServer : IHomeServer { return resJson.GetProperty("content_uri").GetString()!; } - public async Task CreateRoom(CreateRoomRequest creationEvent) { + public async Task CreateRoom(CreateRoomRequest creationEvent) { var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs index 83b279a..b1b0362 100644 --- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs +++ b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs @@ -10,7 +10,7 @@ public class MatrixAuth { public static async Task Login(string homeserver, string username, string password) { Console.WriteLine($"Logging in to {homeserver} as {username}..."); homeserver = (await new RemoteHomeServer(homeserver).Configure()).FullHomeServerDomain; - var hc = new HttpClient(); + var hc = new MatrixHttpClient(); var payload = new { type = "m.login.password", identifier = new { @@ -25,11 +25,6 @@ public class MatrixAuth { Console.WriteLine($"Login: {resp.StatusCode}"); var data = await resp.Content.ReadFromJsonAsync(); if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data); - if (data.TryGetProperty("retry_after_ms", out var retryAfter)) { - Console.WriteLine($"Login: Waiting {retryAfter.GetInt32()}ms before retrying"); - await Task.Delay(retryAfter.GetInt32()); - return await Login(homeserver, username, password); - } Console.WriteLine($"Login: {data.ToJson()}"); return data.Deserialize(); diff --git a/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs b/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs index 98b0aab..8994529 100644 --- a/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs @@ -1,36 +1,9 @@ -using System.Reflection; -using System.Text.Json; using MatrixRoomUtils.Core.Interfaces; -using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Extensions; -public static class IEnumerableExtensions { - public static List DeserializeMatrixTypes(this List stateEvents) { - return stateEvents.Select(DeserializeMatrixType).ToList(); - } - - public static StateEventResponse DeserializeMatrixType(this JsonElement stateEvent) { - var type = stateEvent.GetProperty("type").GetString(); - var knownType = StateEvent.KnownStateEventTypes.FirstOrDefault(x => x.GetCustomAttribute()?.EventName == type); - if (knownType == null) { - Console.WriteLine($"Warning: unknown event type '{type}'!"); - return new StateEventResponse(); - } - - var eventInstance = Activator.CreateInstance(typeof(StateEventResponse).MakeGenericType(knownType))!; - stateEvent.Deserialize(eventInstance.GetType()); - - return (StateEventResponse) eventInstance; - } - - public static void Replace(this List stateEvents, StateEvent old, StateEvent @new) { - var index = stateEvents.IndexOf(old); - if (index == -1) return; - stateEvents[index] = @new; - } -} - +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class MatrixEventAttribute : Attribute { public string EventName { get; set; } + public bool Legacy { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs index 78f4456..36da644 100644 --- a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs @@ -7,7 +7,7 @@ using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Extensions; public static class JsonElementExtensions { - public static void FindExtraJsonElementFields([DisallowNull] this JsonElement? res, Type t) { + public static bool FindExtraJsonElementFields([DisallowNull] this JsonElement? res, Type t) { var props = t.GetProperties(); var unknownPropertyFound = false; foreach (var field in res.Value.EnumerateObject()) { @@ -17,8 +17,10 @@ public static class JsonElementExtensions { } if (unknownPropertyFound) Console.WriteLine(res.Value.ToJson()); + + return unknownPropertyFound; } - public static void FindExtraJsonObjectFields([DisallowNull] this JsonObject? res, Type t) { + public static bool FindExtraJsonObjectFields([DisallowNull] this JsonObject? res, Type t) { var props = t.GetProperties(); var unknownPropertyFound = false; foreach (var field in res) { @@ -31,5 +33,7 @@ public static class JsonElementExtensions { } if (unknownPropertyFound) Console.WriteLine(res.ToJson()); + + return unknownPropertyFound; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs index 04c31cd..bb4ddd5 100644 --- a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs +++ b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs @@ -38,6 +38,7 @@ public class SyncHelper { catch (Exception e) { Console.WriteLine(e); } + return null; } @@ -48,6 +49,7 @@ public class SyncHelper { sync = await Sync(sync?.NextBatch, cancellationToken); Console.WriteLine($"Got sync, next batch: {sync?.NextBatch}!"); if (sync == null) continue; + if (sync.Rooms is { Invite.Count: > 0 }) { foreach (var roomInvite in sync.Rooms.Invite) { Console.WriteLine(roomInvite.Value.GetType().Name); @@ -81,7 +83,8 @@ public class SyncHelper { /// /// Event fired when a room invite is received /// - public event EventHandler>? InviteReceived; + public event EventHandler>? + InviteReceived; public event EventHandler? TimelineEventReceived; public event EventHandler? AccountDataReceived; diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs index fcff0f2..029530c 100644 --- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs +++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -20,22 +20,6 @@ public class IHomeServer { } private async Task _resolveHomeserverFromWellKnown(string homeserver) { - if (RuntimeCache.HomeserverResolutionCache.Count == 0) { - // Console.WriteLine("No cached homeservers, resolving..."); - await Task.Delay(Random.Shared.Next(1000, 5000)); - } - - if (RuntimeCache.HomeserverResolutionCache.ContainsKey(homeserver)) { - if (RuntimeCache.HomeserverResolutionCache[homeserver].ResolutionTime < DateTime.Now.AddHours(1)) { - Console.WriteLine($"Found cached homeserver: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}"); - return RuntimeCache.HomeserverResolutionCache[homeserver].Result; - } - - Console.WriteLine($"Cached homeserver expired, removing: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}"); - RuntimeCache.HomeserverResolutionCache.Remove(homeserver); - } - //throw new NotImplementedException(); - string result = null; Console.WriteLine($"Resolving homeserver: {homeserver}"); if (!homeserver.StartsWith("http")) homeserver = "https://" + homeserver; @@ -67,10 +51,6 @@ public class IHomeServer { if (result != null) { Console.WriteLine($"Resolved homeserver: {homeserver} -> {result}"); - RuntimeCache.HomeserverResolutionCache.TryAdd(homeserver, new HomeServerResolutionResult { - Result = result, - ResolutionTime = DateTime.Now - }); return result; } diff --git a/MatrixRoomUtils.Core/JoinRules.cs b/MatrixRoomUtils.Core/JoinRules.cs deleted file mode 100644 index 7ce56c4..0000000 --- a/MatrixRoomUtils.Core/JoinRules.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace MatrixRoomUtils.Core; - -public class JoinRules { - private static string Public = "public"; - private static string Invite = "invite"; - private static string Knock = "knock"; - - [JsonPropertyName("join_rule")] - public string JoinRule { get; set; } - - [JsonPropertyName("allow")] - public List Allow { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj index a043378..60f11f0 100644 --- a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj +++ b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj @@ -7,12 +7,7 @@ - - ..\..\..\.cache\NuGetPackages\microsoft.aspnetcore.app.ref\7.0.5\ref\net7.0\Microsoft.AspNetCore.Mvc.Core.dll - - - ..\..\..\.cache\NuGetPackages\microsoft.extensions.dependencyinjection.abstractions\7.0.0\lib\net7.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - + diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs index 3f50d2e..c593318 100644 --- a/MatrixRoomUtils.Core/RemoteHomeServer.cs +++ b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -21,20 +21,4 @@ public class RemoteHomeServer : IHomeServer { return this; } - - public async Task GetRoom(string roomId) => new Room(_httpClient, roomId); - - public async Task> GetJoinedRooms() { - var rooms = new List(); - var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); - if (!roomQuery.IsSuccessStatusCode) { - Console.WriteLine($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); - throw new InvalidDataException($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}"); - } - - var roomsJson = await roomQuery.Content.ReadFromJsonAsync(); - foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString())); - - return rooms; - } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs index 6e67887..422a557 100644 --- a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs +++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs @@ -27,10 +27,10 @@ public class StateEventResponse : StateEvent { [JsonPropertyName("prev_content")] public dynamic PrevContent { get; set; } - + public class UnsignedData { [JsonPropertyName("age")] - public ulong Age { get; set; } + public ulong? Age { get; set; } [JsonPropertyName("prev_content")] public dynamic? PrevContent { get; set; } diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs deleted file mode 100644 index 59c56ed..0000000 --- a/MatrixRoomUtils.Core/Room.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System.Net.Http.Json; -using System.Text; -using System.Text.Json; -using System.Web; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.RoomTypes; - -namespace MatrixRoomUtils.Core; - -public class Room { - private readonly HttpClient _httpClient; - - public Room(HttpClient httpClient, string roomId) { - _httpClient = httpClient; - RoomId = roomId; - if(GetType() != typeof(SpaceRoom)) - AsSpace = new SpaceRoom(_httpClient, RoomId); - } - - public string RoomId { get; set; } - - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) { - var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; - if (!string.IsNullOrEmpty(type)) url += $"/{type}"; - if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; - - var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) { - if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}"); - return null; - } - - var result = await res.Content.ReadFromJsonAsync(); - return result; - } - - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) { - var res = await GetStateAsync(type, stateKey, logOnFailure); - if (res == null) return default; - return res.Value.Deserialize(); - } - - public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") { - var url = $"/_matrix/client/v3/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; - if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; - var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); - } - - var result = await res.Content.ReadFromJsonAsync(); - return result ?? new MessagesResponse(); - } - - public async Task GetNameAsync() { - var res = await GetStateAsync("m.room.name"); - if (!res.HasValue) { - Console.WriteLine($"Room {RoomId} has no name!"); - return RoomId; - } - - var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() ?? RoomId : RoomId; - //Console.WriteLine($"Got name: {resn}"); - return resn; - } - - 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] }; - var fullJoinUrl = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers); - var res = await _httpClient.PostAsJsonAsync(fullJoinUrl, new { - reason - }); - } - - public async Task> GetMembersAsync(bool joinedOnly = true) { - var res = await GetStateAsync(""); - if (!res.HasValue) return new List(); - var members = new List(); - foreach (var member in res.Value.EnumerateArray()) { - if (member.GetProperty("type").GetString() != "m.room.member") continue; - if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue; - var memberId = member.GetProperty("state_key").GetString(); - members.Add(memberId ?? throw new InvalidOperationException("Event type was member but state key was null!")); - } - - return members; - } - - public async Task> GetAliasesAsync() { - var res = await GetStateAsync("m.room.aliases"); - if (!res.HasValue) return new List(); - var aliases = new List(); - foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? ""); - - return aliases; - } - - public async Task GetCanonicalAliasAsync() { - var res = await GetStateAsync("m.room.canonical_alias"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("alias").GetString() ?? ""; - } - - public async Task GetTopicAsync() { - var res = await GetStateAsync("m.room.topic"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("topic").GetString() ?? ""; - } - - public async Task GetAvatarUrlAsync() { - var res = await GetStateAsync("m.room.avatar"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("url").GetString() ?? ""; - } - - public async Task GetJoinRuleAsync() { - var res = await GetStateAsync("m.room.join_rules"); - if (!res.HasValue) return new JoinRules(); - return res.Value.Deserialize() ?? new JoinRules(); - } - - public async Task GetHistoryVisibilityAsync() { - var res = await GetStateAsync("m.room.history_visibility"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("history_visibility").GetString() ?? ""; - } - - public async Task GetGuestAccessAsync() { - var res = await GetStateAsync("m.room.guest_access"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("guest_access").GetString() ?? ""; - } - - public async Task GetCreateEventAsync() { - var res = await GetStateAsync("m.room.create"); - if (!res.HasValue) return new CreateEvent(); - - res.FindExtraJsonElementFields(typeof(CreateEvent)); - - return res.Value.Deserialize() ?? new CreateEvent(); - } - - public async Task GetRoomType() { - var res = await GetStateAsync("m.room.create"); - if (!res.HasValue) return null; - if (res.Value.TryGetProperty("type", out var type)) return type.GetString(); - return null; - } - - public async Task ForgetAsync() { - var res = await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task LeaveAsync(string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/leave", new { - reason - }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task KickAsync(string userId, string? reason = null) { - - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/kick", new UserIdAndReason() { UserId = userId, Reason = reason }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task BanAsync(string userId, string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/ban", new UserIdAndReason() { UserId = userId, Reason = reason }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task UnbanAsync(string userId) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/unban", new UserIdAndReason() { UserId = userId }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task SendStateEventAsync(string eventType, object content) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - } - return await res.Content.ReadFromJsonAsync(); - } - - public async Task SendMessageEventAsync(string eventType, object content) { - var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/"+new Guid(), content); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - } - return await res.Content.ReadFromJsonAsync(); - } - - - public readonly SpaceRoom AsSpace; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs new file mode 100644 index 0000000..8dc30d1 --- /dev/null +++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs @@ -0,0 +1,243 @@ +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; +using System.Web; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.RoomTypes; + +namespace MatrixRoomUtils.Core; + +public class GenericRoom { + internal readonly AuthenticatedHomeServer _homeServer; + internal readonly HttpClient _httpClient; + + public GenericRoom(AuthenticatedHomeServer homeServer, string roomId) { + _homeServer = homeServer; + _httpClient = homeServer._httpClient; + RoomId = roomId; + if (GetType() != typeof(SpaceRoom)) + AsSpace = new SpaceRoom(homeServer, RoomId); + } + + public string RoomId { get; set; } + + public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) { + var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; + if (!string.IsNullOrEmpty(type)) url += $"/{type}"; + if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; + + var res = await _httpClient.GetAsync(url); + if (!res.IsSuccessStatusCode) { + if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}"); + return null; + } + + var result = await res.Content.ReadFromJsonAsync(); + return result; + } + + public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) { + var res = await GetStateAsync(type, stateKey, logOnFailure); + if (res == null) return default; + return res.Value.Deserialize(); + } + + public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", + string filter = "") { + var url = $"/_matrix/client/v3/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; + if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; + var res = await _httpClient.GetAsync(url); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); + } + + var result = await res.Content.ReadFromJsonAsync(); + return result ?? new MessagesResponse(); + } + + public async Task GetNameAsync() { + var res = await GetStateAsync("m.room.name"); + if (!res.HasValue) { + Console.WriteLine($"Room {RoomId} has no name!"); + return RoomId; + } + + var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() ?? RoomId : RoomId; + //Console.WriteLine($"Got name: {resn}"); + return resn; + } + + 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] }; + var fullJoinUrl = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers); + var res = await _httpClient.PostAsJsonAsync(fullJoinUrl, new { + reason + }); + } + + public async Task> GetMembersAsync(bool joinedOnly = true) { + var res = await GetStateAsync(""); + if (!res.HasValue) return new List(); + var members = new List(); + foreach (var member in res.Value.EnumerateArray()) { + if (member.GetProperty("type").GetString() != "m.room.member") continue; + if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue; + var memberId = member.GetProperty("state_key").GetString(); + members.Add( + memberId ?? throw new InvalidOperationException("Event type was member but state key was null!")); + } + + return members; + } + + public async Task> GetAliasesAsync() { + var res = await GetStateAsync("m.room.aliases"); + if (!res.HasValue) return new List(); + var aliases = new List(); + foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? ""); + + return aliases; + } + + public async Task GetCanonicalAliasAsync() { + var res = await GetStateAsync("m.room.canonical_alias"); + if (!res.HasValue) return ""; + return res.Value.GetProperty("alias").GetString() ?? ""; + } + + public async Task GetTopicAsync() { + var res = await GetStateAsync("m.room.topic"); + if (!res.HasValue) return ""; + return res.Value.GetProperty("topic").GetString() ?? ""; + } + + public async Task GetAvatarUrlAsync() { + var res = await GetStateAsync("m.room.avatar"); + if (!res.HasValue) return ""; + return res.Value.GetProperty("url").GetString() ?? ""; + } + + public async Task GetJoinRuleAsync() { + var res = await GetStateAsync("m.room.join_rules"); + if (!res.HasValue) return new JoinRulesEventData(); + return res.Value.Deserialize() ?? new JoinRulesEventData(); + } + + public async Task GetHistoryVisibilityAsync() { + var res = await GetStateAsync("m.room.history_visibility"); + if (!res.HasValue) return ""; + return res.Value.GetProperty("history_visibility").GetString() ?? ""; + } + + public async Task GetGuestAccessAsync() { + var res = await GetStateAsync("m.room.guest_access"); + if (!res.HasValue) return ""; + return res.Value.GetProperty("guest_access").GetString() ?? ""; + } + + public async Task GetCreateEventAsync() { + var res = await GetStateAsync("m.room.create"); + if (!res.HasValue) return new CreateEvent(); + + res.FindExtraJsonElementFields(typeof(CreateEvent)); + + return res.Value.Deserialize() ?? new CreateEvent(); + } + + public async Task GetRoomType() { + var res = await GetStateAsync("m.room.create"); + if (!res.HasValue) return null; + if (res.Value.TryGetProperty("type", out var type)) return type.GetString(); + return null; + } + + public async Task ForgetAsync() { + var res = await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); + } + } + + public async Task LeaveAsync(string? reason = null) { + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/leave", new { + reason + }); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); + } + } + + public async Task KickAsync(string userId, string? reason = null) { + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/kick", + new UserIdAndReason() { UserId = userId, Reason = reason }); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); + } + } + + public async Task BanAsync(string userId, string? reason = null) { + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/ban", + new UserIdAndReason() { UserId = userId, Reason = reason }); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); + } + } + + public async Task UnbanAsync(string userId) { + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/unban", + new UserIdAndReason() { UserId = userId }); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); + } + } + + public async Task SendStateEventAsync(string eventType, object content) { + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content); + if (!res.IsSuccessStatusCode) { + Console.WriteLine( + $"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + throw new Exception( + $"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + } + + return await res.Content.ReadFromJsonAsync(); + } + + public async Task SendMessageEventAsync(string eventType, MessageEventData content) { + var url = $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(); + var res = await _httpClient.PutAsJsonAsync(url, content); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + } + + var resu = await res.Content.ReadFromJsonAsync(); + + return resu; + } + + public async Task SendFileAsync(string eventType, string fileName, Stream fileStream) { + var url = $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(); + var content = new MultipartFormDataContent(); + content.Add(new StreamContent(fileStream), "file", fileName); + var res = await _httpClient.PutAsync(url, content); + if (!res.IsSuccessStatusCode) { + Console.WriteLine($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + throw new Exception($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); + } + + var resu = await res.Content.ReadFromJsonAsync(); + + return resu; + } + + public readonly SpaceRoom AsSpace; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs index 6eaa73b..6b586c7 100644 --- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs @@ -5,18 +5,22 @@ using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.RoomTypes; -public class SpaceRoom : Room { - public SpaceRoom(HttpClient httpClient, string roomId) : base(httpClient, roomId) { } +public class SpaceRoom : GenericRoom { + private readonly AuthenticatedHomeServer _homeServer; + private readonly GenericRoom _room; + public SpaceRoom(AuthenticatedHomeServer homeServer, string roomId) : base(homeServer, roomId) { + _homeServer = homeServer; + } - public async Task> GetRoomsAsync(bool includeRemoved = false) { - var rooms = new List(); + public async Task> GetRoomsAsync(bool includeRemoved = false) { + var rooms = new List(); var state = await GetStateAsync(""); if (state != null) { var states = state.Value.Deserialize()!; foreach (var stateEvent in states.Where(x => x.Type == "m.space.child")) { var roomId = stateEvent.StateKey; if(stateEvent.TypedContent.ToJson() != "{}" || includeRemoved) - rooms.Add(await RuntimeCache.CurrentHomeServer.GetRoom(roomId)); + rooms.Add(await _homeServer.GetRoom(roomId)); } } diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs deleted file mode 100644 index 7ab3952..0000000 --- a/MatrixRoomUtils.Core/RuntimeCache.cs +++ /dev/null @@ -1,99 +0,0 @@ -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Responses; -using MatrixRoomUtils.Core.StateEventTypes; - -namespace MatrixRoomUtils.Core; - -public class RuntimeCache { - public static bool WasLoaded = false; - - static RuntimeCache() => - Task.Run(async () => { - while (true) { - await Task.Delay(1000); - foreach (var (key, value) in GenericResponseCache) - if (value.Cache.Any()) - SaveObject("rory.matrixroomutils.generic_cache:" + key, value); - else - RemoveObject("rory.matrixroomutils.generic_cache:" + key); - } - }); - - public static string? LastUsedToken { get; set; } - public static AuthenticatedHomeServer CurrentHomeServer { get; set; } - public static Dictionary LoginSessions { get; set; } = new(); - - public static Dictionary HomeserverResolutionCache { get; set; } = new(); - // public static Dictionary ProfileCache { get; set; } = new(); - - public static Dictionary> GenericResponseCache { get; set; } = new(); - - public static Task Save { get; set; } = new Task(() => { Console.WriteLine("RuntimeCache.Save() was called, but no callback was set!"); }); - public static Action SaveObject { get; set; } = (key, value) => { Console.WriteLine($"RuntimeCache.SaveObject({key}, {value}) was called, but no callback was set!"); }; - public static Action RemoveObject { get; set; } = key => { Console.WriteLine($"RuntimeCache.RemoveObject({key}) was called, but no callback was set!"); }; -} - -public class UserInfo { - public ProfileResponse Profile { get; set; } = new(); - public LoginResponse LoginResponse { get; set; } - - public string AccessToken => LoginResponse.AccessToken; -} - -public class HomeServerResolutionResult { - public string Result { get; set; } - public DateTime ResolutionTime { get; set; } -} - -public class ObjectCache where T : class { - public ObjectCache() => - //expiry timer - Task.Run(async () => { - while (Cache.Any()) { - await Task.Delay(1000); - foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(15).ToList()) - // Console.WriteLine($"Removing {x.Key} from cache"); - Cache.Remove(x.Key); - //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this); - } - }); - - public Dictionary> Cache { get; set; } = new(); - public string Name { get; set; } = null!; - - public GenericResult this[string key] { - get { - if (Cache.ContainsKey(key)) { - // Console.WriteLine($"cache.get({key}): hit"); - // Console.WriteLine($"Found item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); - if (Cache[key].ExpiryTime < DateTime.Now) - Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(false)}"); - return Cache[key]; - } - - Console.WriteLine($"cache.get({key}): miss"); - return null; - } - set => Cache[key] = value; - // Console.WriteLine($"set({key}) = {Cache[key].Result.ToJson(indent:false)}"); - // Console.WriteLine($"new_state: {this.ToJson(indent:false)}"); - // Console.WriteLine($"New item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}"); - // Console.Error.WriteLine("Full cache: " + Cache.ToJson()); - } - - public bool ContainsKey(string key) => Cache.ContainsKey(key); -} - -public class GenericResult { - public GenericResult() { - //expiry timer - } - - public GenericResult(T? result, DateTime? expiryTime = null) : this() { - Result = result; - ExpiryTime = expiryTime; - } - - public T? Result { get; set; } - public DateTime? ExpiryTime { get; set; } = DateTime.Now; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs index cb8f0b4..f2c8701 100644 --- a/MatrixRoomUtils.Core/StateEvent.cs +++ b/MatrixRoomUtils.Core/StateEvent.cs @@ -1,9 +1,12 @@ +using System.Diagnostics; using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; +using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core; @@ -19,14 +22,36 @@ public class StateEvent { [JsonPropertyName("state_key")] public string StateKey { get; set; } = ""; + private string _type; + [JsonPropertyName("type")] - public string Type { get; set; } + public string Type { + get => _type; + set { + _type = value; + if (RawContent != null && this is StateEventResponse stateEventResponse) { + if (File.Exists($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json")) return; + var x = GetType.Name; + } + } + } [JsonPropertyName("replaces_state")] public string? ReplacesState { get; set; } + private JsonObject? _rawContent; + [JsonPropertyName("content")] - public JsonObject? RawContent { get; set; } + public JsonObject? RawContent { + get => _rawContent; + set { + _rawContent = value; + if (Type != null && this is StateEventResponse stateEventResponse) { + if (File.Exists($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json")) return; + var x = GetType.Name; + } + } + } public T1 GetContent() where T1 : IStateEventType { return RawContent.Deserialize(); @@ -35,17 +60,36 @@ public class StateEvent { [JsonIgnore] public Type GetType { get { - var type = StateEvent.KnownStateEventTypes.FirstOrDefault(x => - x.GetCustomAttribute()?.EventName == Type); - if (type == null) { - Console.WriteLine($"Warning: unknown event type '{Type}'!"); - Console.WriteLine(RawContent.ToJson()); - return typeof(object); + if (Type == "m.receipt") { + return typeof(Dictionary); + } + + var type = KnownStateEventTypes.FirstOrDefault(x => + x.GetCustomAttributes()?.Any(y => y.EventName == Type) ?? false); + + //special handling for some types + // if (type == typeof(RoomEmotesEventData)) { + // RawContent["emote"] = RawContent["emote"]?.AsObject() ?? new JsonObject(); + // } + + if (this is StateEventResponse stateEventResponse) { + if (type == null || type == typeof(object)) { + Console.WriteLine($"Warning: unknown event type '{Type}'!"); + Console.WriteLine(RawContent.ToJson()); + Directory.CreateDirectory($"unknown_state_events/{Type}"); + File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json", + RawContent.ToJson()); + Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json"); + } + else if (RawContent.FindExtraJsonObjectFields(type)) { + Directory.CreateDirectory($"unknown_state_events/{Type}"); + File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json", + RawContent.ToJson()); + Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json"); + } } - RawContent.FindExtraJsonObjectFields(type); - - return type; + return type ?? typeof(object); } } diff --git a/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs deleted file mode 100644 index 2f9502e..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.canonical_alias")] -public class CanonicalAliasEventData : IStateEventType { - [JsonPropertyName("alias")] - public string? Alias { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs new file mode 100644 index 0000000..4a75b98 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Common/RoomEmotesEventData.cs @@ -0,0 +1,26 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "im.ponies.room_emotes")] +public class RoomEmotesEventData : IStateEventType { + [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 { + +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs b/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs deleted file mode 100644 index 1727ce9..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.guest_access")] -public class GuestAccessData : IStateEventType { - [JsonPropertyName("guest_access")] - public string GuestAccess { get; set; } - - public bool IsGuestAccessEnabled { - get => GuestAccess == "can_join"; - set => GuestAccess = value ? "can_join" : "forbidden"; - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs b/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs deleted file mode 100644 index 481cc08..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.history_visibility")] -public class HistoryVisibilityData : IStateEventType { - public string HistoryVisibility { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs deleted file mode 100644 index acf7777..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.member")] -public class MemberEventData : IStateEventType { - [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; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs deleted file mode 100644 index ad99709..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -[MatrixEvent(EventName = "m.room.message")] -public class MessageEventData : IStateEventType { - [JsonPropertyName("body")] - public string Body { get; set; } - [JsonPropertyName("msgtype")] - public string MessageType { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs deleted file mode 100644 index e67639b..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -public class PolicyRuleStateEventData : IStateEventType { - /// - /// 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 -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs b/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs deleted file mode 100644 index a3e44d1..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.power_levels")] -public class PowerLevelEvent : IStateEventType { - [JsonPropertyName("ban")] - public int Ban { get; set; } // = 50; - - [JsonPropertyName("events_default")] - public int EventsDefault { get; set; } // = 0; - - [JsonPropertyName("events")] - public Dictionary Events { get; set; } // = null!; - - [JsonPropertyName("invite")] - public int Invite { get; set; } // = 50; - - [JsonPropertyName("kick")] - public int Kick { get; set; } // = 50; - - [JsonPropertyName("notifications")] - public NotificationsPL NotificationsPl { get; set; } // = null!; - - [JsonPropertyName("redact")] - public int Redact { get; set; } // = 50; - - [JsonPropertyName("state_default")] - public int StateDefault { get; set; } // = 50; - - [JsonPropertyName("users")] - public Dictionary Users { get; set; } // = null!; - - [JsonPropertyName("users_default")] - public int UsersDefault { get; set; } // = 0; - - public class NotificationsPL { - [JsonPropertyName("room")] - public int Room { get; set; } = 50; - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs deleted file mode 100644 index a17b6f9..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.presence")] -public class PresenceStateEventData : IStateEventType { - [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; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs b/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs deleted file mode 100644 index d36ef74..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.member")] -public class ProfileResponse { - [JsonPropertyName("avatar_url")] - public string? AvatarUrl { get; set; } = ""; - - [JsonPropertyName("displayname")] - public string? DisplayName { get; set; } = ""; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs deleted file mode 100644 index 03ce16b..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.avatar")] -public class RoomAvatarEventData : IStateEventType { - [JsonPropertyName("url")] - public string? Url { get; set; } - - [JsonPropertyName("info")] - public RoomAvatarInfo? Info { get; set; } - - public class RoomAvatarInfo { - - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs deleted file mode 100644 index 72651c8..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.topic")] -public class RoomTopicEventData : IStateEventType { - [JsonPropertyName("topic")] - public string? Topic { get; set; } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs b/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs deleted file mode 100644 index 41bf0a8..0000000 --- a/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Interfaces; - -namespace MatrixRoomUtils.Core.StateEventTypes; - -[MatrixEvent(EventName = "m.room.server_acl")] -public class ServerACLData : IStateEventType { - [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; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs new file mode 100644 index 0000000..4d6f9c3 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.canonical_alias")] +public class CanonicalAliasEventData : IStateEventType { + [JsonPropertyName("alias")] + public string? Alias { get; set; } + [JsonPropertyName("alt_aliases")] + public string[]? AltAliases { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs new file mode 100644 index 0000000..1727ce9 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.guest_access")] +public class GuestAccessData : IStateEventType { + [JsonPropertyName("guest_access")] + public string GuestAccess { get; set; } + + public bool IsGuestAccessEnabled { + get => GuestAccess == "can_join"; + set => GuestAccess = value ? "can_join" : "forbidden"; + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs new file mode 100644 index 0000000..2bae838 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.history_visibility")] +public class HistoryVisibilityData : IStateEventType { + [JsonPropertyName("history_visibility")] + public string HistoryVisibility { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs new file mode 100644 index 0000000..590835b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core; + +[MatrixEvent(EventName = "m.room.join_rules")] +public class JoinRulesEventData : IStateEventType { + private static string Public = "public"; + private static string Invite = "invite"; + private static string Knock = "knock"; + + [JsonPropertyName("join_rule")] + public string JoinRule { get; set; } + + [JsonPropertyName("allow")] + public List Allow { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs new file mode 100644 index 0000000..bc1c52b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +[MatrixEvent(EventName = "m.room.message")] +public class MessageEventData : IStateEventType { + [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; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs new file mode 100644 index 0000000..debbef0 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.policy.rule.user")] +[MatrixEvent(EventName = "m.policy.rule.server")] +[MatrixEvent(EventName = "org.matrix.mjolnir.rule.server")] +public class PolicyRuleStateEventData : IStateEventType { + /// + /// 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 +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs new file mode 100644 index 0000000..c6100bb --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.power_levels")] +public class PowerLevelEvent : IStateEventType { + [JsonPropertyName("ban")] + public int Ban { get; set; } // = 50; + + [JsonPropertyName("events_default")] + public int EventsDefault { get; set; } // = 0; + + [JsonPropertyName("events")] + public Dictionary Events { get; set; } // = null!; + + [JsonPropertyName("invite")] + public int Invite { get; set; } // = 50; + + [JsonPropertyName("kick")] + public int Kick { get; set; } // = 50; + + [JsonPropertyName("notifications")] + public NotificationsPL NotificationsPl { get; set; } // = null!; + + [JsonPropertyName("redact")] + public int Redact { get; set; } // = 50; + + [JsonPropertyName("state_default")] + public int StateDefault { get; set; } // = 50; + + [JsonPropertyName("users")] + public Dictionary Users { get; set; } // = null!; + + [JsonPropertyName("users_default")] + public int 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 int Historical { get; set; } // = 50; + + public class NotificationsPL { + [JsonPropertyName("room")] + public int Room { get; set; } = 50; + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs new file mode 100644 index 0000000..a17b6f9 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.presence")] +public class PresenceStateEventData : IStateEventType { + [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; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs new file mode 100644 index 0000000..d36ef74 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.member")] +public class ProfileResponse { + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } = ""; + + [JsonPropertyName("displayname")] + public string? DisplayName { get; set; } = ""; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs new file mode 100644 index 0000000..03ce16b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.avatar")] +public class RoomAvatarEventData : IStateEventType { + [JsonPropertyName("url")] + public string? Url { get; set; } + + [JsonPropertyName("info")] + public RoomAvatarInfo? Info { get; set; } + + public class RoomAvatarInfo { + + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs new file mode 100644 index 0000000..2e4bb5a --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomCreateEventData.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.create")] +public class RoomCreateEventData : IStateEventType { + [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 { } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomEncryptionEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomEncryptionEventData.cs new file mode 100644 index 0000000..8d0576d --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomEncryptionEventData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.encryption")] +public class RoomEncryptionEventData : IStateEventType { + [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; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs new file mode 100644 index 0000000..50d9dd2 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.member")] +public class RoomMemberEventData : IStateEventType { + [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; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomNameEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomNameEventData.cs new file mode 100644 index 0000000..642b5f9 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomNameEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.name")] +public class RoomNameEventData : IStateEventType { + [JsonPropertyName("name")] + public string? Name { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPinnedEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPinnedEventData.cs new file mode 100644 index 0000000..05c0048 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomPinnedEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.pinned_events")] +public class RoomPinnedEventData : IStateEventType { + [JsonPropertyName("pinned")] + public string[]? PinnedEvents { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs new file mode 100644 index 0000000..cc5b35b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.topic")] +[MatrixEvent(EventName = "org.matrix.msc3765.topic", Legacy = true)] +public class RoomTopicEventData : IStateEventType { + [JsonPropertyName("topic")] + public string? Topic { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTypingEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTypingEventData.cs new file mode 100644 index 0000000..eac4af2 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTypingEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.typing")] +public class RoomTypingEventData : IStateEventType { + [JsonPropertyName("user_ids")] + public string[]? UserIds { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs new file mode 100644 index 0000000..41bf0a8 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.server_acl")] +public class ServerACLData : IStateEventType { + [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; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceChildEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceChildEventData.cs new file mode 100644 index 0000000..f65cd5b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceChildEventData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.space.child")] +public class SpaceChildEventData : IStateEventType { + [JsonPropertyName("auto_join")] + public bool? AutoJoin { get; set; } + [JsonPropertyName("via")] + public string[]? Via { get; set; } + [JsonPropertyName("suggested")] + public bool? Suggested { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceParentEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceParentEventData.cs new file mode 100644 index 0000000..a40f7ae --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/SpaceParentEventData.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes.Spec; + +[MatrixEvent(EventName = "m.space.parent")] +public class SpaceParentEventData : IStateEventType { + [JsonPropertyName("via")] + public string[]? Via { get; set; } + + [JsonPropertyName("canonical")] + public bool? Canonical { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user index 93b1fca..8c6a223 100644 --- a/MatrixRoomUtils.sln.DotSettings.user +++ b/MatrixRoomUtils.sln.DotSettings.user @@ -10,6 +10,7 @@ True + True -- cgit 1.5.1