about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-02-18 07:40:41 +0100
committerRory& <root@rory.gay>2025-02-18 07:40:41 +0100
commit8673cb236f427ba6af6382e3b5702a134f1afe2e (patch)
tree91cf9a092c47e4041d2e70fe01582184ebc90897
parentChange defaults of GetManyMessages (diff)
downloadLibMatrix-8673cb236f427ba6af6382e3b5702a134f1afe2e.tar.xz
MxcUri class, other various work
m---------ArcaneLibs0
-rw-r--r--LibMatrix.EventTypes/LibMatrix.EventTypes.csproj2
-rw-r--r--LibMatrix.EventTypes/Spec/State/RoomInfo/RoomJoinRulesEventContent.cs2
-rw-r--r--LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs6
-rw-r--r--LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs4
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs61
-rw-r--r--LibMatrix/Homeservers/FederationClient.cs4
-rw-r--r--LibMatrix/Homeservers/RemoteHomeServer.cs14
-rw-r--r--LibMatrix/Homeservers/UserInteractiveAuthClient.cs12
-rw-r--r--LibMatrix/LibMatrix.csproj2
-rw-r--r--LibMatrix/MxcUri.cs43
-rw-r--r--LibMatrix/Responses/CreateRoomRequest.cs2
-rw-r--r--LibMatrix/Responses/LoginResponse.cs6
-rw-r--r--LibMatrix/Responses/SyncResponse.cs4
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs2
-rw-r--r--Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs2
-rw-r--r--Utilities/LibMatrix.DevTestBot/Bot/Commands/PingCommand.cs13
-rw-r--r--Utilities/LibMatrix.DevTestBot/Bot/PingTestBot.cs125
-rw-r--r--Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj2
-rw-r--r--Utilities/LibMatrix.DevTestBot/Program.cs2
-rw-r--r--Utilities/LibMatrix.DevTestBot/appsettings.json4
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs18
22 files changed, 260 insertions, 70 deletions
diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject 174c07bba99f268bafcb15153e26ad63980d729
+Subproject 21040e61a1a2876092830af82ec67b8d4a8ac73
diff --git a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj b/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj

