diff options
71 files changed, 728 insertions, 461 deletions
diff --git a/.gitignore b/.gitignore index 47d212e..33be3d4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ MatrixRoomUtils.Web/wwwroot/MRU.tar.xz *.tar.xz matrix-sync.json /patches/ +MatrixRoomUtils.Bot/bot_data/ +appsettings.Local.json diff --git a/.idea/.idea.MatrixRoomUtils/.idea/indexLayout.xml b/.idea/.idea.MatrixRoomUtils/.idea/indexLayout.xml index 7b08163..9c54b37 100644 --- a/.idea/.idea.MatrixRoomUtils/.idea/indexLayout.xml +++ b/.idea/.idea.MatrixRoomUtils/.idea/indexLayout.xml @@ -1,7 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="UserContentModel"> - <attachedFolders /> + <attachedFolders> + <Path>MatrixRoomUtils.Bot/bot_data</Path> + </attachedFolders> <explicitIncludes /> <explicitExcludes /> </component> diff --git a/MatrixRoomUtils.Bot/MRUBot.cs b/MatrixRoomUtils.Bot/MRUBot.cs index 9b2a395..157673d 100644 --- a/MatrixRoomUtils.Bot/MRUBot.cs +++ b/MatrixRoomUtils.Bot/MRUBot.cs @@ -1,55 +1,82 @@ -using System.CodeDom.Compiler; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using MatrixRoomUtils.Bot; using MatrixRoomUtils.Core; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Helpers; -using MatrixRoomUtils.Core.Responses; using MatrixRoomUtils.Core.Services; +using MatrixRoomUtils.Core.StateEventTypes; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; public class MRUBot : IHostedService { private readonly HomeserverProviderService _homeserverProviderService; private readonly ILogger<MRUBot> _logger; + private readonly MRUBotConfiguration _configuration; - public MRUBot(HomeserverProviderService homeserverProviderService, ILogger<MRUBot> logger) { + public MRUBot(HomeserverProviderService homeserverProviderService, ILogger<MRUBot> logger, + MRUBotConfiguration configuration) { Console.WriteLine("MRUBot hosted service instantiated!"); _homeserverProviderService = homeserverProviderService; _logger = logger; + _configuration = configuration; } /// <summary>Triggered when the application host is ready to start the service.</summary> /// <param name="cancellationToken">Indicates that the start process has been aborted.</param> [SuppressMessage("ReSharper", "FunctionNeverReturns")] public async Task StartAsync(CancellationToken cancellationToken) { - var hs = await _homeserverProviderService.GetAuthenticatedWithToken("rory.gay", "syt_bXJ1Y29yZXRlc3Q_XKUmPswDGZBiLAmFfAut_1iO0KD"); + Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); + AuthenticatedHomeServer hs; + try { + hs = await _homeserverProviderService.GetAuthenticatedWithToken(_configuration.Homeserver, + _configuration.AccessToken); + } + catch (Exception e) { + _logger.LogError(e.Message); + throw; + } + await (await hs.GetRoom("!DoHEdFablOLjddKWIp:rory.gay")).JoinAsync(); - // #pragma warning disable CS4014 - // Task.Run(async Task? () => { - // #pragma warning restore CS4014 - // while (true) { - // var rooms = await hs.GetJoinedRooms(); - // foreach (var room in rooms) { - // var states = await room.GetStateAsync<List<StateEventResponse>>(""); - // foreach (var state in states) { - // // Console.WriteLine( - // // $"{state.RoomId}: {state.Type}::{state.StateKey} = {ObjectExtensions.ToJson(state.Content, indent: false)}"); - // } - // } - // - // await Task.Delay(1000, cancellationToken); - // } - // }, cancellationToken); - #pragma warning disable CS4014 - Task.Run(async Task? () => { - #pragma warning restore CS4014 - SyncResult? sync = null; - while (true) { - sync = await hs.SyncHelper.Sync(sync?.NextBatch); - _logger.LogInformation($"Got sync, next batch: {sync?.NextBatch}!"); - } - }, cancellationToken); - + + hs.SyncHelper.InviteReceived += async (_, args) => { + // Console.WriteLine($"Got invite to {args.Key}:"); + // foreach (var stateEvent in args.Value.InviteState.Events) { + // Console.WriteLine($"[{stateEvent.Sender}: {stateEvent.StateKey}::{stateEvent.Type}] " + + // ObjectExtensions.ToJson(stateEvent.Content, indent: false, ignoreNull: true)); + // } + + var inviteEvent = + args.Value.InviteState.Events.FirstOrDefault(x => + x.Type == "m.room.member" && x.StateKey == hs.WhoAmI.UserId); + Console.WriteLine( + $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as MemberEventData).Reason}"); + if (inviteEvent.Sender == "@emma:rory.gay") { + try { + await (await hs.GetRoom(args.Key)).JoinAsync(reason: "I was invited by Emma (Rory&)!"); + } + catch (Exception e) { + Console.WriteLine(e); + await (await hs.GetRoom(args.Key)).LeaveAsync(reason: "I was unable to join the room: " + e); + } + } + }; + hs.SyncHelper.TimelineEventReceived += async (_, @event) => { + Console.WriteLine( + $"Got timeline event in {@event.RoomId}: {@event.ToJson(indent: false, ignoreNull: true)}"); + + // Console.WriteLine(eventResponse.ToJson(indent: false)); + if (@event is { Type: "m.room.message", TypedContent: MessageEventData message }) { + if (message is { MessageType: "m.text", Body: "!ping" }) { + Console.WriteLine( + $"Got ping from {@event.Sender} in {@event.RoomId} with message id {@event.EventId}!"); + await (await hs.GetRoom(@event.RoomId)).SendMessageEventAsync("m.room.message", + new MessageEventData() { MessageType = "m.text", Body = "pong!" }); + } + } + }; + + await hs.SyncHelper.RunSyncLoop(cancellationToken); } /// <summary>Triggered when the application host is performing a graceful shutdown.</summary> diff --git a/MatrixRoomUtils.Bot/MRUBotConfiguration.cs b/MatrixRoomUtils.Bot/MRUBotConfiguration.cs new file mode 100644 index 0000000..14d9b60 --- /dev/null +++ b/MatrixRoomUtils.Bot/MRUBotConfiguration.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.Configuration; + +namespace MatrixRoomUtils.Bot; + +public class MRUBotConfiguration { + public MRUBotConfiguration(IConfiguration config) { + config.GetRequiredSection("Bot").Bind(this); + } + public string Homeserver { get; set; } = ""; + public string AccessToken { get; set; } = ""; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj index 095d0f6..a82b6aa 100644 --- a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj +++ b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj @@ -23,5 +23,9 @@ <ItemGroup> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.5.23280.8" /> </ItemGroup> - + <ItemGroup> + <Content Include="appsettings*.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + </ItemGroup> </Project> diff --git a/MatrixRoomUtils.Bot/Program.cs b/MatrixRoomUtils.Bot/Program.cs index 441003e..e8a5b96 100644 --- a/MatrixRoomUtils.Bot/Program.cs +++ b/MatrixRoomUtils.Bot/Program.cs @@ -7,20 +7,16 @@ using Microsoft.Extensions.Hosting; Console.WriteLine("Hello, World!"); -using IHost host = Host.CreateDefaultBuilder(args) - .ConfigureServices((_, services) => { - services.AddScoped<TieredStorageService>(x => - new( - cacheStorageProvider: new FileStorageProvider("data/cache/"), - dataStorageProvider: new FileStorageProvider("data/data/") - ) - ); - - services.AddRoryLibMatrixServices(); - - services.AddHostedService<MRUBot>(); - }) - .Build(); - +var host = Host.CreateDefaultBuilder(args).ConfigureServices((_, services) => { + services.AddScoped<TieredStorageService>(x => + new( + cacheStorageProvider: new FileStorageProvider("bot_data/cache/"), + dataStorageProvider: new FileStorageProvider("bot_data/data/") + ) + ); + services.AddScoped<MRUBotConfiguration>(); + services.AddRoryLibMatrixServices(); + services.AddHostedService<MRUBot>(); +}).UseConsoleLifetime().Build(); await host.RunAsync(); \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/Properties/launchSettings.json b/MatrixRoomUtils.Bot/Properties/launchSettings.json new file mode 100644 index 0000000..997e294 --- /dev/null +++ b/MatrixRoomUtils.Bot/Properties/launchSettings.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Default": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + + } + }, + "Development": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + }, + "Local config": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Local" + } + } + } +} diff --git a/MatrixRoomUtils.Bot/appsettings.Development.json b/MatrixRoomUtils.Bot/appsettings.Development.json new file mode 100644 index 0000000..27bbd50 --- /dev/null +++ b/MatrixRoomUtils.Bot/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Bot/appsettings.json b/MatrixRoomUtils.Bot/appsettings.json new file mode 100644 index 0000000..5668b53 --- /dev/null +++ b/MatrixRoomUtils.Bot/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "Bot": { + "Homeserver": "rory.gay", + "AccessToken": "syt_xxxxxxxxxxxxxxxxx" + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs index 74e85b1..23e98ae 100644 --- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs +++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -2,6 +2,7 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Filters; using MatrixRoomUtils.Core.Helpers; @@ -17,15 +18,16 @@ public class AuthenticatedHomeServer : IHomeServer { public readonly HomeserverAdminApi Admin; public readonly SyncHelper SyncHelper; - public AuthenticatedHomeServer(string canonicalHomeServerDomain, string accessToken, TieredStorageService storage) { + public AuthenticatedHomeServer(TieredStorageService storage, string canonicalHomeServerDomain, string accessToken) { _storage = storage; - AccessToken = accessToken; - HomeServerDomain = canonicalHomeServerDomain; + AccessToken = accessToken.Trim(); + HomeServerDomain = canonicalHomeServerDomain.Trim(); Admin = new HomeserverAdminApi(this); SyncHelper = new SyncHelper(this, storage); _httpClient = new MatrixHttpClient(); } + public WhoAmIResponse WhoAmI { get; set; } = null!; public string UserId { get; } public string AccessToken { get; set; } @@ -35,6 +37,7 @@ public class AuthenticatedHomeServer : IHomeServer { _httpClient = new MatrixHttpClient { BaseAddress = new Uri(FullHomeServerDomain) }; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken); Console.WriteLine("[AHS] Finished setting up http client"); + WhoAmI = (await _httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!; return this; } @@ -54,7 +57,7 @@ public class AuthenticatedHomeServer : IHomeServer { } public async Task<string> UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream") { - var res = await _httpClient.PostAsync($"/_matrix/media/r0/upload?filename={fileName}", new StreamContent(fileStream)); + var res = await _httpClient.PostAsync($"/_matrix/media/v3/upload?filename={fileName}", new StreamContent(fileStream)); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to upload file: {await res.Content.ReadAsStringAsync()}"); throw new InvalidDataException($"Failed to upload file: {await res.Content.ReadAsStringAsync()}"); @@ -65,7 +68,7 @@ public class AuthenticatedHomeServer : IHomeServer { } public async Task<Room> CreateRoom(CreateRoomRequest creationEvent) { - var res = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/createRoom", creationEvent); + var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); throw new InvalidDataException($"Failed to create room: {await res.Content.ReadAsStringAsync()}"); @@ -168,4 +171,14 @@ public class AuthenticatedHomeServer : IHomeServer { } while (i < Math.Min(limit, totalRooms ?? limit)); } } +} + +public class WhoAmIResponse { + [JsonPropertyName("user_id")] + public string UserId { get; set; } = null!; + + [JsonPropertyName("device_id")] + public string? DeviceId { get; set; } + [JsonPropertyName("is_guest")] + public bool? IsGuest { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs index 0f9eb58..83b279a 100644 --- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs +++ b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs @@ -2,6 +2,7 @@ using System.Net.Http.Json; using System.Text.Json; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core.Authentication; @@ -20,7 +21,7 @@ public class MatrixAuth { initial_device_display_name = "Rory&::MatrixRoomUtils" }; Console.WriteLine($"Sending login request to {homeserver}..."); - var resp = await hc.PostAsJsonAsync($"{homeserver}/_matrix/client/r0/login", payload); + var resp = await hc.PostAsJsonAsync($"{homeserver}/_matrix/client/v3/login", payload); Console.WriteLine($"Login: {resp.StatusCode}"); var data = await resp.Content.ReadFromJsonAsync<JsonElement>(); if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data); diff --git a/MatrixRoomUtils.Core/CreateEvent.cs b/MatrixRoomUtils.Core/CreateEvent.cs new file mode 100644 index 0000000..a7022c5 --- /dev/null +++ b/MatrixRoomUtils.Core/CreateEvent.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace MatrixRoomUtils.Core; + +public class CreateEvent { + [JsonPropertyName("creator")] + public string Creator { get; set; } + + [JsonPropertyName("room_version")] + public string RoomVersion { get; set; } + + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("predecessor")] + public object? Predecessor { get; set; } + + [JsonPropertyName("m.federate")] + public bool Federate { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/EventIdResponse.cs b/MatrixRoomUtils.Core/EventIdResponse.cs new file mode 100644 index 0000000..77dc7f8 --- /dev/null +++ b/MatrixRoomUtils.Core/EventIdResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace MatrixRoomUtils.Core; + +public class EventIdResponse { + [JsonPropertyName("event_id")] + public string EventId { get; set; } = null!; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/ClassCollector.cs b/MatrixRoomUtils.Core/Extensions/ClassCollector.cs new file mode 100644 index 0000000..9d3d3c0 --- /dev/null +++ b/MatrixRoomUtils.Core/Extensions/ClassCollector.cs @@ -0,0 +1,30 @@ +using System.Reflection; + +namespace MatrixRoomUtils.Core.Extensions; + +public class ClassCollector<T> where T : class { + static ClassCollector() { + if (!typeof(T).IsInterface) + throw new ArgumentException( + $"ClassCollector<T> must be used with an interface type. Passed type: {typeof(T).Name}"); + } + + public List<Type> ResolveFromAllAccessibleAssemblies() => AppDomain.CurrentDomain.GetAssemblies().SelectMany(ResolveFromAssembly).ToList(); + + public List<Type> ResolveFromObjectReference(object obj) => ResolveFromTypeReference(obj.GetType()); + + public List<Type> ResolveFromTypeReference(Type t) => Assembly.GetAssembly(t)?.GetReferencedAssemblies().SelectMany(ResolveFromAssemblyName).ToList() ?? new List<Type>(); + + public List<Type> ResolveFromAssemblyName(AssemblyName assemblyName) => ResolveFromAssembly(Assembly.Load(assemblyName)); + + public List<Type> ResolveFromAssembly(Assembly assembly) => assembly.GetTypes() + .Where(x => x is { IsClass: true, IsAbstract: false } && x.GetInterfaces().Contains(typeof(T))).ToList(); + // { + // List<Type> ret = new(); + // foreach (var x in assembly.GetTypes().Where(x => x is { IsClass: true, IsAbstract: false } && x.GetInterfaces().Contains(typeof(T))).ToList()) { + // // Console.WriteLine($"[!!] Found class {x.FullName}"); + // ret.Add(x); + // } + // return ret; + // } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs b/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs new file mode 100644 index 0000000..98b0aab --- /dev/null +++ b/MatrixRoomUtils.Core/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,36 @@ +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; + } +} + +public class MatrixEventAttribute : Attribute { + public string EventName { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs index b007136..78f4456 100644 --- a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs @@ -1,12 +1,13 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; namespace MatrixRoomUtils.Core.Extensions; public static class JsonElementExtensions { - public static void FindExtraJsonFields([DisallowNull] this JsonElement? res, Type t) { + public static void FindExtraJsonElementFields([DisallowNull] this JsonElement? res, Type t) { var props = t.GetProperties(); var unknownPropertyFound = false; foreach (var field in res.Value.EnumerateObject()) { @@ -17,4 +18,18 @@ public static class JsonElementExtensions { if (unknownPropertyFound) Console.WriteLine(res.Value.ToJson()); } + public static void FindExtraJsonObjectFields([DisallowNull] this JsonObject? res, Type t) { + var props = t.GetProperties(); + var unknownPropertyFound = false; + foreach (var field in res) { + if (props.Any(x => x.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name == field.Key)) continue; + Console.WriteLine($"[!!] Unknown property {field.Key} in {t.Name}!"); + unknownPropertyFound = true; + // foreach (var propertyInfo in props) { + // Console.WriteLine($"[!!] Known property {propertyInfo.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name} in {t.Name}!"); + // } + } + + if (unknownPropertyFound) Console.WriteLine(res.ToJson()); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs index 812c81c..a4b0791 100644 --- a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs @@ -1,5 +1,7 @@ using System.Text.Encodings.Web; using System.Text.Json; +using MatrixRoomUtils.Core.Interfaces; +using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.Extensions; diff --git a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs index edbb646..04c31cd 100644 --- a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs +++ b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs @@ -1,5 +1,7 @@ +using System.Diagnostics.CodeAnalysis; using System.Net.Http.Json; using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Interfaces; using MatrixRoomUtils.Core.Responses; using MatrixRoomUtils.Core.Services; using MatrixRoomUtils.Core.StateEventTypes; @@ -15,7 +17,7 @@ public class SyncHelper { _storageService = storageService; } - public async Task<SyncResult?> Sync(string? since = null) { + public async Task<SyncResult?> Sync(string? since = null, CancellationToken? cancellationToken = null) { var outFileName = "sync-" + (await _storageService.CacheStorageProvider.GetAllKeys()).Count(x => x.StartsWith("sync")) + ".json"; @@ -23,12 +25,66 @@ public class SyncHelper { if (!string.IsNullOrWhiteSpace(since)) url += $"&since={since}"; else url += "&full_state=true"; Console.WriteLine("Calling: " + url); - var res = await _homeServer._httpClient.GetFromJsonAsync<SyncResult>(url); - await _storageService.CacheStorageProvider.SaveObject(outFileName, res); - return res; + try { + var res = await _homeServer._httpClient.GetFromJsonAsync<SyncResult>(url, + cancellationToken: cancellationToken ?? CancellationToken.None); + await _storageService.CacheStorageProvider.SaveObject(outFileName, res); + Console.WriteLine($"Wrote file: {outFileName}"); + return res; + } + catch (TaskCanceledException) { + Console.WriteLine("Sync cancelled!"); + } + catch (Exception e) { + Console.WriteLine(e); + } + return null; + } + + [SuppressMessage("ReSharper", "FunctionNeverReturns")] + public async Task RunSyncLoop(CancellationToken? cancellationToken = null, bool skipInitialSyncEvents = true) { + SyncResult? sync = null; + while (cancellationToken is null || !cancellationToken.Value.IsCancellationRequested) { + 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); + InviteReceived?.Invoke(this, roomInvite); + } + } + + if (sync.AccountData is { Events: { Count: > 0 } }) { + foreach (var accountDataEvent in sync.AccountData.Events) { + AccountDataReceived?.Invoke(this, accountDataEvent); + } + } + + // Things that are skipped on the first sync + if (skipInitialSyncEvents) { + skipInitialSyncEvents = false; + continue; + } + + if (sync.Rooms is { Join.Count: > 0 }) { + foreach (var updatedRoom in sync.Rooms.Join) { + foreach (var stateEventResponse in updatedRoom.Value.Timeline.Events) { + stateEventResponse.RoomId = updatedRoom.Key; + TimelineEventReceived?.Invoke(this, stateEventResponse); + } + } + } + } } - - public event EventHandler<SyncResult>? ; + + /// <summary> + /// Event fired when a room invite is received + /// </summary> + public event EventHandler<KeyValuePair<string, SyncResult.RoomsDataStructure.InvitedRoomDataStructure>>? InviteReceived; + + public event EventHandler<StateEventResponse>? TimelineEventReceived; + public event EventHandler<StateEventResponse>? AccountDataReceived; } public class SyncResult { @@ -36,30 +92,30 @@ public class SyncResult { public string NextBatch { get; set; } [JsonPropertyName("account_data")] - public EventList AccountData { get; set; } + public EventList? AccountData { get; set; } [JsonPropertyName("presence")] - public PresenceDataStructure Presence { get; set; } + public PresenceDataStructure? Presence { get; set; } [JsonPropertyName("device_one_time_keys_count")] public Dictionary<string, int> DeviceOneTimeKeysCount { get; set; } [JsonPropertyName("rooms")] - public RoomsDataStructure Rooms { get; set; } + public RoomsDataStructure? Rooms { get; set; } // supporting classes public class PresenceDataStructure { [JsonPropertyName("events")] - public List<StateEventResponse<PresenceStateEventData>> Events { get; set; } + public List<StateEventResponse> Events { get; set; } } public class RoomsDataStructure { [JsonPropertyName("join")] - public Dictionary<string, JoinedRoomDataStructure> Join { get; set; } + public Dictionary<string, JoinedRoomDataStructure>? Join { get; set; } [JsonPropertyName("invite")] - public Dictionary<string, InvitedRoomDataStructure> Invite { get; set; } - + public Dictionary<string, InvitedRoomDataStructure>? Invite { get; set; } + public class JoinedRoomDataStructure { [JsonPropertyName("timeline")] public TimelineDataStructure Timeline { get; set; } @@ -75,7 +131,7 @@ public class SyncResult { [JsonPropertyName("unread_notifications")] public UnreadNotificationsDataStructure UnreadNotifications { get; set; } - + [JsonPropertyName("summary")] public SummaryDataStructure Summary { get; set; } @@ -97,12 +153,14 @@ public class SyncResult { [JsonPropertyName("highlight_count")] public int HighlightCount { get; set; } } - + public class SummaryDataStructure { [JsonPropertyName("m.heroes")] public List<string> Heroes { get; set; } + [JsonPropertyName("m.invited_member_count")] public int InvitedMemberCount { get; set; } + [JsonPropertyName("m.joined_member_count")] public int JoinedMemberCount { get; set; } } diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs index c5645e6..fcff0f2 100644 --- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs +++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; using System.Text.Json; using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core.Interfaces; @@ -91,7 +91,7 @@ public class IHomeServer { } _profileCache.Add(mxid, null); - var resp = await _httpClient.GetAsync($"/_matrix/client/r0/profile/{mxid}"); + var resp = await _httpClient.GetAsync($"/_matrix/client/v3/profile/{mxid}"); var data = await resp.Content.ReadFromJsonAsync<JsonElement>(); if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); var profile = data.Deserialize<ProfileResponse>(); @@ -99,5 +99,5 @@ public class IHomeServer { return profile; } - public string? ResolveMediaUri(string mxc) => mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/"); + public string? ResolveMediaUri(string mxc) => mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/v3/download/"); } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Interfaces/IStateEventType.cs b/MatrixRoomUtils.Core/Interfaces/IStateEventType.cs new file mode 100644 index 0000000..053f50c --- /dev/null +++ b/MatrixRoomUtils.Core/Interfaces/IStateEventType.cs @@ -0,0 +1,5 @@ +namespace MatrixRoomUtils.Core.Interfaces; + +public interface IStateEventType { + +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/JoinRules.cs b/MatrixRoomUtils.Core/JoinRules.cs new file mode 100644 index 0000000..7ce56c4 --- /dev/null +++ b/MatrixRoomUtils.Core/JoinRules.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace MatrixRoomUtils.Core; + +public class JoinRules { + private static string Public = "public"; + private static string Invite = "invite"; + private static string Knock = "knock"; + + [JsonPropertyName("join_rule")] + public string JoinRule { get; set; } + + [JsonPropertyName("allow")] + public List<string> Allow { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs index 3df70e1..50fae20 100644 --- a/MatrixRoomUtils.Core/MatrixException.cs +++ b/MatrixRoomUtils.Core/MatrixException.cs @@ -52,6 +52,6 @@ public class MatrixException : Exception { "M_EXCLUSIVE" => "The resource being requested is reserved by an application service, or the application service making the request has not created the resource: " + Error, "M_RESOURCE_LIMIT_EXCEEDED" => "Exceeded resource limit: " + Error, "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => "Cannot leave server notice room: " + Error, - _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson() + _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true) }; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj index 5ca40bd..a043378 100644 --- a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj +++ b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj @@ -7,6 +7,9 @@ </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> diff --git a/MatrixRoomUtils.Core/MessagesResponse.cs b/MatrixRoomUtils.Core/MessagesResponse.cs new file mode 100644 index 0000000..7a303bc --- /dev/null +++ b/MatrixRoomUtils.Core/MessagesResponse.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Interfaces; +using MatrixRoomUtils.Core.Responses; + +namespace MatrixRoomUtils.Core; + +public class MessagesResponse { + [JsonPropertyName("start")] + public string Start { get; set; } + + [JsonPropertyName("end")] + public string? End { get; set; } + + [JsonPropertyName("chunk")] + public List<StateEventResponse> Chunk { get; set; } = new(); + + [JsonPropertyName("state")] + public List<StateEventResponse> State { get; set; } = new(); +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs index da7d569..8719b5a 100644 --- a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs +++ b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs @@ -1,8 +1,8 @@ -using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core.Responses; @@ -20,6 +20,7 @@ public class CreateRoomRequest { //we dont want to use this, we want more control // [JsonPropertyName("preset")] // public string Preset { get; set; } = null!; + [JsonPropertyName("initial_state")] public List<StateEvent> InitialState { get; set; } = null!; @@ -47,237 +48,12 @@ public class CreateRoomRequest { } } - //extra properties - [JsonIgnore] - public string HistoryVisibility { - get { - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility"); - if (stateEvent == null) { - InitialState.Add(new StateEvent { - Type = "m.room.history_visibility", - Content = new JsonObject { - ["history_visibility"] = "shared" - } - }); - return "shared"; - } - - return stateEvent.ContentAsJsonNode["history_visibility"].GetValue<string>(); - } - set { - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility"); - if (stateEvent == null) - InitialState.Add(new StateEvent { - Type = "m.room.history_visibility", - Content = new JsonObject { - ["history_visibility"] = value - } - }); - else { - var v = stateEvent.ContentAsJsonNode; - v["history_visibility"] = value; - stateEvent.ContentAsJsonNode = v; - } - } - } - - [JsonIgnore] - public string RoomIcon { - get { - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar"); - if (stateEvent == null) { - InitialState.Add(new StateEvent { - Type = "m.room.avatar", - Content = new JsonObject { - ["url"] = "" - } - }); - return ""; - } - - return stateEvent.ContentAsJsonNode["url"].GetValue<string>(); - } - set { - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar"); - if (stateEvent == null) - InitialState.Add(new StateEvent { - Type = "m.room.avatar", - Content = new JsonObject { - ["url"] = value - } - }); - else { - var v = stateEvent.ContentAsJsonNode; - v["url"] = value; - stateEvent.ContentAsJsonNode = v; - } - } - } - - // [JsonIgnore] - // public string GuestAccess - // { - // get - // { - // var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.guest_access"); - // if (stateEvent == null) - // { - // InitialState.Add(new StateEvent() - // { - // Type = "m.room.guest_access", - // Content = new JsonObject() - // { - // ["guest_access"] = "can_join" - // } - // }); - // return "can_join"; - // } - // - // return stateEvent.ContentAsJsonNode["guest_access"].GetValue<string>(); - // } - // set - // { - // var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.guest_access"); - // if (stateEvent == null) - // { - // InitialState.Add(new StateEvent() - // { - // Type = "m.room.guest_access", - // Content = new JsonObject() - // { - // ["guest_access"] = value - // } - // }); - // } - // else - // { - // var v = stateEvent.ContentAsJsonNode; - // v["guest_access"] = value; - // stateEvent.ContentAsJsonNode = v; - // } - // } - // } - - public ServerACL ServerACLs { - get { - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl"); - if (stateEvent == null) { - InitialState.Add(new StateEvent { - Type = "m.room.server_acl", - Content = new JsonObject { - ["allow"] = new JsonArray { - "*" - }, - ["deny"] = new JsonArray() - } - }); - return new ServerACL { - Allow = new List<string> { - "*" - }, - Deny = new List<string>(), - AllowIpLiterals = true - }; - } - - return new ServerACL { - Allow = stateEvent.ContentAsJsonNode["allow"].Deserialize<List<string>>(), - Deny = stateEvent.ContentAsJsonNode["deny"].Deserialize<List<string>>(), - AllowIpLiterals = true - }; - } - set { - Console.WriteLine($"Setting server acl to {value.ToJson()}"); - var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl"); - if (stateEvent == null) - InitialState.Add(new StateEvent { - Type = "m.room.server_acl", - Content = new JsonObject { - ["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow)), - ["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny)) - ["allow_ip_literals"] = value.AllowIpLiterals - } - }); - else { - var v = stateEvent.ContentAsJsonNode; - v["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow)); - v["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny)); - v["allow_ip_literals"] = value.AllowIpLiterals; - stateEvent.ContentAsJsonNode = v; - Console.WriteLine($"v={v.ToJson()}"); - Console.WriteLine($"stateEvent.ContentAsJsonNode={stateEvent.ContentAsJsonNode.ToJson()}"); - } - } - } - public Dictionary<string, string> Validate() { Dictionary<string, string> errors = new(); if (!Regex.IsMatch(RoomAliasName, @"[a-zA-Z0-9_\-]+$")) - errors.Add("room_alias_name", "Room alias name must only contain letters, numbers, underscores, and hyphens."); + errors.Add("room_alias_name", + "Room alias name must only contain letters, numbers, underscores, and hyphens."); return errors; } -} - -public class CreationContentBaseType { - private readonly CreateRoomRequest createRoomRequest; - - public CreationContentBaseType(CreateRoomRequest createRoomRequest) => this.createRoomRequest = createRoomRequest; - - [JsonPropertyName("type")] - public string Type { - get => (string)createRoomRequest.CreationContent["type"]; - set { - if (value is "null" or "") createRoomRequest.CreationContent.Remove("type"); - else createRoomRequest.CreationContent["type"] = value; - } - } -} - -public class PowerLevelEvent { - [JsonPropertyName("ban")] - public int Ban { get; set; } // = 50; - - [JsonPropertyName("events_default")] - public int EventsDefault { get; set; } // = 0; - - [JsonPropertyName("events")] - public Dictionary<string, int> Events { get; set; } // = null!; - - [JsonPropertyName("invite")] - public int Invite { get; set; } // = 50; - - [JsonPropertyName("kick")] - public int Kick { get; set; } // = 50; - - [JsonPropertyName("notifications")] - public NotificationsPL NotificationsPl { get; set; } // = null!; - - [JsonPropertyName("redact")] - public int Redact { get; set; } // = 50; - - [JsonPropertyName("state_default")] - public int StateDefault { get; set; } // = 50; - - [JsonPropertyName("users")] - public Dictionary<string, int> Users { get; set; } // = null!; - - [JsonPropertyName("users_default")] - public int UsersDefault { get; set; } // = 0; -} - -public class NotificationsPL { - [JsonPropertyName("room")] - public int Room { get; set; } = 50; -} - -public class ServerACL { - [JsonPropertyName("allow")] - public List<string> Allow { get; set; } // = null!; - - [JsonPropertyName("deny")] - public List<string> Deny { get; set; } // = null!; - - [JsonPropertyName("allow_ip_literals")] - public bool AllowIpLiterals { get; set; } // = false; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/CreationContentBaseType.cs b/MatrixRoomUtils.Core/Responses/CreationContentBaseType.cs new file mode 100644 index 0000000..743c552 --- /dev/null +++ b/MatrixRoomUtils.Core/Responses/CreationContentBaseType.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace MatrixRoomUtils.Core.Responses; + +public class CreationContentBaseType { + private readonly CreateRoomRequest createRoomRequest; + + public CreationContentBaseType(CreateRoomRequest createRoomRequest) => this.createRoomRequest = createRoomRequest; + + [JsonPropertyName("type")] + public string Type { + get => (string)createRoomRequest.CreationContent["type"]; + set { + if (value is "null" or "") createRoomRequest.CreationContent.Remove("type"); + else createRoomRequest.CreationContent["type"] = value; + } + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Responses/LoginResponse.cs b/MatrixRoomUtils.Core/Responses/LoginResponse.cs index 3259e44..8d0d94f 100644 --- a/MatrixRoomUtils.Core/Responses/LoginResponse.cs +++ b/MatrixRoomUtils.Core/Responses/LoginResponse.cs @@ -1,6 +1,7 @@ using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core.Responses; @@ -19,7 +20,7 @@ public class LoginResponse { public async Task<ProfileResponse> GetProfile() { var hc = new HttpClient(); - var resp = await hc.GetAsync($"{HomeServer}/_matrix/client/r0/profile/{UserId}"); + var resp = await hc.GetAsync($"{HomeServer}/_matrix/client/v3/profile/{UserId}"); var data = await resp.Content.ReadFromJsonAsync<JsonElement>(); if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); return data.Deserialize<ProfileResponse>(); diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs index 7b138e0..6e67887 100644 --- a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs +++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core.Responses; @@ -26,7 +27,7 @@ public class StateEventResponse : StateEvent { [JsonPropertyName("prev_content")] public dynamic PrevContent { get; set; } - + public class UnsignedData { [JsonPropertyName("age")] public ulong Age { get; set; } @@ -40,9 +41,4 @@ public class StateEventResponse : StateEvent { [JsonPropertyName("transaction_id")] public string? TransactionId { get; set; } } -} - -public class StateEventResponse<T> : StateEventResponse where T : class { - [JsonPropertyName("content")] - public T Content { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs index 1568746..59c56ed 100644 --- a/MatrixRoomUtils.Core/Room.cs +++ b/MatrixRoomUtils.Core/Room.cs @@ -1,10 +1,8 @@ using System.Net.Http.Json; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using System.Web; using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Responses; using MatrixRoomUtils.Core.RoomTypes; namespace MatrixRoomUtils.Core; @@ -43,7 +41,7 @@ public class Room { } public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") { - var url = $"/_matrix/client/r0/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; + var url = $"/_matrix/client/v3/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; var res = await _httpClient.GetAsync(url); if (!res.IsSuccessStatusCode) { @@ -67,12 +65,14 @@ public class Room { return resn; } - public async Task JoinAsync(string[]? homeservers = null) { - var join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}"; + public async Task JoinAsync(string[]? homeservers = null, string? reason = null) { + var join_url = $"/_matrix/client/v3/join/{HttpUtility.UrlEncode(RoomId)}"; Console.WriteLine($"Calling {join_url} with {homeservers?.Length ?? 0} via's..."); if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] }; var fullJoinUrl = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers); - var res = await _httpClient.PostAsync(fullJoinUrl, null); + var res = await _httpClient.PostAsJsonAsync(fullJoinUrl, new { + reason + }); } public async Task<List<string>> GetMembersAsync(bool joinedOnly = true) { @@ -138,7 +138,7 @@ public class Room { var res = await GetStateAsync("m.room.create"); if (!res.HasValue) return new CreateEvent(); - res.FindExtraJsonFields(typeof(CreateEvent)); + res.FindExtraJsonElementFields(typeof(CreateEvent)); return res.Value.Deserialize<CreateEvent>() ?? new CreateEvent(); } @@ -151,7 +151,7 @@ public class Room { } public async Task ForgetAsync() { - var res = await _httpClient.PostAsync($"/_matrix/client/r0/rooms/{RoomId}/forget", null); + var res = await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); throw new Exception($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); @@ -159,7 +159,9 @@ public class Room { } public async Task LeaveAsync(string? reason = null) { - var res = await _httpClient.PostAsync($"/_matrix/client/r0/rooms/{RoomId}/leave", string.IsNullOrWhiteSpace(reason) ? null : new StringContent($"{{\"reason\":\"{reason}\"}}", Encoding.UTF8, "application/json")); + var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/leave", new { + reason + }); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); throw new Exception($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); @@ -168,7 +170,7 @@ public class Room { public async Task KickAsync(string userId, string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/r0/rooms/{RoomId}/kick", new UserIdAndReason() { user_id = 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}"); @@ -176,7 +178,7 @@ public class Room { } public async Task BanAsync(string userId, string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/r0/rooms/{RoomId}/ban", new UserIdAndReason() { user_id = 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}"); @@ -184,61 +186,31 @@ public class Room { } public async Task UnbanAsync(string userId) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/r0/rooms/{RoomId}/unban", new UserIdAndReason() { user_id = 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}"); + } + 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); + 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>(); + } public readonly SpaceRoom AsSpace; -} - -internal class UserIdAndReason { - public string user_id { get; set; } - public string? reason { get; set; } -} -public class MessagesResponse { - [JsonPropertyName("start")] - public string Start { get; set; } - - [JsonPropertyName("end")] - public string? End { get; set; } - - [JsonPropertyName("chunk")] - public List<StateEventResponse> Chunk { get; set; } = new(); - - [JsonPropertyName("state")] - public List<StateEventResponse> State { get; set; } = new(); -} - -public class CreateEvent { - [JsonPropertyName("creator")] - public string Creator { get; set; } - - [JsonPropertyName("room_version")] - public string RoomVersion { get; set; } - - [JsonPropertyName("type")] - public string? Type { get; set; } - - [JsonPropertyName("predecessor")] - public object? Predecessor { get; set; } - - [JsonPropertyName("m.federate")] - public bool Federate { get; set; } -} - -public class JoinRules { - private const string Public = "public"; - private const string Invite = "invite"; - private const string Knock = "knock"; - - [JsonPropertyName("join_rule")] - public string JoinRule { get; set; } - - [JsonPropertyName("allow")] - public List<string> Allow { get; set; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs index 7f634dc..6eaa73b 100644 --- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs @@ -1,5 +1,6 @@ using System.Text.Json; using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; using MatrixRoomUtils.Core.Responses; namespace MatrixRoomUtils.Core.RoomTypes; @@ -11,10 +12,10 @@ public class SpaceRoom : Room { var rooms = new List<Room>(); var state = await GetStateAsync(""); if (state != null) { - var states = state.Value.Deserialize<StateEventResponse<object>[]>()!; + var states = state.Value.Deserialize<StateEventResponse[]>()!; foreach (var stateEvent in states.Where(x => x.Type == "m.space.child")) { var roomId = stateEvent.StateKey; - if(stateEvent.Content.ToJson() != "{}" || includeRemoved) + if(stateEvent.TypedContent.ToJson() != "{}" || includeRemoved) rooms.Add(await RuntimeCache.CurrentHomeServer.GetRoom(roomId)); } } diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs index db71ee5..7ab3952 100644 --- a/MatrixRoomUtils.Core/RuntimeCache.cs +++ b/MatrixRoomUtils.Core/RuntimeCache.cs @@ -1,5 +1,6 @@ using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core; diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs index 3db4584..0f09a45 100644 --- a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs +++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs @@ -1,5 +1,3 @@ -using MatrixRoomUtils.Core.Attributes; - namespace MatrixRoomUtils.Core.Services; public class HomeserverProviderService { @@ -13,6 +11,6 @@ public class HomeserverProviderService { } public async Task<AuthenticatedHomeServer> GetAuthenticatedWithToken(string homeserver, string accessToken) { - return await new AuthenticatedHomeServer(homeserver, accessToken, _tieredStorageService).Configure(); + return await new AuthenticatedHomeServer(_tieredStorageService, homeserver, accessToken).Configure(); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Services/ServiceInstaller.cs b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs index 43255d8..1b275c5 100644 --- a/MatrixRoomUtils.Core/Services/ServiceInstaller.cs +++ b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs @@ -1,16 +1,27 @@ -using MatrixRoomUtils.Core.Interfaces.Services; using Microsoft.Extensions.DependencyInjection; namespace MatrixRoomUtils.Core.Services; public static class ServiceInstaller { - public static IServiceCollection AddRoryLibMatrixServices(this IServiceCollection services) { + public static IServiceCollection AddRoryLibMatrixServices(this IServiceCollection services, RoryLibMatrixConfiguration? config = null) { + //Check required services if (!services.Any(x => x.ServiceType == typeof(TieredStorageService))) throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!"); + //Add config + if(config != null) + services.AddSingleton(config); + else { + services.AddSingleton(new RoryLibMatrixConfiguration()); + } + //Add services services.AddScoped<HomeserverProviderService>(); return services; } +} + +public class RoryLibMatrixConfiguration { + public string AppName { get; set; } = "Rory&::LibMatrix"; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs index a8c1fac..cb8f0b4 100644 --- a/MatrixRoomUtils.Core/StateEvent.cs +++ b/MatrixRoomUtils.Core/StateEvent.cs @@ -1,12 +1,20 @@ +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; namespace MatrixRoomUtils.Core; public class StateEvent { - [JsonPropertyName("content")] - public dynamic Content { get; set; } = new { }; + public static List<Type> KnownStateEventTypes = + new ClassCollector<IStateEventType>().ResolveFromAllAccessibleAssemblies(); + + public object TypedContent { + get => RawContent.Deserialize(GetType)!; + set => RawContent = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(value)); + } [JsonPropertyName("state_key")] public string StateKey { get; set; } = ""; @@ -17,35 +25,40 @@ public class StateEvent { [JsonPropertyName("replaces_state")] public string? ReplacesState { get; set; } - //extra properties + [JsonPropertyName("content")] + public JsonObject? RawContent { get; set; } + + public T1 GetContent<T1>() where T1 : IStateEventType { + return RawContent.Deserialize<T1>(); + } + [JsonIgnore] - public JsonNode ContentAsJsonNode { - get => JsonSerializer.SerializeToNode(Content); - set => Content = value; + 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); + } + + RawContent.FindExtraJsonObjectFields(type); + + return type; + } } + //debug public string dtype { get { var res = GetType().Name switch { - "StateEvent`1" => $"StateEvent<{Content.GetType().Name}>", + "StateEvent`1" => $"StateEvent", _ => GetType().Name }; return res; } } - public StateEvent<T> As<T>() where T : class => (StateEvent<T>)this; -} - -public class StateEvent<T> : StateEvent where T : class { - public StateEvent() { - //import base content if not an empty object - if (base.Content.GetType() == typeof(T)) { - Console.WriteLine($"StateEvent<{typeof(T)}> created with base content of type {base.Content.GetType()}. Importing base content."); - Content = base.Content; - } - } - - [JsonPropertyName("content")] - public new T Content { get; set; } + public string cdtype => TypedContent.GetType().Name; } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.cs new file mode 100644 index 0000000..2f9502e --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/CanonicalAliasEventData.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.canonical_alias")] +public class CanonicalAliasEventData : IStateEventType { + [JsonPropertyName("alias")] + public string? Alias { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs b/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs new file mode 100644 index 0000000..1727ce9 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/GuestAccessData.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.guest_access")] +public class GuestAccessData : IStateEventType { + [JsonPropertyName("guest_access")] + public string GuestAccess { get; set; } + + public bool IsGuestAccessEnabled { + get => GuestAccess == "can_join"; + set => GuestAccess = value ? "can_join" : "forbidden"; + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs b/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs new file mode 100644 index 0000000..481cc08 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/HistoryVisibilityData.cs @@ -0,0 +1,9 @@ +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.history_visibility")] +public class HistoryVisibilityData : IStateEventType { + public string HistoryVisibility { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs new file mode 100644 index 0000000..acf7777 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/MemberEventData.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.member")] +public class MemberEventData : IStateEventType { + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + [JsonPropertyName("membership")] + public string Membership { get; set; } = null!; + + [JsonPropertyName("displayname")] + public string? Displayname { get; set; } + + [JsonPropertyName("is_direct")] + public bool? IsDirect { get; set; } + + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs new file mode 100644 index 0000000..ad99709 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/MessageEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +[MatrixEvent(EventName = "m.room.message")] +public class MessageEventData : IStateEventType { + [JsonPropertyName("body")] + public string Body { get; set; } + [JsonPropertyName("msgtype")] + public string MessageType { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs index 6f6d082..e67639b 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs @@ -1,8 +1,9 @@ using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core.StateEventTypes; -public class PolicyRuleStateEventData { +public class PolicyRuleStateEventData : IStateEventType { /// <summary> /// Entity this ban applies to, can use * and ? as globs. /// </summary> diff --git a/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs b/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs new file mode 100644 index 0000000..a3e44d1 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/PowerLevelEvent.cs @@ -0,0 +1,43 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.power_levels")] +public class PowerLevelEvent : IStateEventType { + [JsonPropertyName("ban")] + public int Ban { get; set; } // = 50; + + [JsonPropertyName("events_default")] + public int EventsDefault { get; set; } // = 0; + + [JsonPropertyName("events")] + public Dictionary<string, int> Events { get; set; } // = null!; + + [JsonPropertyName("invite")] + public int Invite { get; set; } // = 50; + + [JsonPropertyName("kick")] + public int Kick { get; set; } // = 50; + + [JsonPropertyName("notifications")] + public NotificationsPL NotificationsPl { get; set; } // = null!; + + [JsonPropertyName("redact")] + public int Redact { get; set; } // = 50; + + [JsonPropertyName("state_default")] + public int StateDefault { get; set; } // = 50; + + [JsonPropertyName("users")] + public Dictionary<string, int> Users { get; set; } // = null!; + + [JsonPropertyName("users_default")] + public int UsersDefault { get; set; } // = 0; + + public class NotificationsPL { + [JsonPropertyName("room")] + public int Room { get; set; } = 50; + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs index d835c95..a17b6f9 100644 --- a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs @@ -1,8 +1,11 @@ using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core.StateEventTypes; -public class PresenceStateEventData { +[MatrixEvent(EventName = "m.presence")] +public class PresenceStateEventData : IStateEventType { [JsonPropertyName("presence")] public string Presence { get; set; } [JsonPropertyName("last_active_ago")] diff --git a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs b/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs index db72386..d36ef74 100644 --- a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs +++ b/MatrixRoomUtils.Core/StateEventTypes/ProfileResponse.cs @@ -1,7 +1,9 @@ using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; -namespace MatrixRoomUtils.Core.Responses; +namespace MatrixRoomUtils.Core.StateEventTypes; +[MatrixEvent(EventName = "m.room.member")] public class ProfileResponse { [JsonPropertyName("avatar_url")] public string? AvatarUrl { get; set; } = ""; diff --git a/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs new file mode 100644 index 0000000..03ce16b --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/RoomAvatarEventData.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.avatar")] +public class RoomAvatarEventData : IStateEventType { + [JsonPropertyName("url")] + public string? Url { get; set; } + + [JsonPropertyName("info")] + public RoomAvatarInfo? Info { get; set; } + + public class RoomAvatarInfo { + + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.cs new file mode 100644 index 0000000..72651c8 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/RoomTopicEventData.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.topic")] +public class RoomTopicEventData : IStateEventType { + [JsonPropertyName("topic")] + public string? Topic { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs b/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs new file mode 100644 index 0000000..41bf0a8 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/ServerACLData.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "m.room.server_acl")] +public class ServerACLData : IStateEventType { + [JsonPropertyName("allow")] + public List<string> Allow { get; set; } // = null!; + + [JsonPropertyName("deny")] + public List<string> Deny { get; set; } // = null!; + + [JsonPropertyName("allow_ip_literals")] + public bool AllowIpLiterals { get; set; } // = false; +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/UserIdAndReason.cs b/MatrixRoomUtils.Core/UserIdAndReason.cs new file mode 100644 index 0000000..3801077 --- /dev/null +++ b/MatrixRoomUtils.Core/UserIdAndReason.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace MatrixRoomUtils.Core; + +internal class UserIdAndReason { + [JsonPropertyName("user_id")] + public string UserId { get; set; } + [JsonPropertyName("reason")] + public string? Reason { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs index 878eca4..59ec4db 100644 --- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs +++ b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs @@ -21,7 +21,7 @@ public class LocalStorageWrapper { Console.WriteLine("Access token is not null, creating authenticated home server"); Console.WriteLine($"Homeserver cache: {RuntimeCache.HomeserverResolutionCache.Count} entries"); // Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson()); - RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer, RuntimeCache.LastUsedToken, TODO).Configure(); + RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer, RuntimeCache.LastUsedToken, null).Configure(); Console.WriteLine("Created authenticated home server"); } } diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs index 77c8281..47844e3 100644 --- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs @@ -1,6 +1,7 @@ using System.Text.Json.Nodes; using MatrixRoomUtils.Core; using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Web.Classes.RoomCreationTemplates; @@ -18,9 +19,9 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { history_visibility = "world_readable" } }, - new StateEvent<Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent> { + new StateEvent<GuestAccessData> { Type = "m.room.guest_access", - Content = new Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent { + Content = new GuestAccessData { GuestAccess = "can_join" } }, diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor index da5c172..4e4cec8 100644 --- a/MatrixRoomUtils.Web/Pages/DebugTools.razor +++ b/MatrixRoomUtils.Web/Pages/DebugTools.razor @@ -1,6 +1,5 @@ @page "/Debug" @using System.Reflection -@using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.Interfaces @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor index 0843d5f..cdb5693 100644 --- a/MatrixRoomUtils.Web/Pages/DevOptions.razor +++ b/MatrixRoomUtils.Web/Pages/DevOptions.razor @@ -1,5 +1,4 @@ @page "/DevOptions" -@using MatrixRoomUtils.Core.Extensions @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor index 1fe13bd..858fad9 100644 --- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor +++ b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor @@ -1,5 +1,4 @@ @page "/HSAdmin/RoomQuery" -@using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.Filters @using MatrixRoomUtils.Core.Responses.Admin @using MatrixRoomUtils.Web.Shared.SimpleComponents diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor index 8031146..80dbfd1 100644 --- a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor +++ b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor @@ -1,6 +1,5 @@ @page "/KnownHomeserverList" @using System.Text.Json -@using MatrixRoomUtils.Core.Extensions @using System.Diagnostics @using MatrixRoomUtils.Core.Responses <h3>Known Homeserver List</h3> diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor index 3b78817..9df7fa6 100644 --- a/MatrixRoomUtils.Web/Pages/LoginPage.razor +++ b/MatrixRoomUtils.Web/Pages/LoginPage.razor @@ -52,7 +52,7 @@ var userinfo = new UserInfo { LoginResponse = result }; - userinfo.Profile = await (await new AuthenticatedHomeServer(result.HomeServer, result.AccessToken, TODO).Configure()).GetProfile(result.UserId); + userinfo.Profile = await RuntimeCache.CurrentHomeServer.GetProfile(result.UserId); RuntimeCache.LastUsedToken = result.AccessToken; RuntimeCache.LoginSessions.Add(result.AccessToken, userinfo); diff --git a/MatrixRoomUtils.Web/Pages/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/MediaLocator.razor index 36cd8e6..38d1514 100644 --- a/MatrixRoomUtils.Web/Pages/MediaLocator.razor +++ b/MatrixRoomUtils.Web/Pages/MediaLocator.razor @@ -61,7 +61,7 @@ await sem.WaitAsync(); var httpClient = new HttpClient { BaseAddress = new Uri(hs) }; httpClient.Timeout = TimeSpan.FromSeconds(5); - var rmu = mxcUrl.Replace("mxc://", $"{hs}/_matrix/media/r0/download/"); + var rmu = mxcUrl.Replace("mxc://", $"{hs}/_matrix/media/v3/download/"); try { var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, rmu)); if (res.IsSuccessStatusCode) { diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor index 76b4384..0840ebf 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor @@ -1,5 +1,4 @@ @page "/PolicyListEditor/{RoomId}" -@using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.StateEventTypes @using System.Text.Json @using MatrixRoomUtils.Core.Responses @@ -210,7 +209,7 @@ else { private async Task LoadStatesAsync() { // using var client = new HttpClient(); // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state"); + // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/v3/rooms/{RoomId}/state"); // var Content = await response.Content.ReadAsStringAsync(); // Console.WriteLine(JsonSerializer.Deserialize<object>(Content).ToJson()); // var stateEvents = JsonSerializer.Deserialize<List<StateEventResponse>>(Content); diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor index 20eab7a..8f711b5 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -1,5 +1,4 @@ @page "/PolicyListEditor" -@using MatrixRoomUtils.Core.Extensions @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager <h3>Policy list editor - Room list</h3> diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor index 5cfda77..80d852a 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor @@ -1,9 +1,8 @@ @page "/RoomManagerCreateRoom" -@using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.Responses @using System.Text.Json -@using System.Text.Json.Serialization @using System.Reflection +@using MatrixRoomUtils.Core.StateEventTypes @using MatrixRoomUtils.Web.Classes.RoomCreationTemplates @* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not *@ @using MatrixRoomUtils.Web.Shared.SimpleComponents @@ -53,12 +52,12 @@ <tr> <td style="padding-top: 16px;">History visibility:</td> <td style="padding-top: 16px;"> - <InputSelect @bind-Value="@creationEvent.HistoryVisibility"> - <option value="invited">Invited</option> - <option value="joined">Joined</option> - <option value="shared">Shared</option> - <option value="world_readable">World readable</option> - </InputSelect> + @* <InputSelect @bind-Value="@creationEvent.HistoryVisibility"> *@ + @* <option value="invited">Invited</option> *@ + @* <option value="joined">Joined</option> *@ + @* <option value="shared">Shared</option> *@ + @* <option value="world_readable">World readable</option> *@ + @* </InputSelect> *@ </td> </tr> <tr> @@ -220,15 +219,15 @@ OverwriteWrappedPropertiesFromEvent(); creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - guestAccessEvent = creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content; + guestAccessEvent = creationEvent["m.room.guest_access"].As<GuestAccessData>().Content; Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); - Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}"); - creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content.IsGuestAccessEnabled = true; + Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessData>().ToJson()}"); + creationEvent["m.room.guest_access"].As<GuestAccessData>().Content.IsGuestAccessEnabled = true; Console.WriteLine("-- Created new guest access content --"); Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); - Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}"); - Console.WriteLine($"Creation event casted back: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}"); + Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessData>().ToJson()}"); + Console.WriteLine($"Creation event casted back: {creationEvent["m.room.guest_access"].As<GuestAccessData>().ToJson()}"); StateHasChanged(); } } @@ -236,7 +235,7 @@ private Dictionary<string, string> creationEventValidationErrors { get; set; } = new(); private CreateRoomRequest creationEvent { get; set; } - GuestAccessContent guestAccessEvent { get; set; } + GuestAccessData guestAccessEvent { get; set; } private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new(); @@ -275,7 +274,7 @@ Console.WriteLine("Overwriting wrapped properties"); Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}"); Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}"); - creationEvent.ServerACLs = new ServerACL { + creationEvent.ServerACLs = new ServerACLData { Allow = ServerACLAllowRules, Deny = ServerACLDenyRules, AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals @@ -336,14 +335,5 @@ _ => key }; - public class GuestAccessContent { - [JsonPropertyName("guest_access")] - public string GuestAccess { get; set; } - - public bool IsGuestAccessEnabled { - get => GuestAccess == "can_join"; - set => GuestAccess = value ? "can_join" : "forbidden"; - } - } } diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor index ab650d1..a9c71c4 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor @@ -1,5 +1,4 @@ @page "/RoomManager/Space/{RoomId}" -@using MatrixRoomUtils.Core.Extensions @using System.Text.Json @using MatrixRoomUtils.Core.Responses <h3>Room manager - Viewing Space</h3> diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor index fa5b6a8..296514c 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor @@ -63,7 +63,7 @@ using var client = new HttpClient(); //TODO: can this be improved? client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); + var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/v3/rooms/{RoomId}/state"); // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); var _data = await response.Content.ReadAsStreamAsync(); diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor index bfd4d10..ff1d9ac 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor @@ -1,5 +1,4 @@ @page "/RoomStateViewer/{RoomId}" -@using MatrixRoomUtils.Core.Extensions @using System.Net.Http.Headers @using System.Text.Json @inject ILocalStorageService LocalStorage @@ -88,7 +87,7 @@ //TODO: can we improve this? using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); + var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/v3/rooms/{RoomId}/state"); // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); var _data = await response.Content.ReadAsStreamAsync(); diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor index 8ab44fb..975da43 100644 --- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor +++ b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor @@ -22,12 +22,13 @@ private int _roomCount { get; set; } = 0; protected override async Task OnInitializedAsync() { - var hs = await new AuthenticatedHomeServer(User.LoginResponse.HomeServer, User.AccessToken, TODO).Configure(); + await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + if (User.Profile.AvatarUrl != null && User.Profile.AvatarUrl != "") - _avatarUrl = hs.ResolveMediaUri(User.Profile.AvatarUrl); + _avatarUrl = RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.Profile.AvatarUrl); else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId; try { - _roomCount = (await hs.GetJoinedRooms()).Count; + _roomCount = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Count; } catch { _roomCount = -1; diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor index 43a4111..0a58b4d 100644 --- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor +++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor @@ -36,13 +36,11 @@ await _semaphoreSlim.WaitAsync(); - var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.HomeServerDomain, RuntimeCache.CurrentHomeServer.AccessToken, TODO).Configure(); - if (User == null) { if (UserId == null) { throw new ArgumentNullException(nameof(UserId)); } - User = await hs.GetProfile(UserId); + User = await RuntimeCache.CurrentHomeServer.GetProfile(UserId); } // UserId = User.; diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor index 42a5f64..ea7d2ec 100644 --- a/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor +++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor @@ -1,4 +1,3 @@ -@using MatrixRoomUtils.Core.Extensions <table> @foreach (var i in Items.Keys) { var key = i; diff --git a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor index 4fb5596..f89dc44 100644 --- a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor +++ b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor @@ -1,22 +1,21 @@ -@using MatrixRoomUtils.Core.Extensions -@if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "ban") { +@if (Event.membership"]!.GetValue<string>() == "ban") { <i>@Event.StateKey was banned</i> } -else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "invite") { +else if (Event.membership"]!.GetValue<string>() == "invite") { <i>@Event.StateKey was invited</i> } -else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "join") { +else if (Event.membership"]!.GetValue<string>() == "join") { @if (Event.ReplacesState != null) { - <i>@Event.StateKey changed their display name to @(Event.ContentAsJsonNode["displayname"]!.GetValue<string>())</i> + <i>@Event.StateKey changed their display name to @(Event.displayname"]!.GetValue<string>())</i> } else { <i><InlineUserItem UserId="@Event.StateKey"></InlineUserItem> joined</i> } } -else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "leave") { +else if (Event.membership"]!.GetValue<string>() == "leave") { <i>@Event.StateKey left</i> } -else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "knock") { +else if (Event.membership"]!.GetValue<string>() == "knock") { <i>@Event.StateKey knocked</i> } else { diff --git a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor index 80a432b..6c26dc2 100644 --- a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor +++ b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor @@ -1,4 +1,3 @@ -@using MatrixRoomUtils.Core.Extensions @using MatrixRoomUtils.Core.Responses <pre> @ObjectExtensions.ToJson(Event.Content, indent: false) diff --git a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineUnknownItem.razor b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineUnknownItem.razor index f78bdc9..d8ea7e2 100644 --- a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineUnknownItem.razor +++ b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineUnknownItem.razor @@ -1,4 +1,3 @@ -@using MatrixRoomUtils.Core.Extensions <div> <details style="display: inline;"> diff --git a/MatrixRoomUtils.Web/Shared/UserListItem.razor b/MatrixRoomUtils.Web/Shared/UserListItem.razor index b99671a..52f398a 100644 --- a/MatrixRoomUtils.Web/Shared/UserListItem.razor +++ b/MatrixRoomUtils.Web/Shared/UserListItem.razor @@ -25,7 +25,7 @@ private string? profileAvatar { get; set; } = "/icon-192.png"; private string? profileName { get; set; } = "Loading..."; - private static SemaphoreSlim _semaphoreSlim = new(128); + private static SemaphoreSlim _semaphoreSlim = new(8); protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); @@ -33,13 +33,11 @@ await _semaphoreSlim.WaitAsync(); - var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.HomeServerDomain, RuntimeCache.CurrentHomeServer.AccessToken, TODO).Configure(); - if (User == null) { if (UserId == null) { throw new ArgumentNullException(nameof(UserId)); } - User = await hs.GetProfile(UserId); + User = await RuntimeCache.CurrentHomeServer.GetProfile(UserId); } // UserId = User.; diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css index cb7ba63..b3a8cf3 100644 --- a/MatrixRoomUtils.Web/wwwroot/css/app.css +++ b/MatrixRoomUtils.Web/wwwroot/css/app.css @@ -74,7 +74,7 @@ a, .btn-link { } .blazor-error-boundary { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + background: url(data:image/svg+xml;base64,PHN2ZyB3aWv3aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; padding: 1rem 1rem 1rem 3.7rem; color: white; } diff --git a/MatrixRoomUtils.sln b/MatrixRoomUtils.sln index a29cf89..a29cf89 100644..100755 --- a/MatrixRoomUtils.sln +++ b/MatrixRoomUtils.sln diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user index 033da4f..93b1fca 100644 --- a/MatrixRoomUtils.sln.DotSettings.user +++ b/MatrixRoomUtils.sln.DotSettings.user @@ -1,7 +1,15 @@ <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> - <s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String> + <s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">DoNotShowAndRun</s:String> + <s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsBuildSolutionLoadingNodeCount/@EntryValue">12</s:Int64> + <s:Boolean x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsBuildSolutionLoadingOrderingEnabled/@EntryValue">True</s:Boolean> + <s:String x:Key="/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue">No</s:String> + <s:Int64 x:Key="/Default/Environment/Hierarchy/Build/SolutionBuilderNext/ParallelProcessesCount2/@EntryValue">12</s:Int64> + + <s:Boolean x:Key="/Default/Environment/Hierarchy/Build/SolutionBuilderNext/ShouldRestoreNugetPackages/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=244e90fe_002Dee26_002D4f78_002D86eb_002D27529ae48905_0023MatrixRoomUtils/@EntryIndexedValue">True</s:Boolean> <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> @@ -10,4 +18,10 @@ + + + + + + </wpf:ResourceDictionary> \ No newline at end of file |