about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--MatrixRoomUtils.Bot/FileStorageProvider.cs31
-rw-r--r--MatrixRoomUtils.Bot/MRUBot.cs60
-rw-r--r--MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj4
-rw-r--r--MatrixRoomUtils.Bot/Program.cs24
-rw-r--r--MatrixRoomUtils.Core/AuthenticatedHomeServer.cs11
-rw-r--r--MatrixRoomUtils.Core/Helpers/SyncHelper.cs121
-rw-r--r--MatrixRoomUtils.Core/Interfaces/IHomeServer.cs2
-rw-r--r--MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs39
-rw-r--r--MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj6
-rw-r--r--MatrixRoomUtils.Core/Room.cs49
-rw-r--r--MatrixRoomUtils.Core/Services/HomeserverProviderService.cs18
-rw-r--r--MatrixRoomUtils.Core/Services/HomeserverService.cs8
-rw-r--r--MatrixRoomUtils.Core/Services/ServiceInstaller.cs16
-rw-r--r--MatrixRoomUtils.Core/Services/TieredStorageService.cs6
-rw-r--r--MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs14
-rw-r--r--MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj6
-rw-r--r--MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs17
-rw-r--r--MatrixRoomUtils.Web/Classes/SessionStorageProviderService.cs12
-rw-r--r--MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj4
-rw-r--r--MatrixRoomUtils.Web/Pages/LoginPage.razor2
-rw-r--r--MatrixRoomUtils.Web/Program.cs8
-rw-r--r--MatrixRoomUtils.Web/SessionStorageProviderService.cs5
-rw-r--r--MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor2
-rw-r--r--MatrixRoomUtils.Web/Shared/InlineUserItem.razor2
-rw-r--r--MatrixRoomUtils.Web/Shared/UserListItem.razor2
-rw-r--r--MatrixRoomUtils.sln.DotSettings.user3
26 files changed, 423 insertions, 49 deletions
diff --git a/MatrixRoomUtils.Bot/FileStorageProvider.cs b/MatrixRoomUtils.Bot/FileStorageProvider.cs
new file mode 100644
index 0000000..8d99828
--- /dev/null
+++ b/MatrixRoomUtils.Bot/FileStorageProvider.cs
@@ -0,0 +1,31 @@
+using System.Text.Json;
+using MatrixRoomUtils.Core.Extensions;
+using MatrixRoomUtils.Core.Interfaces.Services;
+
+namespace MatrixRoomUtils.Bot; 
+
+public class FileStorageProvider : IStorageProvider {
+    public string TargetPath { get; }
+
+    /// <summary>
+    /// Creates a new instance of <see cref="FileStorageProvider" />.
+    /// </summary>
+    /// <param name="targetPath"></param>
+    public FileStorageProvider(string targetPath) {
+        Console.WriteLine($"Initialised FileStorageProvider with path {targetPath}");
+        TargetPath = targetPath;
+        if(!Directory.Exists(targetPath)) {
+            Directory.CreateDirectory(targetPath);
+        }
+    }
+
+    public async Task SaveObject<T>(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), ObjectExtensions.ToJson(value));
+
+    public async Task<T?> LoadObject<T>(string key) => JsonSerializer.Deserialize<T>(await File.ReadAllTextAsync(Path.Join(TargetPath, key)));
+
+    public async Task<bool> ObjectExists(string key) => File.Exists(Path.Join(TargetPath, key));
+
+    public async Task<List<string>> GetAllKeys() => Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList();
+
+    public async Task DeleteObject(string key) => File.Delete(Path.Join(TargetPath, key));
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Bot/MRUBot.cs b/MatrixRoomUtils.Bot/MRUBot.cs
new file mode 100644
index 0000000..9b2a395
--- /dev/null
+++ b/MatrixRoomUtils.Bot/MRUBot.cs
@@ -0,0 +1,60 @@
+using System.CodeDom.Compiler;
+using System.Diagnostics.CodeAnalysis;
+using MatrixRoomUtils.Core;
+using MatrixRoomUtils.Core.Extensions;
+using MatrixRoomUtils.Core.Helpers;
+using MatrixRoomUtils.Core.Responses;
+using MatrixRoomUtils.Core.Services;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+public class MRUBot : IHostedService {
+    private readonly HomeserverProviderService _homeserverProviderService;
+    private readonly ILogger<MRUBot> _logger;
+
+    public MRUBot(HomeserverProviderService homeserverProviderService, ILogger<MRUBot> logger) {
+        Console.WriteLine("MRUBot hosted service instantiated!");
+        _homeserverProviderService = homeserverProviderService;
+        _logger = logger;
+    }
+
+    /// <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");
+        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);
+        
+    }
+
+    /// <summary>Triggered when the application host is performing a graceful shutdown.</summary>
+    /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
+    public async Task StopAsync(CancellationToken cancellationToken) {
+        Console.WriteLine("Shutting down bot!");
+    }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
index 8ff14c4..095d0f6 100644
--- a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
+++ b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
@@ -6,7 +6,7 @@
     <LangVersion>preview</LangVersion>

     <ImplicitUsings>enable</ImplicitUsings>

     <Nullable>enable</Nullable>