index b8c4132..ae2f4de 100644 --- a/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj +++ b/LibMatrix.EventTypes/LibMatrix.EventTypes.csproj
@@ -7,7 +7,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250123-182609" Condition="'$(Configuration)' == 'Release'"/> + <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250208-191806" Condition="'$(Configuration)' == 'Release'" /> <ProjectReference Include="..\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj" Condition="'$(Configuration)' == 'Debug'"/> </ItemGroup> diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomJoinRulesEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomJoinRulesEventContent.cs
index a008c37..03d994d 100644 --- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomJoinRulesEventContent.cs +++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomJoinRulesEventContent.cs
@@ -12,7 +12,7 @@ public class RoomJoinRulesEventContent : EventContent { /// unknown values are treated as "private" /// </summary> [JsonPropertyName("join_rule")] - public string JoinRuleValue { get; set; } = null!; + public string JoinRuleValue { get; set; } [JsonIgnore] public JoinRules JoinRule { diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs
index c9aa603..22fa3b7 100644 --- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs +++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs
@@ -20,7 +20,7 @@ public class RoomPowerLevelEventContent : EventContent { public long? Kick { get; set; } = 50; [JsonPropertyName("notifications")] - public NotificationsPowerLevels? NotificationsPl { get; set; } // = null!; + public NotificationsPowerLevels? NotificationsPl { get; set; } [JsonPropertyName("redact")] public long? Redact { get; set; } = 50; @@ -29,10 +29,10 @@ public class RoomPowerLevelEventContent : EventContent { public long? StateDefault { get; set; } = 50; [JsonPropertyName("events")] - public Dictionary<string, long>? Events { get; set; } // = null!; + public Dictionary<string, long>? Events { get; set; } [JsonPropertyName("users")] - public Dictionary<string, long>? Users { get; set; } // = null!; + public Dictionary<string, long>? Users { get; set; } [JsonPropertyName("users_default")] public long? UsersDefault { get; set; } = 0; diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs
index e3fc892..c492250 100644 --- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs +++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs
@@ -7,10 +7,10 @@ public class RoomServerAclEventContent : EventContent { public const string EventId = "m.room.server_acl"; [JsonPropertyName("allow")] - public List<string>? Allow { get; set; } // = null!; + public List<string>? Allow { get; set; } [JsonPropertyName("deny")] - public List<string>? Deny { get; set; } // = null!; + public List<string>? Deny { get; set; } [JsonPropertyName("allow_ip_literals")] public bool AllowIpLiterals { get; set; } // = false; diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index f47ab64..8c95bc3 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -39,11 +39,12 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { public string UserId => WhoAmI.UserId; public string UserLocalpart => UserId.Split(":")[0][1..]; public string ServerName => UserId.Split(":", 2)[1]; + public string BaseUrl => ClientHttpClient.BaseAddress!.ToString().TrimEnd('/'); [JsonIgnore] public string AccessToken { get; set; } - public HsNamedCaches NamedCaches { get; set; } = null!; + public HsNamedCaches NamedCaches { get; set; } public GenericRoom GetRoom(string roomId) { if (roomId is null || !roomId.StartsWith("!")) throw new ArgumentException("Room ID must start with !", nameof(roomId)); @@ -408,22 +409,22 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { #region Authenticated Media // TODO: implement /_matrix/client/v1/media/config when it's actually useful - https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediaconfig + private bool? _serverSupportsAuthMedia; - private (string ServerName, string MediaId) ParseMxcUri(string mxcUri) { - if (!mxcUri.StartsWith("mxc://")) throw new ArgumentException("Matrix Content URIs must start with 'mxc://'", nameof(mxcUri)); - var parts = mxcUri[6..].Split('/'); - if (parts.Length != 2) throw new ArgumentException($"Invalid Matrix Content URI '{mxcUri}' passed! Matrix Content URIs must exist of only 2 parts!", nameof(mxcUri)); - return (parts[0], parts[1]); - } + public async Task<string> GetMediaUrlAsync(MxcUri mxcUri, string? filename = null, int? timeout = null) { + if (_serverSupportsAuthMedia == true) return mxcUri.ToDownloadUri(BaseUrl, filename, timeout); + if (_serverSupportsAuthMedia == false) return mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout); - public async Task<Stream> GetMediaStreamAsync(string mxcUri, string? filename = null, int? timeout = null) { - var (serverName, mediaId) = ParseMxcUri(mxcUri); try { - var uri = $"/_matrix/client/v1/media/download/{serverName}/{mediaId}"; - if (!string.IsNullOrWhiteSpace(filename)) uri += $"/{HttpUtility.UrlEncode(filename)}"; - if (timeout is not null) uri += $"?timeout_ms={timeout}"; - var res = await ClientHttpClient.GetAsync(uri); - return await res.Content.ReadAsStreamAsync(); + // Console.WriteLine($"Trying authenticated media URL: {uri}"); + var res = await ClientHttpClient.SendAsync(new() { + Method = HttpMethod.Head, + RequestUri = (new Uri(mxcUri.ToDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute)) + }); + if (res.IsSuccessStatusCode) { + _serverSupportsAuthMedia = true; + return mxcUri.ToDownloadUri(BaseUrl, filename, timeout); + } } catch (MatrixException e) { if (e is not { ErrorCode: "M_UNKNOWN" }) throw; @@ -431,11 +432,15 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { //fallback to legacy media try { - var uri = $"/_matrix/media/v3/download/{serverName}/{mediaId}"; - if (!string.IsNullOrWhiteSpace(filename)) uri += $"/{HttpUtility.UrlEncode(filename)}"; - if (timeout is not null) uri += $"?timeout_ms={timeout}"; - var res = await ClientHttpClient.GetAsync(uri); - return await res.Content.ReadAsStreamAsync(); + // Console.WriteLine($"Trying legacy media URL: {uri}"); + var res = await ClientHttpClient.SendAsync(new() { + Method = HttpMethod.Head, + RequestUri = new(mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute) + }); + if (res.IsSuccessStatusCode) { + _serverSupportsAuthMedia = false; + return mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout); + } } catch (MatrixException e) { if (e is not { ErrorCode: "M_UNKNOWN" }) throw; @@ -443,13 +448,19 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { throw new LibMatrixException() { ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED, - Error = "Failed to download media" + Error = "Failed to get media URL" }; - // return default; } - public async Task<Stream> GetThumbnailStreamAsync(string mxcUri, int width, int height, string? method = null, int? timeout = null) { - var (serverName, mediaId) = ParseMxcUri(mxcUri); + public async Task<Stream> GetMediaStreamAsync(string mxcUri, string? filename = null, int? timeout = null) { + var uri = await GetMediaUrlAsync(mxcUri, filename, timeout); + var res = await ClientHttpClient.GetAsync(uri); + return await res.Content.ReadAsStreamAsync(); + } + + public async Task<Stream> GetThumbnailStreamAsync(MxcUri mxcUri, int width, int height, string? method = null, int? timeout = null) { + var (serverName, mediaId) = mxcUri; + try { var uri = new Uri($"/_matrix/client/v1/thumbnail/{serverName}/{mediaId}"); uri = uri.AddQuery("width", width.ToString()); @@ -494,7 +505,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { catch (MatrixException e) { if (e is not { ErrorCode: "M_UNRECOGNIZED" }) throw; } - + //fallback to legacy media try { var res = await ClientHttpClient.GetAsync($"/_matrix/media/v3/preview_url?url={HttpUtility.UrlEncode(url)}"); @@ -503,7 +514,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { catch (MatrixException e) { if (e is not { ErrorCode: "M_UNRECOGNIZED" }) throw; } - + throw new LibMatrixException() { ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED, Error = "Failed to download URL preview" diff --git a/LibMatrix/Homeservers/FederationClient.cs b/LibMatrix/Homeservers/FederationClient.cs
index c7f0240..617b737 100644 --- a/LibMatrix/Homeservers/FederationClient.cs +++ b/LibMatrix/Homeservers/FederationClient.cs
@@ -13,8 +13,8 @@ public class FederationClient { if (proxy is not null) HttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", federationEndpoint); } - public MatrixHttpClient HttpClient { get; set; } = null!; - public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } = null!; + public MatrixHttpClient HttpClient { get; set; } + public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } public async Task<ServerVersionResponse> GetServerVersionAsync() => await HttpClient.GetFromJsonAsync<ServerVersionResponse>("/_matrix/federation/v1/version"); } diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index 7995f03..7ac54a7 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -10,24 +10,24 @@ using LibMatrix.Services; namespace LibMatrix.Homeservers; public class RemoteHomeserver { - public RemoteHomeserver(string baseUrl, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { + public RemoteHomeserver(string serverName, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { if (string.IsNullOrWhiteSpace(proxy)) proxy = null; - BaseUrl = baseUrl; + ServerNameOrUrl = serverName; WellKnownUris = wellKnownUris; ClientHttpClient = new MatrixHttpClient { - BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {baseUrl}!")), + BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {serverName}!")), // Timeout = TimeSpan.FromSeconds(300) // TODO: Re-implement this }; - if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", baseUrl); + if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName); if (!string.IsNullOrWhiteSpace(wellKnownUris.Server)) FederationClient = new FederationClient(WellKnownUris.Server!, proxy); Auth = new(this); } private Dictionary<string, object> _profileCache { get; set; } = new(); - public string BaseUrl { get; } + public string ServerNameOrUrl { get; } [JsonIgnore] public MatrixHttpClient ClientHttpClient { get; set; } @@ -111,8 +111,8 @@ public class RemoteHomeserver { public class AliasResult { [JsonPropertyName("room_id")] - public string RoomId { get; set; } = null!; + public string RoomId { get; set; } [JsonPropertyName("servers")] - public List<string> Servers { get; set; } = null!; + public List<string> Servers { get; set; } } \ No newline at end of file diff --git a/LibMatrix/Homeservers/UserInteractiveAuthClient.cs b/LibMatrix/Homeservers/UserInteractiveAuthClient.cs
index bb3889f..8de01d2 100644 --- a/LibMatrix/Homeservers/UserInteractiveAuthClient.cs +++ b/LibMatrix/Homeservers/UserInteractiveAuthClient.cs
@@ -49,27 +49,27 @@ public class UserInteractiveAuthClient { internal class RegisterFlowsResponse { [JsonPropertyName("session")] - public string Session { get; set; } = null!; + public string Session { get; set; } [JsonPropertyName("flows")] - public List<RegisterFlow> Flows { get; set; } = null!; + public List<RegisterFlow> Flows { get; set; } [JsonPropertyName("params")] - public JsonObject Params { get; set; } = null!; + public JsonObject Params { get; set; } public class RegisterFlow { [JsonPropertyName("stages")] - public List<string> Stages { get; set; } = null!; + public List<string> Stages { get; set; } } } internal class LoginFlowsResponse { [JsonPropertyName("flows")] - public List<LoginFlow> Flows { get; set; } = null!; + public List<LoginFlow> Flows { get; set; } public class LoginFlow { [JsonPropertyName("type")] - public string Type { get; set; } = null!; + public string Type { get; set; } } } diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj
index c074e81..4814a18 100644 --- a/LibMatrix/LibMatrix.csproj +++ b/LibMatrix/LibMatrix.csproj
@@ -18,7 +18,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250123-182609" Condition="'$(Configuration)' == 'Release'"/> + <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20250208-191806" Condition="'$(Configuration)' == 'Release'" /> <ProjectReference Include="..\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj" Condition="'$(Configuration)' == 'Debug'"/> </ItemGroup> diff --git a/LibMatrix/MxcUri.cs b/LibMatrix/MxcUri.cs new file mode 100644
index 0000000..02a8fa6 --- /dev/null +++ b/LibMatrix/MxcUri.cs
@@ -0,0 +1,43 @@ +using System.Diagnostics.CodeAnalysis; + +namespace LibMatrix; + +public class MxcUri { + public required string ServerName; + public required string MediaId; + + public static MxcUri Parse([StringSyntax("Uri")] string mxcUri) { + if (!mxcUri.StartsWith("mxc://")) throw new ArgumentException("Matrix Content URIs must start with 'mxc://'", nameof(mxcUri)); + var parts = mxcUri[6..].Split('/'); + if (parts.Length != 2) throw new ArgumentException($"Invalid Matrix Content URI '{mxcUri}' passed! Matrix Content URIs must exist of only 2 parts!", nameof(mxcUri)); + return new MxcUri { + ServerName = parts[0], + MediaId = parts[1] + }; + } + + public static implicit operator MxcUri(string mxcUri) => Parse(mxcUri); + public static implicit operator string(MxcUri mxcUri) => $"mxc://{mxcUri.ServerName}/{mxcUri.MediaId}"; + public static implicit operator (string, string)(MxcUri mxcUri) => (mxcUri.ServerName, mxcUri.MediaId); + public static implicit operator MxcUri((string serverName, string mediaId) mxcUri) => (mxcUri.serverName, mxcUri.mediaId); + // public override string ToString() => $"mxc://{ServerName}/{MediaId}"; + + public string ToDownloadUri(string? baseUrl = null, string? filename = null, int? timeout = null) { + var uri = $"{baseUrl}/_matrix/client/v1/media/download/{ServerName}/{MediaId}"; + if (filename is not null) uri += $"/{filename}"; + if (timeout is not null) uri += $"?timeout={timeout}"; + return uri; + } + + public string ToLegacyDownloadUri(string? baseUrl = null, string? filename = null, int? timeout = null) { + var uri = $"{baseUrl}/_matrix/media/v3/download/{ServerName}/{MediaId}"; + if (filename is not null) uri += $"/{filename}"; + if (timeout is not null) uri += $"?timeout_ms={timeout}"; + return uri; + } + + public void Deconstruct(out string serverName, out string mediaId) { + serverName = ServerName; + mediaId = MediaId; + } +} \ No newline at end of file diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs
index 1da6ff3..d9a6acd 100644 --- a/LibMatrix/Responses/CreateRoomRequest.cs +++ b/LibMatrix/Responses/CreateRoomRequest.cs
@@ -26,7 +26,7 @@ public class CreateRoomRequest { //we dont want to use this, we want more control // [JsonPropertyName("preset")] - // public string Preset { get; set; } = null!; + // public string Preset { get; set; } [JsonPropertyName("initial_state")] public List<StateEvent>? InitialState { get; set; } diff --git a/LibMatrix/Responses/LoginResponse.cs b/LibMatrix/Responses/LoginResponse.cs
index b15eb20..ac2269c 100644 --- a/LibMatrix/Responses/LoginResponse.cs +++ b/LibMatrix/Responses/LoginResponse.cs
@@ -4,10 +4,10 @@ namespace LibMatrix.Responses; public class LoginResponse { [JsonPropertyName("access_token")] - public string AccessToken { get; set; } = null!; + public string AccessToken { get; set; } [JsonPropertyName("device_id")] - public string DeviceId { get; set; } = null!; + public string DeviceId { get; set; } private string? _homeserver; @@ -18,7 +18,7 @@ public class LoginResponse { } [JsonPropertyName("user_id")] - public string UserId { get; set; } = null!; + public string UserId { get; set; } // public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedHomeserver(string? proxy = null) { // var urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(Homeserver); diff --git a/LibMatrix/Responses/SyncResponse.cs b/LibMatrix/Responses/SyncResponse.cs
index b2308c5..529bd5c 100644 --- a/LibMatrix/Responses/SyncResponse.cs +++ b/LibMatrix/Responses/SyncResponse.cs
@@ -8,7 +8,7 @@ internal partial class SyncResponseSerializerContext : JsonSerializerContext { } public class SyncResponse { [JsonPropertyName("next_batch")] - public string NextBatch { get; set; } = null!; + public string NextBatch { get; set; } [JsonPropertyName("account_data")] public EventList? AccountData { get; set; } @@ -17,7 +17,7 @@ public class SyncResponse { public PresenceDataStructure? Presence { get; set; } [JsonPropertyName("device_one_time_keys_count")] - public Dictionary<string, int>? DeviceOneTimeKeysCount { get; set; } = null!; + public Dictionary<string, int>? DeviceOneTimeKeysCount { get; set; } [JsonPropertyName("rooms")] public RoomsDataStructure? Rooms { get; set; } diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 202bfc1..8f1b56d 100644 --- a/LibMatrix/RoomTypes/GenericRoom.cs +++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -553,5 +553,5 @@ public class GenericRoom { public class RoomIdResponse { [JsonPropertyName("room_id")] - public string RoomId { get; set; } = null!; + public string RoomId { get; set; } } \ No newline at end of file diff --git a/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs b/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs
index 88c0353..13c5f58 100644 --- a/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs +++ b/Tests/LibMatrix.Tests/Abstractions/RoomAbstraction.cs
@@ -52,7 +52,7 @@ public static class RoomAbstraction { return testRoom; } - private static SemaphoreSlim _spaceSemaphore = null!; + private static SemaphoreSlim _spaceSemaphore = new(1, 1); public static async Task<SpaceRoom> GetTestSpace(AuthenticatedHomeserverGeneric hs, int roomCount = 100, bool addSpaces = false, int spaceSizeReduction = 10) { _spaceSemaphore ??= new SemaphoreSlim(roomCount / spaceSizeReduction, roomCount / spaceSizeReduction); diff --git a/Utilities/LibMatrix.DevTestBot/Bot/Commands/PingCommand.cs b/Utilities/LibMatrix.DevTestBot/Bot/Commands/PingCommand.cs
index 85c86a3..745c75d 100644 --- a/Utilities/LibMatrix.DevTestBot/Bot/Commands/PingCommand.cs +++ b/Utilities/LibMatrix.DevTestBot/Bot/Commands/PingCommand.cs
@@ -7,5 +7,16 @@ public class PingCommand : ICommand { public string Name { get; } = "ping"; public string Description { get; } = "Pong!"; - public async Task Invoke(CommandContext ctx) => await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "pong!")); + // public async Task Invoke(CommandContext ctx) => await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "pong!")); + public async Task Invoke(CommandContext ctx) { + // await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "pong!")); + var count = ctx.Args.Length > 0 ? int.Parse(ctx.Args[0]) : 1; + var tasks = Enumerable.Range(0, count).Select(async i => { + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: $"!ping {i}", messageType: "m.text")); + await Task.Delay(1000); + }).ToList(); + await Task.WhenAll(tasks); + + await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent(body: "Pong!")); + } } \ No newline at end of file diff --git a/Utilities/LibMatrix.DevTestBot/Bot/PingTestBot.cs b/Utilities/LibMatrix.DevTestBot/Bot/PingTestBot.cs new file mode 100644
index 0000000..9c8ad67 --- /dev/null +++ b/Utilities/LibMatrix.DevTestBot/Bot/PingTestBot.cs
@@ -0,0 +1,125 @@ +using System.Diagnostics.CodeAnalysis; +using ArcaneLibs.Extensions; +using LibMatrix.EventTypes.Spec; +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.ExampleBot.Bot.Interfaces; +using LibMatrix.Filters; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace LibMatrix.ExampleBot.Bot; + +public class PingTestBot : IHostedService { + private readonly HomeserverProviderService _homeserverProviderService; + private readonly ILogger<DevTestBot> _logger; + private readonly DevTestBotConfiguration _configuration; + private readonly IEnumerable<ICommand> _commands; + + public PingTestBot(HomeserverProviderService homeserverProviderService, ILogger<DevTestBot> logger, + DevTestBotConfiguration configuration, IServiceProvider services) { + logger.LogInformation("{} instantiated!", GetType().Name); + _homeserverProviderService = homeserverProviderService; + _logger = logger; + _configuration = configuration; + _logger.LogInformation("Getting commands..."); + _commands = services.GetServices<ICommand>(); + _logger.LogInformation("Got {} commands!", _commands.Count()); + } + + /// <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) { + // Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); + AuthenticatedHomeserverGeneric hs; + try { + hs = await _homeserverProviderService.GetAuthenticatedWithToken(_configuration.Homeserver, + _configuration.AccessToken); + } + catch (Exception e) { + _logger.LogError("{}", e.Message); + throw; + } + + var msg = new MessageBuilder().WithRainbowString("Meanwhile, I'm sitting here, still struggling with trying to rainbow. ^^'").Build(); + + var syncHelper = new SyncHelper(hs); + syncHelper.Filter = new SyncFilter { + Room = new SyncFilter.RoomFilter { + Timeline = new SyncFilter.RoomFilter.StateFilter() { + Limit = 1, + Senders = ["@me"] + }, + Rooms = ["!ping-v11:maunium.net"], + AccountData = new(types: []), + IncludeLeave = false + } + }; + + // await hs.GetRoom("!VJwxdebqoQlhGSEncc:codestorm.net").JoinAsync(); + + // foreach (var room in await hs.GetJoinedRooms()) { + // if(room.RoomId is "!OGEhHVWSdvArJzumhm:matrix.org") continue; + // foreach (var stateEvent in await room.GetStateAsync<List<StateEvent>>("")) { + // var _ = stateEvent.GetType; + // } + // _logger.LogInformation($"Got room state for {room.RoomId}!"); + // } + + // syncHelper.InviteReceivedHandlers.Add(async Task (args) => { + // var inviteEvent = + // args.Value.InviteState.Events.FirstOrDefault(x => + // x.Type == "m.room.member" && x.StateKey == hs.UserId); + // _logger.LogInformation( + // $"Got invite to {args.Key} by {inviteEvent.Sender} with reason: {(inviteEvent.TypedContent as RoomMemberEventContent).Reason}"); + // if (inviteEvent.Sender.EndsWith(":rory.gay") || inviteEvent.Sender == "@mxidupwitch:the-apothecary.club") + // try { + // var senderProfile = await hs.GetProfileAsync(inviteEvent.Sender); + // await hs.GetRoom(args.Key).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); + // } + // catch (Exception e) { + // _logger.LogError("{}", e.ToString()); + // await hs.GetRoom(args.Key).LeaveAsync("I was unable to join the room: " + e); + // } + // }); + syncHelper.TimelineEventHandlers.Add(async @event => { + _logger.LogInformation( + "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(false, true)); + + var room = hs.GetRoom(@event.RoomId); + // _logger.LogInformation(eventResponse.ToJson(indent: false)); + if (@event is not { Sender: "@emma:rory.gay" }) return; + if (@event is { Type: "m.room.message", TypedContent: RoomMessageEventContent message }) + if (message is { MessageType: "m.text" } && message.Body.StartsWith(_configuration.Prefix)) { + var command = _commands.FirstOrDefault(x => x.Name == message.Body.Split(' ')[0][_configuration.Prefix.Length..]); + if (command == null) { + await room.SendMessageEventAsync( + new RoomMessageEventContent("m.text", "Command not found!")); + return; + } + + var ctx = new CommandContext { + Room = room, + MessageEvent = @event + }; + if (await command.CanInvoke(ctx)) + await command.Invoke(ctx); + else + await room.SendMessageEventAsync( + new RoomMessageEventContent("m.text", "You do not have permission to run this command!")); + } + }); + await syncHelper.RunSyncLoopAsync(cancellationToken: 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 Task StopAsync(CancellationToken cancellationToken) { + _logger.LogInformation("Shutting down bot!"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj b/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj
index 3f4c9d8..8817e9c 100644 --- a/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj +++ b/Utilities/LibMatrix.DevTestBot/LibMatrix.DevTestBot.csproj
@@ -18,7 +18,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="ArcaneLibs.StringNormalisation" Version="1.0.0-preview.20250123-182609" Condition="'$(Configuration)' == 'Release'"/> + <PackageReference Include="ArcaneLibs.StringNormalisation" Version="1.0.0-preview.20250208-191806" Condition="'$(Configuration)' == 'Release'" /> <ProjectReference Include="..\..\ArcaneLibs\ArcaneLibs.StringNormalisation\ArcaneLibs.StringNormalisation.csproj" Condition="'$(Configuration)' == 'Debug'"/> <ProjectReference Include="..\..\LibMatrix\LibMatrix.csproj"/> </ItemGroup> diff --git a/Utilities/LibMatrix.DevTestBot/Program.cs b/Utilities/LibMatrix.DevTestBot/Program.cs
index daaa65e..8eaaea8 100644 --- a/Utilities/LibMatrix.DevTestBot/Program.cs +++ b/Utilities/LibMatrix.DevTestBot/Program.cs
@@ -24,7 +24,7 @@ var host = Host.CreateDefaultBuilder(args).ConfigureServices((_, services) => { } // services.AddHostedService<ServerRoomSizeCalulator>(); - services.AddHostedService<DevTestBot>(); + services.AddHostedService<PingTestBot>(); }).UseConsoleLifetime().Build(); await host.RunAsync(); \ No newline at end of file diff --git a/Utilities/LibMatrix.DevTestBot/appsettings.json b/Utilities/LibMatrix.DevTestBot/appsettings.json
index db64c22..0d18b81 100644 --- a/Utilities/LibMatrix.DevTestBot/appsettings.json +++ b/Utilities/LibMatrix.DevTestBot/appsettings.json
@@ -8,7 +8,7 @@ }, "Bot": { "Homeserver": "rory.gay", - "AccessToken": "syt_xxxxxxxxxxxxxxxxx", - "Prefix": "!" + "AccessToken": "syt_ZW1tYQ_ATWykhpCWzjxkgTJHnAx_2AaoI7", + "Prefix": "$" } } \ No newline at end of file diff --git a/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs
index 2cfcf32..6dc76f6 100644 --- a/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs +++ b/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs
@@ -4,28 +4,28 @@ namespace LibMatrix.Utilities.Bot.AppServices; public class AppServiceConfiguration { [JsonPropertyName("id")] - public string Id { get; set; } = null!; + public string Id { get; set; } [JsonPropertyName("url")] - public string? Url { get; set; } = null!; + public string? Url { get; set; } [JsonPropertyName("sender_localpart")] - public string SenderLocalpart { get; set; } = null!; + public string SenderLocalpart { get; set; } [JsonPropertyName("as_token")] - public string AppserviceToken { get; set; } = null!; + public string AppserviceToken { get; set; } [JsonPropertyName("hs_token")] - public string HomeserverToken { get; set; } = null!; + public string HomeserverToken { get; set; } [JsonPropertyName("protocols")] - public List<string>? Protocols { get; set; } = null!; + public List<string>? Protocols { get; set; } [JsonPropertyName("rate_limited")] - public bool? RateLimited { get; set; } = null!; + public bool? RateLimited { get; set; } [JsonPropertyName("namespaces")] - public AppserviceNamespaces Namespaces { get; set; } = null!; + public AppserviceNamespaces Namespaces { get; set; } public class AppserviceNamespaces { [JsonPropertyName("users")] @@ -42,7 +42,7 @@ public class AppServiceConfiguration { public bool Exclusive { get; set; } [JsonPropertyName("regex")] - public string Regex { get; set; } = null!; + public string Regex { get; set; } } }