diff options
46 files changed, 471 insertions, 248 deletions
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<bool> 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<ICommand>().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/FileStorageProvider.cs b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs index 8d99828..8d99828 100644 --- a/MatrixRoomUtils.Bot/FileStorageProvider.cs +++ b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs 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<bool> CanInvoke(CommandContext ctx) { + return Task.FromResult(true); + } + + public Task Invoke(CommandContext ctx); +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/MRUBot.cs b/MatrixRoomUtils.Bot/Bot/MRUBot.cs index 157673d..81123e0 100644 --- a/MatrixRoomUtils.Bot/MRUBot.cs +++ b/MatrixRoomUtils.Bot/Bot/MRUBot.cs @@ -1,11 +1,13 @@ 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; @@ -13,13 +15,17 @@ public class MRUBot : IHostedService { private readonly HomeserverProviderService _homeserverProviderService; private readonly ILogger<MRUBot> _logger; private readonly MRUBotConfiguration _configuration; + private readonly IEnumerable<ICommand> _commands; public MRUBot(HomeserverProviderService homeserverProviderService, ILogger<MRUBot> logger, - MRUBotConfiguration configuration) { + MRUBotConfiguration configuration, IServiceProvider services) { Console.WriteLine("MRUBot hosted service instantiated!"); _homeserverProviderService = homeserverProviderService; _logger = logger; _configuration = configuration; + Console.WriteLine("Getting commands..."); + _commands = services.GetServices<ICommand>(); + Console.WriteLine($"Got {_commands.Count()} commands!"); } /// <summary>Triggered when the application host is ready to start the service.</summary> @@ -39,18 +45,20 @@ public class MRUBot : IHostedService { 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)); - // } + // foreach (var room in await hs.GetJoinedRooms()) { + // if(room.RoomId is "!OGEhHVWSdvArJzumhm:matrix.org") continue; + // foreach (var stateEvent in await room.GetStateAsync<List<StateEvent>>("")) { + // 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 MemberEventData).Reason}"); + $"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&)!"); @@ -65,17 +73,37 @@ public class MRUBot : IHostedService { 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", 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!" }); + 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); } diff --git a/MatrixRoomUtils.Bot/MRUBotConfiguration.cs b/MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs index 14d9b60..c91698a 100644 --- a/MatrixRoomUtils.Bot/MRUBotConfiguration.cs +++ b/MatrixRoomUtils.Bot/Bot/MRUBotConfiguration.cs @@ -8,4 +8,5 @@ public class MRUBotConfiguration { } 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/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 @@ </ItemGroup> <ItemGroup> + <PackageReference Include="ArcaneLibs" Version="1.0.0-preview3020494760.012ed3f" /> + <PackageReference Include="Markdig" Version="0.31.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.5.23280.8" /> </ItemGroup> <ItemGroup> <Content Include="appsettings*.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> + <Content Update="appsettings.Local.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> </ItemGroup> </Project> 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<MRUBotConfiguration>(); services.AddRoryLibMatrixServices(); + foreach (var commandClass in new ClassCollector<ICommand>().ResolveFromAllAccessibleAssemblies()) { + Console.WriteLine($"Adding command {commandClass.Name}"); + services.AddScoped(typeof(ICommand), commandClass); + } services.AddHostedService<MRUBot>(); }).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<Room> GetRoom(string roomId) => new Room(_httpClient, roomId); + public async Task<GenericRoom> GetRoom(string roomId) => new(this, roomId); - public async Task<List<Room>> GetJoinedRooms() { - var rooms = new List<Room>(); + public async Task<List<GenericRoom>> GetJoinedRooms() { + var rooms = new List<GenericRoom>(); var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms"); var roomsJson = await roomQuery.Content.ReadFromJsonAsync<JsonElement>(); - 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<Room> CreateRoom(CreateRoomRequest creationEvent) { + public async Task<GenericRoom> 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<LoginResponse> 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<JsonElement>(); 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<LoginResponse>(); 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<StateEventResponse> DeserializeMatrixTypes(this List<JsonElement> 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<MatrixEventAttribute>()?.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<StateEvent> 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 { /// <summary> /// Event fired when a room invite is received /// </summary> - public event EventHandler<KeyValuePair<string, SyncResult.RoomsDataStructure.InvitedRoomDataStructure>>? InviteReceived; + public event EventHandler<KeyValuePair<string, SyncResult.RoomsDataStructure.InvitedRoomDataStructure>>? + InviteReceived; public event EventHandler<StateEventResponse>? TimelineEventReceived; public event EventHandler<StateEventResponse>? 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<string> _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/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 @@ </PropertyGroup> <ItemGroup> - <Reference Include="Microsoft.AspNetCore.Mvc.Core"> - <HintPath>..\..\..\.cache\NuGetPackages\microsoft.aspnetcore.app.ref\7.0.5\ref\net7.0\Microsoft.AspNetCore.Mvc.Core.dll</HintPath> - </Reference> - <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions"> - <HintPath>..\..\..\.cache\NuGetPackages\microsoft.extensions.dependencyinjection.abstractions\7.0.0\lib\net7.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath> - </Reference> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> </ItemGroup> </Project> 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<Room> GetRoom(string roomId) => new Room(_httpClient, roomId); - - public async Task<List<Room>> GetJoinedRooms() { - var rooms = new List<Room>(); - 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<JsonElement>(); - 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/RoomTypes/GenericRoom.cs index 59c56ed..8dc30d1 100644 --- a/MatrixRoomUtils.Core/Room.cs +++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs @@ -7,14 +7,16 @@ using MatrixRoomUtils.Core.RoomTypes; namespace MatrixRoomUtils.Core; -public class Room { - private readonly HttpClient _httpClient; +public class GenericRoom { + internal readonly AuthenticatedHomeServer _homeServer; + internal readonly HttpClient _httpClient; - public Room(HttpClient httpClient, string roomId) { - _httpClient = httpClient; + public GenericRoom(AuthenticatedHomeServer homeServer, string roomId) { + _homeServer = homeServer; + _httpClient = homeServer._httpClient; RoomId = roomId; - if(GetType() != typeof(SpaceRoom)) - AsSpace = new SpaceRoom(_httpClient, RoomId); + if (GetType() != typeof(SpaceRoom)) + AsSpace = new SpaceRoom(homeServer, RoomId); } public string RoomId { get; set; } @@ -40,7 +42,8 @@ public class Room { return res.Value.Deserialize<T>(); } - public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") { + public async Task<MessagesResponse> 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); @@ -83,7 +86,8 @@ public class Room { 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!")); + members.Add( + memberId ?? throw new InvalidOperationException("Event type was member but state key was null!")); } return members; @@ -116,10 +120,10 @@ public class Room { return res.Value.GetProperty("url").GetString() ?? ""; } - public async Task<JoinRules> GetJoinRuleAsync() { + public async Task<JoinRulesEventData> GetJoinRuleAsync() { var res = await GetStateAsync("m.room.join_rules"); - if (!res.HasValue) return new JoinRules(); - return res.Value.Deserialize<JoinRules>() ?? new JoinRules(); + if (!res.HasValue) return new JoinRulesEventData(); + return res.Value.Deserialize<JoinRulesEventData>() ?? new JoinRulesEventData(); } public async Task<string> GetHistoryVisibilityAsync() { @@ -149,7 +153,7 @@ public class Room { 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) { @@ -157,7 +161,7 @@ public class Room { 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 @@ -167,50 +171,73 @@ public class Room { 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 }); + 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 }); + 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 }); + 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<EventIdResponse> 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}"); + 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<EventIdResponse>(); } - - public async Task<EventIdResponse> SendMessageEventAsync(string eventType, object content) { - var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/"+new Guid(), content); + + public async Task<EventIdResponse> 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}"); } - return await res.Content.ReadFromJsonAsync<EventIdResponse>(); + + var resu = await res.Content.ReadFromJsonAsync<EventIdResponse>(); + + return resu; } + public async Task<EventIdResponse> 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<EventIdResponse>(); + + 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<List<Room>> GetRoomsAsync(bool includeRemoved = false) { - var rooms = new List<Room>(); + public async Task<List<GenericRoom>> GetRoomsAsync(bool includeRemoved = false) { + var rooms = new List<GenericRoom>(); var state = await GetStateAsync(""); if (state != null) { var states = state.Value.Deserialize<StateEventResponse[]>()!; 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<string, UserInfo> LoginSessions { get; set; } = new(); - - public static Dictionary<string, HomeServerResolutionResult> HomeserverResolutionCache { get; set; } = new(); - // public static Dictionary<string, (DateTime cachedAt, ProfileResponse response)> ProfileCache { get; set; } = new(); - - public static Dictionary<string, ObjectCache<object>> 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<string, object> SaveObject { get; set; } = (key, value) => { Console.WriteLine($"RuntimeCache.SaveObject({key}, {value}) was called, but no callback was set!"); }; - public static Action<string> 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<T> 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<string, GenericResult<T>> Cache { get; set; } = new(); - public string Name { get; set; } = null!; - - public GenericResult<T> 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<T> { - 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<T1>() where T1 : IStateEventType { return RawContent.Deserialize<T1>(); @@ -35,17 +60,36 @@ public class StateEvent { [JsonIgnore] public Type GetType { get { - var type = StateEvent.KnownStateEventTypes.FirstOrDefault(x => - x.GetCustomAttribute<MatrixEventAttribute>()?.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<string, JsonObject>); + } + + var type = KnownStateEventTypes.FirstOrDefault(x => + x.GetCustomAttributes<MatrixEventAttribute>()?.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/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<string, EmoticonData>? Emoticons { get; set; } + + [JsonPropertyName("images")] + public Dictionary<string, EmoticonData>? 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/CanonicalAliasEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs index 2f9502e..4d6f9c3 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/CanonicalAliasEventData.cs @@ -8,4 +8,6 @@ namespace MatrixRoomUtils.Core.StateEventTypes; 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/GuestAccessData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs index 1727ce9..1727ce9 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/GuestAccessData.cs diff --git a/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs index 481cc08..2bae838 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/HistoryVisibilityData.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; @@ -5,5 +6,6 @@ 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/JoinRules.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs index 7ce56c4..590835b 100644 --- a/MatrixRoomUtils.Core/JoinRules.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/JoinRulesEventData.cs @@ -1,8 +1,11 @@ using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core; -public class JoinRules { +[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"; diff --git a/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs index ad99709..bc1c52b 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/MessageEventData.cs @@ -7,5 +7,11 @@ public class MessageEventData : IStateEventType { [JsonPropertyName("body")] public string Body { get; set; } [JsonPropertyName("msgtype")] - public string MessageType { get; set; } + 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/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs index e67639b..debbef0 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PolicyRuleStateEventData.cs @@ -1,8 +1,12 @@ 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 { /// <summary> /// Entity this ban applies to, can use * and ? as globs. diff --git a/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs index a3e44d1..c6100bb 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PowerLevelEvent.cs @@ -36,6 +36,11 @@ public class PowerLevelEvent : IStateEventType { [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; diff --git a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs index a17b6f9..a17b6f9 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/PresenceStateEventData.cs diff --git a/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs index d36ef74..d36ef74 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/ProfileResponse.cs diff --git a/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs index 03ce16b..03ce16b 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAvatarEventData.cs 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/MemberEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs index acf7777..50d9dd2 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomMemberEventData.cs @@ -5,7 +5,7 @@ using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core.StateEventTypes; [MatrixEvent(EventName = "m.room.member")] -public class MemberEventData : IStateEventType { +public class RoomMemberEventData : IStateEventType { [JsonPropertyName("reason")] public string? Reason { get; set; } @@ -17,7 +17,13 @@ public class MemberEventData : IStateEventType { [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/RoomTopicEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs index 72651c8..cc5b35b 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomTopicEventData.cs @@ -5,6 +5,7 @@ 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; } 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/ServerACLData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs index 41bf0a8..41bf0a8 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/ServerACLData.cs 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 @@ <s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=d38da95d_002Ddd83_002D4340_002D96a4_002D6f59fc6ae3d9_0023MatrixRoomUtils_002EWeb/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=f997f26f_002D2ec1_002D4d18_002Db3dd_002Dc46fb2ad65c0_0023MatrixRoomUtils_002EWeb_002EServer/@EntryIndexedValue">True</s:Boolean> |