-    <PublishAot>true</PublishAot>

+    <PublishAot>false</PublishAot>

     <InvariantGlobalization>true</InvariantGlobalization>

     <PublishTrimmed>true</PublishTrimmed>

     <PublishReadyToRun>true</PublishReadyToRun>

@@ -21,7 +21,7 @@
   </ItemGroup>

   

   <ItemGroup>

-    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />

+    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.5.23280.8" />

   </ItemGroup>

 

 </Project>

diff --git a/MatrixRoomUtils.Bot/Program.cs b/MatrixRoomUtils.Bot/Program.cs
index 83fa4f4..441003e 100644
--- a/MatrixRoomUtils.Bot/Program.cs
+++ b/MatrixRoomUtils.Bot/Program.cs
@@ -1,2 +1,26 @@
 // See https://aka.ms/new-console-template for more information

+

+using MatrixRoomUtils.Bot;

+using MatrixRoomUtils.Core.Services;

+using Microsoft.Extensions.DependencyInjection;

+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();

+

+

+await host.RunAsync();
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
index 7e5650f..74e85b1 100644
--- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
+++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
@@ -4,24 +4,29 @@ using System.Text.Json;
 using System.Text.Json.Nodes;
 using MatrixRoomUtils.Core.Extensions;
 using MatrixRoomUtils.Core.Filters;
+using MatrixRoomUtils.Core.Helpers;
 using MatrixRoomUtils.Core.Interfaces;
 using MatrixRoomUtils.Core.Responses;
 using MatrixRoomUtils.Core.Responses.Admin;
