diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index de4f3d4..83b1685 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -113,6 +113,7 @@ public class SyncHelper {
if (sync.Rooms is { Join.Count: > 0 }) {
foreach (var updatedRoom in sync.Rooms.Join) {
+ if(updatedRoom.Value.Timeline is null) continue;
foreach (var stateEventResponse in updatedRoom.Value.Timeline.Events) {
stateEventResponse.RoomId = updatedRoom.Key;
var tasks = TimelineEventHandlers.Select(x => x(stateEventResponse)).ToList();
@@ -178,7 +179,7 @@ public class SyncResult {
public class JoinedRoomDataStructure {
[JsonPropertyName("timeline")]
- public TimelineDataStructure Timeline { get; set; }
+ public TimelineDataStructure? Timeline { get; set; }
[JsonPropertyName("state")]
public EventList State { get; set; }
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index ecac4e4..bb34112 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using LibMatrix.Extensions;
using LibMatrix.Helpers;
@@ -56,7 +57,10 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
}
public async Task<GenericRoom> CreateRoom(CreateRoomRequest creationEvent) {
- var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent);
+ creationEvent.CreationContent["creator"] = UserId;
+ var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent, new JsonSerializerOptions {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ });
if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to create room: {await res.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to create room: {await res.Content.ReadAsStringAsync()}");
@@ -68,13 +72,14 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
#region Account Data
public async Task<T> GetAccountData<T>(string key) {
- var res = await _httpClient.GetAsync($"/_matrix/client/v3/user/{UserId}/account_data/{key}");
- if (!res.IsSuccessStatusCode) {
- Console.WriteLine($"Failed to get account data: {await res.Content.ReadAsStringAsync()}");
- throw new InvalidDataException($"Failed to get account data: {await res.Content.ReadAsStringAsync()}");
- }
-
- return await res.Content.ReadFromJsonAsync<T>();
+ // var res = await _httpClient.GetAsync($"/_matrix/client/v3/user/{UserId}/account_data/{key}");
+ // if (!res.IsSuccessStatusCode) {
+ // Console.WriteLine($"Failed to get account data: {await res.Content.ReadAsStringAsync()}");
+ // throw new InvalidDataException($"Failed to get account data: {await res.Content.ReadAsStringAsync()}");
+ // }
+ //
+ // return await res.Content.ReadFromJsonAsync<T>();
+ return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/user/{UserId}/account_data/{key}");
}
public async Task SetAccountData(string key, object data) {
diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs
index c1c1697..2c05088 100644
--- a/LibMatrix/Responses/CreateRoomRequest.cs
+++ b/LibMatrix/Responses/CreateRoomRequest.cs
@@ -7,6 +7,7 @@ using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using LibMatrix.Extensions;
using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
using LibMatrix.StateEventTypes.Spec;
namespace LibMatrix.Responses;
@@ -29,6 +30,9 @@ public class CreateRoomRequest {
[JsonPropertyName("initial_state")]
public List<StateEvent> InitialState { get; set; } = null!;
+ /// <summary>
+ /// One of: ["public", "private"]
+ /// </summary>
[JsonPropertyName("visibility")]
public string Visibility { get; set; } = null!;
@@ -38,6 +42,9 @@ public class CreateRoomRequest {
[JsonPropertyName("creation_content")]
public JsonObject CreationContent { get; set; } = new();
+ [JsonPropertyName("invite")]
+ public List<string> Invite { get; set; }
+
/// <summary>
/// For use only when you can't use the CreationContent property
/// </summary>
@@ -53,9 +60,10 @@ public class CreateRoomRequest {
StateEvent.KnownStateEventTypes.FirstOrDefault(x =>
x.GetCustomAttributes<MatrixEventAttribute>()?
.Any(y => y.EventName == event_type) ?? false) ?? typeof(object)
- )
+ )
});
}
+
return stateEvent;
}
set {
@@ -75,4 +83,44 @@ public class CreateRoomRequest {
return errors;
}
+
+ public static CreateRoomRequest CreatePrivate(AuthenticatedHomeserverGeneric hs, string? name = null, string? roomAliasName = null) {
+ var request = new CreateRoomRequest() {
+ Name = name ?? "Private Room",
+ Visibility = "private",
+ CreationContent = new(),
+ PowerLevelContentOverride = new() {
+ EventsDefault = 0,
+ UsersDefault = 0,
+ Kick = 50,
+ Ban = 50,
+ Invite = 25,
+ StateDefault = 10,
+ Redact = 50,
+ NotificationsPl = new() {
+ Room = 10
+ },
+ Events = new() {
+ { "m.room.avatar", 50 },
+ { "m.room.canonical_alias", 50 },
+ { "m.room.encryption", 100 },
+ { "m.room.history_visibility", 100 },
+ { "m.room.name", 50 },
+ { "m.room.power_levels", 100 },
+ { "m.room.server_acl", 100 },
+ { "m.room.tombstone", 100 }
+ },
+ Users = new() {
+ {
+ hs.UserId,
+ 101
+ }
+ }
+ },
+ RoomAliasName = roomAliasName,
+ InitialState = new()
+ };
+
+ return request;
+ }
}
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 3ba965b..8ba9a4b 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Web;
using LibMatrix.Extensions;
@@ -83,6 +84,7 @@ public class GenericRoom {
return res ?? new MessagesResponse();
}
+ // TODO: should we even error handle here?
public async Task<string> GetNameAsync() {
try {
var res = await GetStateAsync<RoomNameEventData>("m.room.name");
@@ -103,6 +105,8 @@ public class GenericRoom {
});
}
+
+ // TODO: rewrite (members endpoint?)
public async IAsyncEnumerable<StateEventResponse> GetMembersAsync(bool joinedOnly = true) {
var res = GetFullStateAsync();
await foreach (var member in res) {
@@ -112,6 +116,9 @@ public class GenericRoom {
}
}
+
+#region Utility shortcuts
+
public async Task<List<string>> GetAliasesAsync() {
var res = await GetStateAsync<RoomAliasEventData>("m.room.aliases");
return res.Aliases;
@@ -143,6 +150,13 @@ public class GenericRoom {
return res.Type;
}
+ public async Task<RoomPowerLevelEventData?> GetPowerLevelAsync() =>
+ await GetStateAsync<RoomPowerLevelEventData>("m.room.power_levels");
+
+#endregion
+
+
+
public async Task ForgetAsync() =>
await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null);
@@ -169,7 +183,9 @@ public class GenericRoom {
public async Task<EventIdResponse> SendMessageEventAsync(string eventType, RoomMessageEventData content) {
var res = await _httpClient.PutAsJsonAsync(
- $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content);
+ $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content, new JsonSerializerOptions() {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ });
var resu = await res.Content.ReadFromJsonAsync<EventIdResponse>();
return resu;
}
@@ -207,4 +223,8 @@ public class GenericRoom {
}
public readonly SpaceRoom AsSpace;
+
+ public async Task<T> GetEvent<T>(string eventId) {
+ return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}");
+ }
}
diff --git a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs b/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
index d3da559..08e8f22 100644
--- a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
@@ -12,6 +12,10 @@ public class JoinRulesEventData : IStateEventType {
private static string Invite = "invite";
private static string Knock = "knock";
+ /// <summary>
+ /// one of ["public", "invite", "knock", "restricted", "knock_restricted"]
+ /// "private" is reserved without implementation!
+ /// </summary>
[JsonPropertyName("join_rule")]
public string JoinRule { get; set; }
diff --git a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
index 5d65237..11a0e82 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
@@ -17,4 +17,24 @@ public class RoomMessageEventData : IStateEventType {
[JsonPropertyName("format")]
public string Format { get; set; }
+
+ [JsonPropertyName("m.relates_to")]
+ public MessageRelatesTo? RelatesTo { get; set; }
+
+ /// <summary>
+ /// Media URI for this message, if any
+ /// </summary>
+ [JsonPropertyName("url")]
+ public string? Url { get; set; }
+
+ public class MessageRelatesTo {
+
+ [JsonPropertyName("m.in_reply_to")]
+ public MessageInReplyTo? InReplyTo { get; set; }
+
+ public class MessageInReplyTo {
+ [JsonPropertyName("event_id")]
+ public string EventId { get; set; }
+ }
+ }
}
|