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>
|