+using MatrixRoomUtils.Core.Services;
 
 namespace MatrixRoomUtils.Core;
 
 public class AuthenticatedHomeServer : IHomeServer {
+    private readonly TieredStorageService _storage;
     public readonly HomeserverAdminApi Admin;
+    public readonly SyncHelper SyncHelper;
 
-    public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain) {
-        UserId = userId;
+    public AuthenticatedHomeServer(string canonicalHomeServerDomain, string accessToken, TieredStorageService storage) {
+        _storage = storage;
         AccessToken = accessToken;
         HomeServerDomain = canonicalHomeServerDomain;
         Admin = new HomeserverAdminApi(this);
+        SyncHelper = new SyncHelper(this, storage);
         _httpClient = new MatrixHttpClient();
     }
 
-    public string UserId { get; set; }
+    public string UserId { get; }
     public string AccessToken { get; set; }
 
     public async Task<AuthenticatedHomeServer> Configure() {
diff --git a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs
new file mode 100644
index 0000000..edbb646
--- /dev/null
+++ b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs
@@ -0,0 +1,121 @@
+using System.Net.Http.Json;
+using System.Text.Json.Serialization;
+using MatrixRoomUtils.Core.Responses;
+using MatrixRoomUtils.Core.Services;
+using MatrixRoomUtils.Core.StateEventTypes;
+
+namespace MatrixRoomUtils.Core.Helpers;
+
+public class SyncHelper {
+    private readonly AuthenticatedHomeServer _homeServer;
+    private readonly TieredStorageService _storageService;
+
+    public SyncHelper(AuthenticatedHomeServer homeServer, TieredStorageService storageService) {
+        _homeServer = homeServer;
+        _storageService = storageService;
+    }
+
+    public async Task<SyncResult?> Sync(string? since = null) {
+        var outFileName = "sync-" +
+                          (await _storageService.CacheStorageProvider.GetAllKeys()).Count(x => x.StartsWith("sync")) +
+                          ".json";
+        var url = "/_matrix/client/v3/sync?timeout=30000&set_presence=online";
+        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;
+    }
+    
+    public event EventHandler<SyncResult>? ;
+}
+
+public class SyncResult {
+    [JsonPropertyName("next_batch")]
+    public string NextBatch { get; set; }
+
+    [JsonPropertyName("account_data")]
+    public EventList AccountData { get; set; }
+
+    [JsonPropertyName("presence")]
+    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; }
+
+    // supporting classes
+    public class PresenceDataStructure {
+        [JsonPropertyName("events")]
+        public List<StateEventResponse<PresenceStateEventData>> Events { get; set; }
+    }
+
+    public class RoomsDataStructure {
+        [JsonPropertyName("join")]
+        public Dictionary<string, JoinedRoomDataStructure> Join { get; set; }
+
+        [JsonPropertyName("invite")]
+        public Dictionary<string, InvitedRoomDataStructure> Invite { get; set; }
+        
+        public class JoinedRoomDataStructure {
+            [JsonPropertyName("timeline")]
+            public TimelineDataStructure Timeline { get; set; }
+
+            [JsonPropertyName("state")]
+            public EventList State { get; set; }
+
+            [JsonPropertyName("account_data")]
+            public EventList AccountData { get; set; }
+
+            [JsonPropertyName("ephemeral")]
+            public EventList Ephemeral { get; set; }
+
+            [JsonPropertyName("unread_notifications")]
+            public UnreadNotificationsDataStructure UnreadNotifications { get; set; }
+            
+            [JsonPropertyName("summary")]
+            public SummaryDataStructure Summary { get; set; }
+
+            public class TimelineDataStructure {
+                [JsonPropertyName("events")]
+                public List<StateEventResponse> Events { get; set; }
+
+                [JsonPropertyName("prev_batch")]
+                public string PrevBatch { get; set; }
+
+                [JsonPropertyName("limited")]
+                public bool Limited { get; set; }
+            }
+
+            public class UnreadNotificationsDataStructure {
+                [JsonPropertyName("notification_count")]
+                public int NotificationCount { get; set; }
+
+                [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; }
+            }
+        }
+
+        public class InvitedRoomDataStructure {
+            [JsonPropertyName("invite_state")]
+            public EventList InviteState { get; set; }
+        }
+    }
+}
+
+public class EventList {
+    [JsonPropertyName("events")]
+    public List<StateEventResponse> Events { get; set; }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
index a808c3d..c5645e6 100644
--- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
+++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
@@ -10,7 +10,7 @@ public class IHomeServer {
     public string HomeServerDomain { get; set; }
     public string FullHomeServerDomain { get; set; }
 
-    private protected MatrixHttpClient _httpClient { get; set; } = new();
+    protected internal MatrixHttpClient _httpClient { get; set; } = new();
 
     public async Task<string> ResolveHomeserverFromWellKnown(string homeserver) {
         var res = await _resolveHomeserverFromWellKnown(homeserver);
diff --git a/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs b/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs
index 2540ad7..01314da 100644
--- a/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs
+++ b/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs
@@ -1,17 +1,46 @@
 namespace MatrixRoomUtils.Core.Interfaces.Services; 
 
 public interface IStorageProvider {
-    // save 
-    public async Task SaveAll() {
-        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement Save()!");
+    // save all children of a type with reflection
+    public Task SaveAllChildren<T>(string key, T value) {
+        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement SaveAllChildren<T>(key, value)!");
+        return Task.CompletedTask;
     }
+    
+    // load all children of a type with reflection
+    public Task<T?> LoadAllChildren<T>(string key) {
+        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement LoadAllChildren<T>(key)!");
+        return Task.FromResult(default(T));
+    }
+
 
-    public async Task SaveObject<T>(string key, T value) {
+    public Task SaveObject<T>(string key, T value) {
         Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement SaveObject<T>(key, value)!");
+        return Task.CompletedTask;
+    }
+    
+    // load
+    public Task<T?> LoadObject<T>(string key) {
+        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement LoadObject<T>(key)!");
+        return Task.FromResult(default(T));
+    }
+    
+    // check if exists
+    public Task<bool> ObjectExists(string key) {
+        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement ObjectExists(key)!");
+        return Task.FromResult(false);
+    }
+    
+    // get all keys
+    public Task<List<string>> GetAllKeys() {
+        Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement GetAllKeys()!");
+        return Task.FromResult(new List<string>());
     }
+    
 
     // delete
-    public async Task DeleteObject(string key) {
+    public Task DeleteObject(string key) {
         Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement DeleteObject(key)!");
+        return Task.CompletedTask;
     }
 }
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj
index 6836c68..5ca40bd 100644
--- a/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj
+++ b/MatrixRoomUtils.Core/MatrixRoomUtils.Core.csproj
@@ -6,4 +6,10 @@
         <Nullable>enable</Nullable>
     </PropertyGroup>
 
+    <ItemGroup>
+      <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>
+    </ItemGroup>
+
 </Project>
diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs
index 4f6bbca..1568746 100644
--- a/MatrixRoomUtils.Core/Room.cs
+++ b/MatrixRoomUtils.Core/Room.cs
@@ -1,4 +1,5 @@
 using System.Net.Http.Json;
+using System.Text;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Web;
@@ -148,11 +149,57 @@ 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/r0/rooms/{RoomId}/forget", null);
+        if (!res.IsSuccessStatusCode) {
+            Console.WriteLine($"Failed to forget room {RoomId} - got status: {res.StatusCode}");
+            throw new Exception($"Failed to forget room {RoomId} - got status: {res.StatusCode}");
+        }
+    }
+    
+    public async Task LeaveAsync(string? reason = null) {
+        var res = await _httpClient.PostAsync($"/_matrix/client/r0/rooms/{RoomId}/leave", string.IsNullOrWhiteSpace(reason) ? null : new StringContent($"{{\"reason\":\"{reason}\"}}", Encoding.UTF8, "application/json"));
+        if (!res.IsSuccessStatusCode) {
+            Console.WriteLine($"Failed to leave room {RoomId} - got status: {res.StatusCode}");
+            throw new Exception($"Failed to leave room {RoomId} - got status: {res.StatusCode}");
+        }
+    }
+    
+    public async Task KickAsync(string userId, string? reason = null) {
+       
+        var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/r0/rooms/{RoomId}/kick", new UserIdAndReason() { user_id = 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/r0/rooms/{RoomId}/ban", new UserIdAndReason() { user_id = 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/r0/rooms/{RoomId}/unban", new UserIdAndReason() { user_id = 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 SpaceRoom AsSpace;
+    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; }
diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
new file mode 100644
index 0000000..3db4584
--- /dev/null
+++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
@@ -0,0 +1,18 @@
+using MatrixRoomUtils.Core.Attributes;
+
+namespace MatrixRoomUtils.Core.Services;
+
+public class HomeserverProviderService {
+    private readonly TieredStorageService _tieredStorageService;
+
+    public HomeserverProviderService(TieredStorageService tieredStorageService) {
+        Console.WriteLine("Homeserver provider service instantiated!");
+        _tieredStorageService = tieredStorageService;
+        Console.WriteLine(
+            $"New HomeserverProviderService created with TieredStorageService<{string.Join(", ", tieredStorageService.GetType().GetProperties().Select(x => x.Name))}>!");
+    }
+
+    public async Task<AuthenticatedHomeServer> GetAuthenticatedWithToken(string homeserver, string accessToken) {
+        return await new AuthenticatedHomeServer(homeserver, accessToken, _tieredStorageService).Configure();
+    }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Services/HomeserverService.cs b/MatrixRoomUtils.Core/Services/HomeserverService.cs
deleted file mode 100644
index ba48e6c..0000000
--- a/MatrixRoomUtils.Core/Services/HomeserverService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using MatrixRoomUtils.Core.Attributes;
-
-namespace MatrixRoomUtils.Core.Services; 
-
-[Trace]
-public class HomeserverService {
-    
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Services/ServiceInstaller.cs b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs
new file mode 100644
index 0000000..43255d8
--- /dev/null
+++ b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs
@@ -0,0 +1,16 @@
+using MatrixRoomUtils.Core.Interfaces.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace MatrixRoomUtils.Core.Services; 
+
+public static class ServiceInstaller {
+    
+    public static IServiceCollection AddRoryLibMatrixServices(this IServiceCollection services) {
+        if (!services.Any(x => x.ServiceType == typeof(TieredStorageService)))
+            throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!");
+        services.AddScoped<HomeserverProviderService>();
+        return services;
+    }
+    
+    
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Services/TieredStorageService.cs b/MatrixRoomUtils.Core/Services/TieredStorageService.cs
index f6beddd..2f27443 100644
--- a/MatrixRoomUtils.Core/Services/TieredStorageService.cs
+++ b/MatrixRoomUtils.Core/Services/TieredStorageService.cs
@@ -3,7 +3,11 @@ using MatrixRoomUtils.Core.Interfaces.Services;
 namespace MatrixRoomUtils.Core.Services; 
 
 public class TieredStorageService {
+    public IStorageProvider CacheStorageProvider { get; }
+    public IStorageProvider DataStorageProvider { get; }
+
     public TieredStorageService(IStorageProvider cacheStorageProvider, IStorageProvider dataStorageProvider) {
-        
+        CacheStorageProvider = cacheStorageProvider;
+        DataStorageProvider = dataStorageProvider;
     }
 }
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs
new file mode 100644
index 0000000..d835c95
--- /dev/null
+++ b/MatrixRoomUtils.Core/StateEventTypes/PresenceStateEventData.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace MatrixRoomUtils.Core.StateEventTypes; 
+
+public class PresenceStateEventData {
+    [JsonPropertyName("presence")]
+    public string Presence { get; set; }
+    [JsonPropertyName("last_active_ago")]
+    public long LastActiveAgo { get; set; }
+    [JsonPropertyName("currently_active")]
+    public bool CurrentlyActive { get; set; }
+    [JsonPropertyName("status_msg")]
+    public string StatusMessage { get; set; }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
index a34cd2c..a647a2f 100644
--- a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
+++ b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
@@ -7,15 +7,15 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.3"/>
+        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.7" />
     </ItemGroup>
 
     <ItemGroup>
-        <ProjectReference Include="..\MatrixRoomUtils.Web\MatrixRoomUtils.Web.csproj"/>
+        <ProjectReference Include="..\MatrixRoomUtils.Web\MatrixRoomUtils.Web.csproj" />
     </ItemGroup>
 
     <ItemGroup>
-        <Folder Include="Controllers"/>
+        <Folder Include="Controllers" />
     </ItemGroup>
 
 
diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
index b7da78a..878eca4 100644
--- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
+++ b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
@@ -10,19 +10,18 @@ public class LocalStorageWrapper {
     //some basic logic
     public static async Task InitialiseRuntimeVariables(ILocalStorageService localStorage) {
         //RuntimeCache stuff
-        async Task Save() => await SaveToLocalStorage(localStorage);
-        async Task SaveObject(string key, object obj) => await localStorage.SetItemAsync(key, obj);
-        async Task RemoveObject(string key) => await localStorage.RemoveItemAsync(key);
-        
-        RuntimeCache.Save = Save;
-        RuntimeCache.SaveObject = SaveObject;
-        RuntimeCache.RemoveObject = RemoveObject;
+        // async Task Save() => await SaveToLocalStorage(localStorage);
+        // async Task SaveObject(string key, object obj) => await localStorage.SetItemAsync(key, obj);
+        // async Task RemoveObject(string key) => await localStorage.RemoveItemAsync(key);
+        //
+        // RuntimeCache.Save = Save;
+        // RuntimeCache.SaveObject = SaveObject;
+        // RuntimeCache.RemoveObject = RemoveObject;
         if (RuntimeCache.LastUsedToken != null) {
             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.UserId, RuntimeCache.LastUsedToken,
-                RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer).Configure();
+            RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer, RuntimeCache.LastUsedToken, TODO).Configure();
             Console.WriteLine("Created authenticated home server");
         }
     }
diff --git a/MatrixRoomUtils.Web/Classes/SessionStorageProviderService.cs b/MatrixRoomUtils.Web/Classes/SessionStorageProviderService.cs
new file mode 100644
index 0000000..4a9c7d1
--- /dev/null
+++ b/MatrixRoomUtils.Web/Classes/SessionStorageProviderService.cs
@@ -0,0 +1,12 @@
+using Blazored.SessionStorage;
+using MatrixRoomUtils.Core.Interfaces.Services;
+
+namespace MatrixRoomUtils.Web; 
+
+public class SessionStorageProviderService : IStorageProvider {
+    private readonly ISessionStorageService _sessionStorage;
+
+    public SessionStorageProviderService(ISessionStorageService sessionStorage) {
+        _sessionStorage = sessionStorage;
+    }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
index 33fb062..4bb7aeb 100644
--- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
+++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
@@ -9,8 +9,8 @@
     <ItemGroup>
         <PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
         <PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
-        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.3" />
-        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.3" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.7" />
+        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.7" PrivateAssets="all" />
     </ItemGroup>
 
     <ItemGroup>
diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor
index 16fdd24..3b78817 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.UserId, result.AccessToken, result.HomeServer).Configure()).GetProfile(result.UserId);
+            userinfo.Profile = await (await new AuthenticatedHomeServer(result.HomeServer, result.AccessToken, TODO).Configure()).GetProfile(result.UserId);
             RuntimeCache.LastUsedToken = result.AccessToken;
 
             RuntimeCache.LoginSessions.Add(result.AccessToken, userinfo);
diff --git a/MatrixRoomUtils.Web/Program.cs b/MatrixRoomUtils.Web/Program.cs
index 11e7f19..b106543 100644
--- a/MatrixRoomUtils.Web/Program.cs
+++ b/MatrixRoomUtils.Web/Program.cs
@@ -32,12 +32,10 @@ builder.Services.AddBlazoredSessionStorage(config => {
     config.JsonSerializerOptions.WriteIndented = false;
 });
 
-builder.Services.AddScoped<LocalStorageProviderService>();
-builder.Services.AddSingleton<SessionStorageProviderService>();
-builder.Services.AddSingleton<TieredStorageService>(x =>
+builder.Services.AddScoped<TieredStorageService>(x =>
     new(
-        x.GetRequiredService<SessionStorageProviderService>(),
-        x.GetRequiredService<LocalStorageProviderService>()
+        cacheStorageProvider: new SessionStorageProviderService(x.GetRequiredService<ISessionStorageService>()),
+        dataStorageProvider: new LocalStorageProviderService(x.GetRequiredService<ILocalStorageService>())
     )
 );
 
diff --git a/MatrixRoomUtils.Web/SessionStorageProviderService.cs b/MatrixRoomUtils.Web/SessionStorageProviderService.cs
deleted file mode 100644
index d6bffe6..0000000
--- a/MatrixRoomUtils.Web/SessionStorageProviderService.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using MatrixRoomUtils.Core.Interfaces.Services;
-
-namespace MatrixRoomUtils.Web; 
-
-public class SessionStorageProviderService : IStorageProvider { }
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
index 1fc70f2..8ab44fb 100644
--- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
@@ -22,7 +22,7 @@
     private int _roomCount { get; set; } = 0;
 
     protected override async Task OnInitializedAsync() {
-        var hs = await new AuthenticatedHomeServer(User.LoginResponse.UserId, User.AccessToken, User.LoginResponse.HomeServer).Configure();
+        var hs = await new AuthenticatedHomeServer(User.LoginResponse.HomeServer, User.AccessToken, TODO).Configure();
         if (User.Profile.AvatarUrl != null && User.Profile.AvatarUrl != "")
             _avatarUrl = hs.ResolveMediaUri(User.Profile.AvatarUrl);
         else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId;
diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
index 9833c62..43a4111 100644
--- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
@@ -36,7 +36,7 @@
 
         await _semaphoreSlim.WaitAsync();
 
-        var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure();
+        var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.HomeServerDomain, RuntimeCache.CurrentHomeServer.AccessToken, TODO).Configure();
 
         if (User == null) {
             if (UserId == null) {
diff --git a/MatrixRoomUtils.Web/Shared/UserListItem.razor b/MatrixRoomUtils.Web/Shared/UserListItem.razor
index 43c71ab..b99671a 100644
--- a/MatrixRoomUtils.Web/Shared/UserListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/UserListItem.razor
@@ -33,7 +33,7 @@
 
         await _semaphoreSlim.WaitAsync();
 
-        var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure();
+        var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.HomeServerDomain, RuntimeCache.CurrentHomeServer.AccessToken, TODO).Configure();
 
         if (User == null) {
             if (UserId == null) {
diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user
index dcd4e3e..033da4f 100644
--- a/MatrixRoomUtils.sln.DotSettings.user
+++ b/MatrixRoomUtils.sln.DotSettings.user
@@ -1,5 +1,8 @@
 <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: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>