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; public class Room { private readonly HttpClient _httpClient; public Room(HttpClient httpClient, string roomId) { _httpClient = httpClient; RoomId = roomId; if(GetType() != typeof(SpaceRoom)) AsSpace = new SpaceRoom(_httpClient, RoomId); } public string RoomId { get; set; } public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) { var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(type)) url += $"/{type}"; if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; var res = await _httpClient.GetAsync(url); if (!res.IsSuccessStatusCode) { if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}"); return null; } var result = await res.Content.ReadFromJsonAsync(); return result; } public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) { var res = await GetStateAsync(type, stateKey, logOnFailure); if (res == null) return default; return res.Value.Deserialize(); } public async Task 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}"; if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; var res = await _httpClient.GetAsync(url); if (!res.IsSuccessStatusCode) { Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); } var result = await res.Content.ReadFromJsonAsync(); return result ?? new MessagesResponse(); } public async Task GetNameAsync() { var res = await GetStateAsync("m.room.name"); if (!res.HasValue) { Console.WriteLine($"Room {RoomId} has no name!"); return RoomId; } var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() ?? RoomId : RoomId; //Console.WriteLine($"Got name: {resn}"); return resn; } public async Task JoinAsync(string[]? homeservers = null) { var join_url = $"/_matrix/client/r0/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); } public async Task> GetMembersAsync(bool joinedOnly = true) { var res = await GetStateAsync(""); if (!res.HasValue) return new List(); var members = new List(); foreach (var member in res.Value.EnumerateArray()) { if (member.GetProperty("type").GetString() != "m.room.member") continue; if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue; var memberId = member.GetProperty("state_key").GetString(); members.Add(memberId ?? throw new InvalidOperationException("Event type was member but state key was null!")); } return members; } public async Task> GetAliasesAsync() { var res = await GetStateAsync("m.room.aliases"); if (!res.HasValue) return new List(); var aliases = new List(); foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? ""); return aliases; } public async Task GetCanonicalAliasAsync() { var res = await GetStateAsync("m.room.canonical_alias"); if (!res.HasValue) return ""; return res.Value.GetProperty("alias").GetString() ?? ""; } public async Task GetTopicAsync() { var res = await GetStateAsync("m.room.topic"); if (!res.HasValue) return ""; return res.Value.GetProperty("topic").GetString() ?? ""; } public async Task GetAvatarUrlAsync() { var res = await GetStateAsync("m.room.avatar"); if (!res.HasValue) return ""; return res.Value.GetProperty("url").GetString() ?? ""; } public async Task GetJoinRuleAsync() { var res = await GetStateAsync("m.room.join_rules"); if (!res.HasValue) return new JoinRules(); return res.Value.Deserialize() ?? new JoinRules(); } public async Task GetHistoryVisibilityAsync() { var res = await GetStateAsync("m.room.history_visibility"); if (!res.HasValue) return ""; return res.Value.GetProperty("history_visibility").GetString() ?? ""; } public async Task GetGuestAccessAsync() { var res = await GetStateAsync("m.room.guest_access"); if (!res.HasValue) return ""; return res.Value.GetProperty("guest_access").GetString() ?? ""; } public async Task GetCreateEventAsync() { var res = await GetStateAsync("m.room.create"); if (!res.HasValue) return new CreateEvent(); res.FindExtraJsonFields(typeof(CreateEvent)); return res.Value.Deserialize() ?? new CreateEvent(); } public async Task GetRoomType() { var res = await GetStateAsync("m.room.create"); if (!res.HasValue) return null; 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 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 Chunk { get; set; } = new(); [JsonPropertyName("state")] public List 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 Allow { get; set; } }