diff --git a/Matrix.Sdk.Sample.Console/DependencyInjectionSample.cs b/Matrix.Sdk.Sample.Console/DependencyInjectionSample.cs index 87c1b83..22941de 100644 --- a/Matrix.Sdk.Sample.Console/DependencyInjectionSample.cs +++ b/Matrix.Sdk.Sample.Console/DependencyInjectionSample.cs @@ -47,7 +47,10 @@ public async Task Run(IServiceProvider serviceProvider) if (roomEvent is not TextMessageEvent textMessageEvent) continue; - (string roomId, string senderUserId, string message) = textMessageEvent; + var senderUserId = textMessageEvent.SenderUserId; + var message = textMessageEvent.Message; + var roomId = textMessageEvent.RoomId; + if (client.UserId != senderUserId) Console.WriteLine($"RoomId: {roomId} received message from {senderUserId}: {message}."); } @@ -60,7 +63,10 @@ public async Task Run(IServiceProvider serviceProvider) if (roomEvent is not TextMessageEvent textMessageEvent) continue; - (string roomId, string senderUserId, string message) = textMessageEvent; + var senderUserId = textMessageEvent.SenderUserId; + var message = textMessageEvent.Message; + var roomId = textMessageEvent.RoomId; + if (anotherClient.UserId != senderUserId) Console.WriteLine($"RoomId: {roomId} received message from {senderUserId}: {message}."); } diff --git a/Matrix.Sdk.Sample.Console/Sample.cs b/Matrix.Sdk.Sample.Console/Sample.cs index 7031c8a..6ffde82 100644 --- a/Matrix.Sdk.Sample.Console/Sample.cs +++ b/Matrix.Sdk.Sample.Console/Sample.cs @@ -3,7 +3,7 @@ namespace Matrix.Sdk.Sample.Console using System; using System.Threading; using System.Threading.Tasks; - using Core.Domain.MatrixRoom; + using Core.Domain.Room; using Core.Domain.RoomEvent; using Core.Infrastructure.Dto.Room.Create; using Serilog; @@ -57,8 +57,11 @@ public async Task Run() { if (roomEvent is not TextMessageEvent textMessageEvent) continue; - - (string roomId, string senderUserId, string message) = textMessageEvent; + + var senderUserId = textMessageEvent.SenderUserId; + var message = textMessageEvent.Message; + var roomId = textMessageEvent.RoomId; + if (client.UserId != senderUserId) Console.WriteLine($"RoomId: {roomId} received message from {senderUserId}: {message}."); } @@ -71,7 +74,10 @@ public async Task Run() if (roomEvent is not TextMessageEvent textMessageEvent) continue; - (string roomId, string senderUserId, string message) = textMessageEvent; + var senderUserId = textMessageEvent.SenderUserId; + var message = textMessageEvent.Message; + var roomId = textMessageEvent.RoomId; + if (anotherClient.UserId != senderUserId) Console.WriteLine($"RoomId: {roomId} received message from {senderUserId}: {message}."); } diff --git a/Matrix.Sdk/Constants.cs b/Matrix.Sdk/Constants.cs index a4c3f0f..aa305b1 100644 --- a/Matrix.Sdk/Constants.cs +++ b/Matrix.Sdk/Constants.cs @@ -9,15 +9,16 @@ public static class Constants public class EventType { public const string Create = "m.room.create"; - public const string Member = "m.room.member"; - public const string Message = "m.room.message"; + public const string Redaction = "m.room.redaction"; + public const string Reaction = "m.reaction"; } public class MessageType { public const string Text = "m.text"; + public const string Image = "m.image"; } } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoom.cs b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoom.cs index 6084527..75ec868 100644 --- a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoom.cs +++ b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoom.cs @@ -1,27 +1,31 @@ -namespace Matrix.Sdk.Core.Domain.MatrixRoom +namespace Matrix.Sdk.Core.Domain.Room { using System.Collections.Generic; + using Infrastructure.Dto.Sync; + using Infrastructure.Dto.Sync.Event.Room; + using RoomEvent; - public record MatrixRoom + public record MatrixRoom(string Id, MatrixRoomStatus Status, List JoinedUserIds) { - public MatrixRoom(string id, MatrixRoomStatus status, List joinedUserIds) + public static MatrixRoom Create(string roomId, RoomResponse joinedRoom, MatrixRoomStatus status) { - Id = id; - Status = status; - JoinedUserIds = joinedUserIds; - } + var joinedUserIds = new List(); + foreach (RoomEventResponse timelineEvent in joinedRoom.Timeline.Events) + if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) + joinedUserIds.Add(joinRoomEvent!.SenderUserId); - public MatrixRoom(string id, MatrixRoomStatus status) - { - Id = id; - Status = status; - JoinedUserIds = new List(); + return new MatrixRoom(roomId, status, joinedUserIds); } - public string Id { get; } - - public MatrixRoomStatus Status { get; } + public static MatrixRoom CreateInvite(string roomId, InvitedRoom invitedRoom) + { + var joinedUserIds = new List(); + foreach (RoomStrippedState timelineEvent in invitedRoom.InviteState.Events) + if (JoinRoomEvent.Factory.TryCreateFromStrippedState(timelineEvent, roomId, + out JoinRoomEvent joinRoomEvent)) + joinedUserIds.Add(joinRoomEvent!.SenderUserId); - public List JoinedUserIds { get; } + return new MatrixRoom(roomId, MatrixRoomStatus.Invited, joinedUserIds); + } } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomEventFactory.cs b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomEventFactory.cs deleted file mode 100644 index 12e437e..0000000 --- a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomEventFactory.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Matrix.Sdk.Core.Domain.MatrixRoom -{ - using System.Collections.Generic; - using Infrastructure.Dto.Sync; - using Infrastructure.Dto.Sync.Event.Room; - using RoomEvent; - - public class MatrixRoomEventFactory - { - public List CreateFromJoined(string roomId, JoinedRoom joinedRoom) - { - var roomEvents = new List(); - - foreach (RoomEvent timelineEvent in joinedRoom.Timeline.Events) - if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) - roomEvents.Add(joinRoomEvent!); - else if (CreateRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out CreateRoomEvent createRoomEvent)) - roomEvents.Add(createRoomEvent!); - else if (InviteToRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out InviteToRoomEvent inviteToRoomEvent)) - roomEvents.Add(inviteToRoomEvent!); - else if (TextMessageEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out TextMessageEvent textMessageEvent)) - roomEvents.Add(textMessageEvent); - - return roomEvents; - } - - public List CreateFromInvited(string roomId, InvitedRoom invitedRoom) - { - var roomEvents = new List(); - - foreach (RoomStrippedState inviteStateEvent in invitedRoom.InviteState.Events) - if (JoinRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, - out JoinRoomEvent joinRoomEvent)) - roomEvents.Add(joinRoomEvent!); - else if (CreateRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, - out CreateRoomEvent createRoomEvent)) - roomEvents.Add(createRoomEvent!); - else if (InviteToRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, - out InviteToRoomEvent inviteToRoomEvent)) - roomEvents.Add(inviteToRoomEvent!); - else if (TextMessageEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, - out TextMessageEvent textMessageEvent)) - roomEvents.Add(textMessageEvent); - - return roomEvents; - } - - public List CreateFromLeft(string roomId, LeftRoom leftRoom) - { - var roomEvents = new List(); - - foreach (RoomEvent timelineEvent in leftRoom.Timeline.Events) - if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) - roomEvents.Add(joinRoomEvent!); - else if (CreateRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out CreateRoomEvent createRoomEvent)) - roomEvents.Add(createRoomEvent!); - else if (InviteToRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out InviteToRoomEvent inviteToRoomEvent)) - roomEvents.Add(inviteToRoomEvent!); - else if (TextMessageEvent.Factory.TryCreateFrom(timelineEvent, roomId, - out TextMessageEvent textMessageEvent)) - roomEvents.Add(textMessageEvent); - - return roomEvents; - } - } -} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomFactory.cs b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomFactory.cs deleted file mode 100644 index d0f963a..0000000 --- a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Matrix.Sdk.Core.Domain.MatrixRoom -{ - using System.Collections.Generic; - using Infrastructure.Dto.Sync; - using Infrastructure.Dto.Sync.Event.Room; - using RoomEvent; - - public class MatrixRoomFactory - { - public MatrixRoom CreateJoined(string roomId, JoinedRoom joinedRoom) - { - var joinedUserIds = new List(); - foreach (RoomEvent timelineEvent in joinedRoom.Timeline.Events) - if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) - joinedUserIds.Add(joinRoomEvent!.SenderUserId); - - return new MatrixRoom(roomId, MatrixRoomStatus.Joined, joinedUserIds); - } - - public MatrixRoom CreateInvite(string roomId, InvitedRoom invitedRoom) - { - var joinedUserIds = new List(); - foreach (RoomStrippedState timelineEvent in invitedRoom.InviteState.Events) - if (JoinRoomEvent.Factory.TryCreateFromStrippedState(timelineEvent, roomId, - out JoinRoomEvent joinRoomEvent)) - joinedUserIds.Add(joinRoomEvent!.SenderUserId); - - return new MatrixRoom(roomId, MatrixRoomStatus.Invited, joinedUserIds); - } - - public MatrixRoom CreateLeft(string roomId, LeftRoom leftRoom) - { - var joinedUserIds = new List(); - foreach (RoomEvent timelineEvent in leftRoom.Timeline.Events) - if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) - joinedUserIds.Add(joinRoomEvent!.SenderUserId); - - return new MatrixRoom(roomId, MatrixRoomStatus.Left, joinedUserIds); - } - } -} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomStatus.cs b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomStatus.cs index ad21eb4..d3ca495 100644 --- a/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomStatus.cs +++ b/Matrix.Sdk/Core/Domain/MatrixRoom/MatrixRoomStatus.cs @@ -1,4 +1,4 @@ -namespace Matrix.Sdk.Core.Domain.MatrixRoom +namespace Matrix.Sdk.Core.Domain.Room { public enum MatrixRoomStatus { diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/BaseRoomEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/BaseRoomEvent.cs index 6d19f2f..de51998 100644 --- a/Matrix.Sdk/Core/Domain/RoomEvent/BaseRoomEvent.cs +++ b/Matrix.Sdk/Core/Domain/RoomEvent/BaseRoomEvent.cs @@ -1,4 +1,62 @@ +using System; +using System.Collections.Generic; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room; + namespace Matrix.Sdk.Core.Domain.RoomEvent { - public abstract record BaseRoomEvent(string RoomId, string SenderUserId); + public abstract record BaseRoomEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp) + { + public static List Create(string roomId, RoomResponse joinedRoom) + { + var roomEvents = new List(); + + foreach (RoomEventResponse timelineEvent in joinedRoom.Timeline.Events) + { + var e = Create(roomId, timelineEvent); + if (e != null) roomEvents.Add(e); + + } + return roomEvents; + } + + public static BaseRoomEvent Create(string roomId, RoomEventResponse timelineEvent) + { + if (JoinRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out JoinRoomEvent joinRoomEvent)) return joinRoomEvent; + if (CreateRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var createRoomEvent)) return createRoomEvent; + if (InviteToRoomEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var inviteToRoomEvent)) return inviteToRoomEvent; + if (TextMessageEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var textMessageEvent)) return textMessageEvent; + if (ImageMessageEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var imageMessageEvent)) return imageMessageEvent; + if (RedactionEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var redactionEvent)) return redactionEvent; + if (ReactionEvent.Factory.TryCreateFrom(timelineEvent, roomId, out var reactionEvent)) return reactionEvent; + return null; + } + + public static List CreateFromInvited(string roomId, InvitedRoom invitedRoom) + { + var roomEvents = new List(); + + foreach (RoomStrippedState inviteStateEvent in invitedRoom.InviteState.Events) + { + var e = CreateFromInvited(roomId, inviteStateEvent); + if (e != null) + { + roomEvents.Add(e); + } + } + return roomEvents; + } + + public static BaseRoomEvent CreateFromInvited(string roomId, RoomStrippedState inviteStateEvent) + { + if (JoinRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var joinRoomEvent)) return joinRoomEvent; + if (CreateRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var createRoomEvent)) return createRoomEvent; + if (InviteToRoomEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var inviteToRoomEvent)) return inviteToRoomEvent; + if (TextMessageEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var textMessageEvent)) return textMessageEvent; + if (ImageMessageEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var imageMessageEvent)) return imageMessageEvent; + if (RedactionEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var redactionEvent)) return redactionEvent; + if (ReactionEvent.Factory.TryCreateFromStrippedState(inviteStateEvent, roomId, out var reactionEvent)) return reactionEvent; + return null; + } + } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/CreateRoomEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/CreateRoomEvent.cs index 2a9aad4..db123a1 100644 --- a/Matrix.Sdk/Core/Domain/RoomEvent/CreateRoomEvent.cs +++ b/Matrix.Sdk/Core/Domain/RoomEvent/CreateRoomEvent.cs @@ -1,24 +1,26 @@ +using System; + namespace Matrix.Sdk.Core.Domain.RoomEvent { using Infrastructure.Dto.Sync.Event; using Infrastructure.Dto.Sync.Event.Room; using Infrastructure.Dto.Sync.Event.Room.State; - public record CreateRoomEvent(string RoomId, string SenderUserId, string RoomCreatorUserId) : BaseRoomEvent(RoomId, - SenderUserId) + public record CreateRoomEvent(string EventId, string RoomId, string SenderUserId, string RoomCreatorUserId, DateTimeOffset Timestamp) : BaseRoomEvent(EventId, RoomId, + SenderUserId, Timestamp) { public static class Factory { - public static bool TryCreateFrom(RoomEvent roomEvent, string roomId, out CreateRoomEvent createRoomEvent) + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out CreateRoomEvent createRoomEvent) { RoomCreateContent content = roomEvent.Content.ToObject(); if (roomEvent.EventType == EventType.Create && content != null) { - createRoomEvent = new CreateRoomEvent(roomId, roomEvent.SenderUserId, content.RoomCreatorUserId); + createRoomEvent = new CreateRoomEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, content.RoomCreatorUserId, roomEvent.Timestamp); return true; } - createRoomEvent = new CreateRoomEvent(string.Empty, string.Empty, string.Empty); + createRoomEvent = null; return false; } @@ -29,11 +31,12 @@ public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedStat if (roomStrippedState.EventType == EventType.Create && content != null) { createRoomEvent = - new CreateRoomEvent(roomId, roomStrippedState.SenderUserId, content.RoomCreatorUserId); + new CreateRoomEvent(string.Empty, roomId, roomStrippedState.SenderUserId, content.RoomCreatorUserId, DateTimeOffset.MinValue); return true; + } - createRoomEvent = new CreateRoomEvent(string.Empty, string.Empty, string.Empty); + createRoomEvent = null; return false; } } diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/EditEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/EditEvent.cs new file mode 100644 index 0000000..effe73d --- /dev/null +++ b/Matrix.Sdk/Core/Domain/RoomEvent/EditEvent.cs @@ -0,0 +1,23 @@ +using Matrix.Sdk.Core.Infrastructure.Dto.Event; +using Newtonsoft.Json; + +namespace Matrix.Sdk.Core.Domain.RoomEvent +{ + public record EditEvent(MessageType MessageType, string Message, string EventId) + { + public string body = $"* {Message}"; + public MessageType msgtype { get; } = MessageType; + + [JsonProperty("m.new_content")] + public MessageEvent newContent = new MessageEvent(MessageType, Message); + + [JsonProperty("m.relates_to")] + public RelatesTo mRelatesTo = new RelatesTo(EventId); + + public record RelatesTo(string evntid) + { + public string event_id = evntid; + public string rel_type = "m.replace"; + } + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/ImageMessageEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/ImageMessageEvent.cs new file mode 100644 index 0000000..99f5c4f --- /dev/null +++ b/Matrix.Sdk/Core/Domain/RoomEvent/ImageMessageEvent.cs @@ -0,0 +1,41 @@ +using System; + +namespace Matrix.Sdk.Core.Domain.RoomEvent +{ + using Infrastructure.Dto.Sync.Event; + using Infrastructure.Dto.Sync.Event.Room; + using Infrastructure.Dto.Sync.Event.Room.Messaging; + + public record ImageMessageEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp, string Message, string MxcUrl) : BaseRoomEvent(EventId, RoomId, + SenderUserId, Timestamp) + { + public static class Factory + { + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out ImageMessageEvent textMessageEvent) + { + ImageContent content = roomEvent.Content.ToObject(); + if (roomEvent.EventType == EventType.Message && content?.MessageType == MessageType.Image) + { + textMessageEvent = new ImageMessageEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp, content.Body, content.url); + return true; + } + textMessageEvent = null; + return false; + } + + public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedState, string roomId, + out ImageMessageEvent textMessageEvent) + { + ImageContent content = roomStrippedState.Content.ToObject(); + if (roomStrippedState.EventType == EventType.Message && content?.MessageType == MessageType.Image) + { + textMessageEvent = new ImageMessageEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue, content.Body, content.url); + return true; + } + + textMessageEvent = null; + return false; + } + } + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/InviteToRoomEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/InviteToRoomEvent.cs index 1709756..c947789 100644 --- a/Matrix.Sdk/Core/Domain/RoomEvent/InviteToRoomEvent.cs +++ b/Matrix.Sdk/Core/Domain/RoomEvent/InviteToRoomEvent.cs @@ -1,25 +1,27 @@ +using System; + namespace Matrix.Sdk.Core.Domain.RoomEvent { using Infrastructure.Dto.Sync.Event; using Infrastructure.Dto.Sync.Event.Room; using Infrastructure.Dto.Sync.Event.Room.State; - public record InviteToRoomEvent(string RoomId, string SenderUserId) : BaseRoomEvent(RoomId, SenderUserId) + public record InviteToRoomEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp) : BaseRoomEvent(EventId, RoomId, SenderUserId, Timestamp) { public static class Factory { - public static bool TryCreateFrom(RoomEvent roomEvent, string roomId, + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out InviteToRoomEvent inviteToRoomEvent) { RoomMemberContent content = roomEvent.Content.ToObject(); if (roomEvent.EventType == EventType.Member && content?.UserMembershipState == UserMembershipState.Invite) { - inviteToRoomEvent = new InviteToRoomEvent(roomId, roomEvent.SenderUserId); + inviteToRoomEvent = new InviteToRoomEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp); return true; } - inviteToRoomEvent = new InviteToRoomEvent(string.Empty, string.Empty); + inviteToRoomEvent = null; return false; } @@ -30,11 +32,11 @@ public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedStat if (roomStrippedState.EventType == EventType.Member && content?.UserMembershipState == UserMembershipState.Invite) { - inviteToRoomEvent = new InviteToRoomEvent(roomId, roomStrippedState.SenderUserId); + inviteToRoomEvent = new InviteToRoomEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue); return true; } - inviteToRoomEvent = new InviteToRoomEvent(string.Empty, string.Empty); + inviteToRoomEvent = null; return false; } } diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/JoinRoomEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/JoinRoomEvent.cs index 6c06fe5..8c0ddee 100644 --- a/Matrix.Sdk/Core/Domain/RoomEvent/JoinRoomEvent.cs +++ b/Matrix.Sdk/Core/Domain/RoomEvent/JoinRoomEvent.cs @@ -1,23 +1,25 @@ +using System; + namespace Matrix.Sdk.Core.Domain.RoomEvent { using Infrastructure.Dto.Sync.Event; using Infrastructure.Dto.Sync.Event.Room; using Infrastructure.Dto.Sync.Event.Room.State; - public record JoinRoomEvent(string RoomId, string SenderUserId) : BaseRoomEvent(RoomId, SenderUserId) + public record JoinRoomEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp) : BaseRoomEvent(EventId, RoomId, SenderUserId, Timestamp) { public static class Factory { - public static bool TryCreateFrom(RoomEvent roomEvent, string roomId, out JoinRoomEvent joinRoomEvent) + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out JoinRoomEvent joinRoomEvent) { RoomMemberContent content = roomEvent.Content.ToObject(); if (roomEvent.EventType == EventType.Member && content?.UserMembershipState == UserMembershipState.Join) { - joinRoomEvent = new JoinRoomEvent(roomId, roomEvent.SenderUserId); + joinRoomEvent = new JoinRoomEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp); return true; } - joinRoomEvent = new JoinRoomEvent(string.Empty, string.Empty); + joinRoomEvent = null; return false; } @@ -28,11 +30,11 @@ public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedStat if (roomStrippedState.EventType == EventType.Member && content?.UserMembershipState == UserMembershipState.Join) { - joinRoomEvent = new JoinRoomEvent(roomId, roomStrippedState.SenderUserId); + joinRoomEvent = new JoinRoomEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue); return true; } - joinRoomEvent = new JoinRoomEvent(string.Empty, string.Empty); + joinRoomEvent = null; return false; } } diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/ReactionEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/ReactionEvent.cs new file mode 100644 index 0000000..abd52ad --- /dev/null +++ b/Matrix.Sdk/Core/Domain/RoomEvent/ReactionEvent.cs @@ -0,0 +1,43 @@ +using System; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room.Messaging; + +namespace Matrix.Sdk.Core.Domain.RoomEvent +{ + using Infrastructure.Dto.Sync.Event; + using Infrastructure.Dto.Sync.Event.Room; + using Infrastructure.Dto.Sync.Event.Room.State; + + public record ReactionEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp, string Reaction, string RelatesToEventId) : BaseRoomEvent(EventId, RoomId, SenderUserId, Timestamp) + { + + public static class Factory + { + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out ReactionEvent reactionEvent) + { + MessageContent content = roomEvent.Content.ToObject(); + if (roomEvent.EventType == EventType.Reaction && content?.relatesTo.rel_type == "m.annotation") + { + reactionEvent = new ReactionEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp, content.relatesTo.key, content.relatesTo.event_id); + return true; + } + + reactionEvent = null; + return false; + } + + public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedState, string roomId, + out ReactionEvent reactionEvent) + { + MessageContent content = roomStrippedState.Content.ToObject(); + if (roomStrippedState.EventType == EventType.Reaction && content?.relatesTo.rel_type == "m.annotation") + { + reactionEvent = new ReactionEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue, content.relatesTo.key, content.relatesTo.event_id); + return true; + } + + reactionEvent = null; + return false; + } + } + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/RedactionEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/RedactionEvent.cs new file mode 100644 index 0000000..a176b1f --- /dev/null +++ b/Matrix.Sdk/Core/Domain/RoomEvent/RedactionEvent.cs @@ -0,0 +1,43 @@ +using System; + +namespace Matrix.Sdk.Core.Domain.RoomEvent +{ + using Infrastructure.Dto.Sync.Event; + using Infrastructure.Dto.Sync.Event.Room; + using Infrastructure.Dto.Sync.Event.Room.State; + + public record RedactionEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp, string Reason, string RedactsEventId) : BaseRoomEvent(EventId, RoomId, SenderUserId, Timestamp) + { + private record ReasonObj(string reason); + + public static class Factory + { + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out RedactionEvent redactionEvent) + { + ReasonObj content = roomEvent.Content.ToObject(); + if (roomEvent.EventType == EventType.Redaction) + { + redactionEvent = new RedactionEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp, content.reason, roomEvent.redacts); + return true; + } + + redactionEvent = null; + return false; + } + + public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedState, string roomId, + out RedactionEvent redactionEvent) + { + ReasonObj content = roomStrippedState.Content.ToObject(); + if (roomStrippedState.EventType == EventType.Redaction) + { + redactionEvent = new RedactionEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue, content.reason, string.Empty); + return true; + } + + redactionEvent = null; + return false; + } + } + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/TextMessageEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/TextMessageEvent.cs index de170bd..8c45f4e 100644 --- a/Matrix.Sdk/Core/Domain/RoomEvent/TextMessageEvent.cs +++ b/Matrix.Sdk/Core/Domain/RoomEvent/TextMessageEvent.cs @@ -1,24 +1,26 @@ +using System; + namespace Matrix.Sdk.Core.Domain.RoomEvent { using Infrastructure.Dto.Sync.Event; using Infrastructure.Dto.Sync.Event.Room; using Infrastructure.Dto.Sync.Event.Room.Messaging; - public record TextMessageEvent(string RoomId, string SenderUserId, string Message) : BaseRoomEvent(RoomId, - SenderUserId) + public record TextMessageEvent(string EventId, string RoomId, string SenderUserId, DateTimeOffset Timestamp, string Message, string ReplacesEventId) : BaseRoomEvent(EventId, RoomId, + SenderUserId, Timestamp) { public static class Factory { - public static bool TryCreateFrom(RoomEvent roomEvent, string roomId, out TextMessageEvent textMessageEvent) + public static bool TryCreateFrom(RoomEventResponse roomEvent, string roomId, out TextMessageEvent textMessageEvent) { MessageContent content = roomEvent.Content.ToObject(); if (roomEvent.EventType == EventType.Message && content?.MessageType == MessageType.Text) { - textMessageEvent = new TextMessageEvent(roomId, roomEvent.SenderUserId, content.Body); + textMessageEvent = new TextMessageEvent(roomEvent.EventId, roomId, roomEvent.SenderUserId, roomEvent.Timestamp, content.Body, content.ReplacesEventId); return true; } - textMessageEvent = new TextMessageEvent(string.Empty, string.Empty, string.Empty); + textMessageEvent = new TextMessageEvent(roomEvent.EventId, string.Empty, string.Empty, roomEvent.Timestamp, string.Empty, string.Empty); return false; } @@ -28,11 +30,11 @@ public static bool TryCreateFromStrippedState(RoomStrippedState roomStrippedStat MessageContent content = roomStrippedState.Content.ToObject(); if (roomStrippedState.EventType == EventType.Message && content?.MessageType == MessageType.Text) { - textMessageEvent = new TextMessageEvent(roomId, roomStrippedState.SenderUserId, content.Body); + textMessageEvent = new TextMessageEvent(string.Empty, roomId, roomStrippedState.SenderUserId, DateTimeOffset.MinValue, content.Body, content.ReplacesEventId); return true; } - textMessageEvent = new TextMessageEvent(string.Empty, string.Empty, string.Empty); + textMessageEvent = new TextMessageEvent(string.Empty, string.Empty, string.Empty, DateTimeOffset.MinValue, string.Empty, string.Empty); return false; } } diff --git a/Matrix.Sdk/Core/Domain/RoomEvent/TypingSignalEvent.cs b/Matrix.Sdk/Core/Domain/RoomEvent/TypingSignalEvent.cs new file mode 100644 index 0000000..121da8d --- /dev/null +++ b/Matrix.Sdk/Core/Domain/RoomEvent/TypingSignalEvent.cs @@ -0,0 +1,3 @@ +namespace Matrix.Sdk.Core.Domain.RoomEvent { + public record TypingSignalEvent(bool typing, uint timeout = 0); +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Domain/Services/IPollingService.cs b/Matrix.Sdk/Core/Domain/Services/IPollingService.cs index 004baed..6d79245 100644 --- a/Matrix.Sdk/Core/Domain/Services/IPollingService.cs +++ b/Matrix.Sdk/Core/Domain/Services/IPollingService.cs @@ -1,7 +1,7 @@ namespace Matrix.Sdk.Core.Domain.Services { using System; - using MatrixRoom; + using Room; public interface IPollingService : IDisposable { diff --git a/Matrix.Sdk/Core/Domain/Services/PollingService.cs b/Matrix.Sdk/Core/Domain/Services/PollingService.cs index 0f37a26..9cce14d 100644 --- a/Matrix.Sdk/Core/Domain/Services/PollingService.cs +++ b/Matrix.Sdk/Core/Domain/Services/PollingService.cs @@ -8,7 +8,7 @@ namespace Matrix.Sdk.Core.Domain.Services using System.Threading.Tasks; using Infrastructure.Dto.Sync; using Infrastructure.Services; - using MatrixRoom; + using Room; using Microsoft.Extensions.Logging; public class PollingService : IPollingService diff --git a/Matrix.Sdk/Core/Domain/SyncBatch.cs b/Matrix.Sdk/Core/Domain/SyncBatch.cs index 0661a4e..0225c6e 100644 --- a/Matrix.Sdk/Core/Domain/SyncBatch.cs +++ b/Matrix.Sdk/Core/Domain/SyncBatch.cs @@ -3,12 +3,12 @@ namespace Matrix.Sdk.Core.Domain using System.Collections.Generic; using System.Linq; using Infrastructure.Dto.Sync; - using MatrixRoom; + using Room; using RoomEvent; public record SyncBatch { - private SyncBatch(string nextBatch, List matrixRooms, + private SyncBatch(string nextBatch, List matrixRooms, List matrixRoomEvents) { NextBatch = nextBatch; @@ -17,29 +17,26 @@ private SyncBatch(string nextBatch, List matrixRooms, } public string NextBatch { get; } - public List MatrixRooms { get; } + public List MatrixRooms { get; } public List MatrixRoomEvents { get; } internal static class Factory { - private static readonly MatrixRoomFactory MatrixRoomFactory = new(); - private static readonly MatrixRoomEventFactory MatrixRoomEventFactory = new(); - public static SyncBatch CreateFromSync(string nextBatch, Rooms rooms) { - List matrixRooms = GetMatrixRoomsFromSync(rooms); + List matrixRooms = GetMatrixRoomsFromSync(rooms); List matrixRoomEvents = GetMatrixEventsFromSync(rooms); return new SyncBatch(nextBatch, matrixRooms, matrixRoomEvents); } - private static List GetMatrixRoomsFromSync(Rooms rooms) + private static List GetMatrixRoomsFromSync(Rooms rooms) { - var joinedMatrixRooms = rooms.Join.Select(pair => MatrixRoomFactory.CreateJoined(pair.Key, pair.Value)) + var joinedMatrixRooms = rooms.Join.Select(pair => Room.MatrixRoom.Create(pair.Key, pair.Value, MatrixRoomStatus.Joined)) .ToList(); var invitedMatrixRooms = rooms.Invite - .Select(pair => MatrixRoomFactory.CreateInvite(pair.Key, pair.Value)).ToList(); - var leftMatrixRooms = rooms.Leave.Select(pair => MatrixRoomFactory.CreateLeft(pair.Key, pair.Value)) + .Select(pair => Room.MatrixRoom.CreateInvite(pair.Key, pair.Value)).ToList(); + var leftMatrixRooms = rooms.Leave.Select(pair => Room.MatrixRoom.Create(pair.Key, pair.Value, MatrixRoomStatus.Left)) .ToList(); return joinedMatrixRooms.Concat(invitedMatrixRooms).Concat(leftMatrixRooms).ToList(); @@ -48,11 +45,11 @@ public static SyncBatch CreateFromSync(string nextBatch, Rooms rooms) private static List GetMatrixEventsFromSync(Rooms rooms) { var joinedMatrixRoomEvents = rooms.Join - .SelectMany(pair => MatrixRoomEventFactory.CreateFromJoined(pair.Key, pair.Value)).ToList(); + .SelectMany(pair => BaseRoomEvent.Create(pair.Key, pair.Value)).ToList(); var invitedMatrixRoomEvents = rooms.Invite - .SelectMany(pair => MatrixRoomEventFactory.CreateFromInvited(pair.Key, pair.Value)).ToList(); + .SelectMany(pair => BaseRoomEvent.CreateFromInvited(pair.Key, pair.Value)).ToList(); var leftMatrixRoomEvents = rooms.Leave - .SelectMany(pair => MatrixRoomEventFactory.CreateFromLeft(pair.Key, pair.Value)).ToList(); + .SelectMany(pair => BaseRoomEvent.Create(pair.Key, pair.Value)).ToList(); return joinedMatrixRoomEvents.Concat(invitedMatrixRoomEvents).Concat(leftMatrixRoomEvents).ToList(); } diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Event/ImageMessageEvent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Event/ImageMessageEvent.cs new file mode 100644 index 0000000..bdce1e4 --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Event/ImageMessageEvent.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Matrix.Sdk.Core.Infrastructure.Dto.Event +{ + public record ImageMessageEvent(string body, string url) + { + [JsonConverter(typeof(StringEnumConverter))] + public MessageType msgtype = MessageType.Image; + } +} diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageEvent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageEvent.cs index b12f873..55dff5f 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageEvent.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageEvent.cs @@ -1,18 +1,10 @@ namespace Matrix.Sdk.Core.Infrastructure.Dto.Event { - using Newtonsoft.Json; - public record MessageEvent(MessageType MessageType, string Message) { - [JsonProperty("msgtype")] public MessageType MessageType { get; } = MessageType; - - [JsonProperty("body")] public string Message { get; } = Message; - } - - public record MessageEvent2(string msgtype, string body) - { - public string msgtype { get; } = msgtype; - - public string body { get; } = body; + public MessageType msgtype { get; } = MessageType; + public string body { get; } = Message; + public string formatted_body { get; } = Message == null ? null : Markdig.Markdown.ToHtml(Message); + public string format = "org.matrix.custom.html"; } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageType.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageType.cs index 44c3cc1..fd07d79 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageType.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Event/MessageType.cs @@ -4,7 +4,7 @@ namespace Matrix.Sdk.Core.Infrastructure.Dto.Event public enum MessageType { - [EnumMember(Value = "m.text")] Text - // [JsonProperty("m.text")] Text + [EnumMember(Value = "m.text")] Text, + [EnumMember(Value = "m.image")] Image } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Room/Manage/ChangeTopicRequest.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Room/Manage/ChangeTopicRequest.cs new file mode 100644 index 0000000..c9e1454 --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Room/Manage/ChangeTopicRequest.cs @@ -0,0 +1,17 @@ +namespace System.Runtime.CompilerServices.Dto.Room.Manage +{ + public record ChangeTopicRequest(string? topic ) + { + public string? topic { get; } = topic; + } + + public record ChangeNameRequest(string? name) + { + public string? name { get; } = name; + } + + public record ChangeAvatarRequest(string? url) + { + public string? url { get; } = url; + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/BaseEvent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/BaseEvent.cs index a87d428..9b9fdf2 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/BaseEvent.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/BaseEvent.cs @@ -10,6 +10,8 @@ public record BaseEvent /// public JObject Content { get; init; } + public string redacts; + /// /// Required. The fields in this object will vary depending on the type of event. /// When interacting with the REST API, this is the HTTP body. @@ -25,6 +27,8 @@ public string Type Constants.EventType.Create => EventType.Create, Constants.EventType.Member => EventType.Member, Constants.EventType.Message => EventType.Message, + Constants.EventType.Redaction => EventType.Redaction, + Constants.EventType.Reaction => EventType.Reaction, _ => EventType.Unknown }; } diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/EventType.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/EventType.cs index 7255235..05f3c79 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/EventType.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/EventType.cs @@ -5,6 +5,8 @@ public enum EventType Unknown, Create, Member, - Message + Message, + Redaction, + Reaction } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/BaseMessageContent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/BaseMessageContent.cs new file mode 100644 index 0000000..c3acf1d --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/BaseMessageContent.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room.Messaging +{ + public record BaseMessageContent() + { + /// + /// Required. The textual representation of this message. + /// + public string Body { get; init; } + + public MessageType MessageType { get; private set; } + + /// + /// Required. The type of message, e.g. m.image, m.text + /// + [JsonProperty("msgtype")] + public string Type + { + set => MessageType = value switch + { + Constants.MessageType.Text => MessageType.Text, + Constants.MessageType.Image => MessageType.Image, + _ => MessageType.Unknown + }; + } + } +} diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/ImageContent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/ImageContent.cs new file mode 100644 index 0000000..e46daac --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/ImageContent.cs @@ -0,0 +1,13 @@ +namespace Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room.Messaging +{ + using Newtonsoft.Json; + + public record ImageContent : BaseMessageContent + { + public string url; + + public record Info(int h, int w, int size, string mimetype); + + public Info info; + } +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/MessageContent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/MessageContent.cs index 6c068bd..8c54036 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/MessageContent.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/Messaging/MessageContent.cs @@ -5,29 +5,48 @@ namespace Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room.Messaging public enum MessageType { Text, + Image, Unknown } - public record MessageContent + public record MessageContent : BaseMessageContent { - /// - /// Required. The textual representation of this message. - /// - public string Body { get; init; } + public record RelatesTo + { + public string rel_type; + public string event_id; + public string key; + } - public MessageType MessageType { get; private set; } + [JsonProperty("m.relates_to")] + public RelatesTo relatesTo; - /// - /// Required. The type of message, e.g. m.image, m.text - /// - [JsonProperty("msgtype")] - public string Type + [JsonIgnore] + public string ReplacesEventId + { + get + { + if (relatesTo != null && relatesTo.rel_type == "m.replaces") + { + return relatesTo.event_id; + } + return string.Empty; + } + } + + [JsonIgnore] + public string CleanBody { - set => MessageType = value switch + get { - Constants.MessageType.Text => MessageType.Text, - _ => MessageType.Unknown - }; + if (relatesTo != null && relatesTo.rel_type == "m.replaces") + { + return Body.Substring(3); + } + return Body; + } } + + public string url; } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEvent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEventResponse.cs similarity index 79% rename from Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEvent.cs rename to Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEventResponse.cs index bdf829f..ac26fee 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEvent.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomEventResponse.cs @@ -1,13 +1,16 @@ +using System; +using Newtonsoft.Json; + namespace Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room { - using Newtonsoft.Json; - - public record RoomEvent : BaseEvent + public record RoomEventResponse : BaseEvent { /// /// Required. The globally unique event identifier. /// public string EventId { get; init; } + + public string RoomId { get; init; } /// /// Required. Contains the fully-qualified ID of the user who sent this event. @@ -21,6 +24,9 @@ public record RoomEvent : BaseEvent [JsonProperty("origin_server_ts")] public long OriginServerTimestamp { get; init; } + [JsonIgnore] + public DateTimeOffset Timestamp => DateTimeOffset.FromUnixTimeMilliseconds(OriginServerTimestamp).UtcDateTime; + // ReSharper disable once InvalidXmlDocComment /// /// Required. The ID of the room associated with this event. diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomStateEvent.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomStateEvent.cs index 635a891..768f07a 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomStateEvent.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/Event/Room/RoomStateEvent.cs @@ -1,6 +1,6 @@ namespace Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room { - public record RoomStateEvent : RoomEvent + public record RoomStateEvent : RoomEventResponse { /// /// Required. diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/SyncResponse.cs b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/SyncResponse.cs index a28d59d..2ca8671 100644 --- a/Matrix.Sdk/Core/Infrastructure/Dto/Sync/SyncResponse.cs +++ b/Matrix.Sdk/Core/Infrastructure/Dto/Sync/SyncResponse.cs @@ -24,7 +24,7 @@ public record Rooms /// /// The rooms that the user has joined, mapped as room ID to room information. /// - public Dictionary Join { get; init; } + public Dictionary Join { get; init; } /// /// The rooms that the user has been invited to, mapped as room ID to room information. @@ -34,19 +34,17 @@ public record Rooms /// /// The rooms that the user has left or been banned from, mapped as room ID to room information. /// - public Dictionary Leave { get; init; } + public Dictionary Leave { get; init; } } - public record JoinedRoom(Timeline Timeline, State State); + public record RoomResponse(Timeline Timeline, State State); public record InvitedRoom(InviteState InviteState); - public record LeftRoom(Timeline Timeline, State State); - /// /// The timeline of messages and state changes in the room. /// - public record Timeline(List Events); + public record Timeline(List Events); /// /// Updates to the state, between the time indicated by the since parameter, diff --git a/Matrix.Sdk/Core/Infrastructure/Dto/User/MatrixProfile.cs b/Matrix.Sdk/Core/Infrastructure/Dto/User/MatrixProfile.cs new file mode 100644 index 0000000..269ca6b --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Dto/User/MatrixProfile.cs @@ -0,0 +1,4 @@ +namespace System.Runtime.CompilerServices.Dto.User +{ + public record MatrixProfile(string avatar_url, string displayname); +} \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Extensions/HttpClientExtensions.cs b/Matrix.Sdk/Core/Infrastructure/Extensions/HttpClientExtensions.cs index f539445..04fb168 100644 --- a/Matrix.Sdk/Core/Infrastructure/Extensions/HttpClientExtensions.cs +++ b/Matrix.Sdk/Core/Infrastructure/Extensions/HttpClientExtensions.cs @@ -1,14 +1,14 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + namespace Matrix.Sdk.Core.Infrastructure.Extensions { - using System.Net.Http; - using System.Net.Http.Headers; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using Newtonsoft.Json; - using Newtonsoft.Json.Converters; - using Newtonsoft.Json.Serialization; - internal static class HttpClientExtensions { private static JsonSerializerSettings GetJsonSettings() @@ -71,17 +71,27 @@ public static async Task PutAsJsonAsync(this HttpClient ht var content = new StringContent(json, Encoding.Default, "application/json"); HttpResponseMessage response = await httpClient.PutAsync(requestUri, content, cancellationToken); + string result = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) + { throw new ApiException(response.RequestMessage.RequestUri, json, result, response.StatusCode); - + } + return JsonConvert.DeserializeObject(result, settings)!; } public static async Task GetAsJsonAsync(this HttpClient httpClient, string requestUri, CancellationToken cancellationToken) + { + var json = await httpClient.GetAsStringAsync(requestUri, cancellationToken); + return JsonConvert.DeserializeObject(json, GetJsonSettings())!; + } + + public static async Task GetAsStringAsync(this HttpClient httpClient, + string requestUri, CancellationToken cancellationToken) { HttpResponseMessage response = await httpClient.GetAsync(requestUri, cancellationToken);//.ConfigureAwait(false); string result = await response.Content.ReadAsStringAsync(); @@ -90,7 +100,20 @@ public static async Task GetAsJsonAsync(this HttpClient ht throw new ApiException(response.RequestMessage.RequestUri, null, result, response.StatusCode); - return JsonConvert.DeserializeObject(result, GetJsonSettings())!; + return result; + } + + public static async Task GetAsBytesAsync(this HttpClient httpClient, + string requestUri, CancellationToken cancellationToken) + { + HttpResponseMessage response = await httpClient.GetAsync(requestUri, cancellationToken); + byte[] result = await response.Content.ReadAsByteArrayAsync(); + + if (!response.IsSuccessStatusCode) + throw new ApiException(response.RequestMessage.RequestUri, + null, $"{result.Length} bytes", response.StatusCode); + + return result; } public static void AddBearerToken(this HttpClient httpClient, string bearer) => diff --git a/Matrix.Sdk/Core/Infrastructure/Services/BaseApiService.cs b/Matrix.Sdk/Core/Infrastructure/Services/BaseApiService.cs index c496bfb..0f60615 100644 --- a/Matrix.Sdk/Core/Infrastructure/Services/BaseApiService.cs +++ b/Matrix.Sdk/Core/Infrastructure/Services/BaseApiService.cs @@ -18,6 +18,7 @@ protected BaseApiService(IHttpClientFactory httpClientFactory) } protected virtual string ResourcePath => "_matrix/client/r0"; + protected virtual string MediaPath => "_matrix/media/r0"; /// diff --git a/Matrix.Sdk/Core/Infrastructure/Services/EventService.cs b/Matrix.Sdk/Core/Infrastructure/Services/EventService.cs index 95e08ce..4cead9b 100644 --- a/Matrix.Sdk/Core/Infrastructure/Services/EventService.cs +++ b/Matrix.Sdk/Core/Infrastructure/Services/EventService.cs @@ -1,4 +1,10 @@ -namespace Matrix.Sdk.Core.Infrastructure.Services +using System.Collections.Generic; +using System.Web; +using Matrix.Sdk.Core.Domain.RoomEvent; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room; +using Newtonsoft.Json; + +namespace Matrix.Sdk.Core.Infrastructure.Services { using System; using System.Net.Http; @@ -46,5 +52,145 @@ public async Task SendMessageAsync(string accessToken, return await httpClient.PutAsJsonAsync(path, model, cancellationToken); } + + public async Task SendImageAsync(string accessToken, + string roomId, string transactionId, string filename, + string mxcUrl, CancellationToken cancellationToken) + { + var model = new ImageMessageEvent(filename, mxcUrl); + + const string eventType = "m.room.message"; + + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/send/{eventType}/{transactionId}"; + + var response = await httpClient.PutAsJsonAsync(path, model, cancellationToken); + return response; + } + + public async Task EditMessageAsync(string accessToken, + string roomId, string transactionId, string eventId, + string message, CancellationToken cancellationToken) + { + const string eventType = "m.room.message"; + var model = new EditEvent(MessageType.Text, message, eventId); + + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/send/{eventType}/{transactionId}"; + + return await httpClient.PutAsJsonAsync(path, model, cancellationToken); + } + + public async Task SendTypingSignalAsync(string accessToken, + string roomId, string userId, TimeSpan timeout, CancellationToken cancellationToken) + { + var model = new TypingSignalEvent(true, (uint)timeout.TotalMilliseconds); + await SendTypingSignalAsync(accessToken, roomId, userId, model, cancellationToken); + } + public async Task SendTypingSignalAsync(string accessToken, + string roomId, string userId, bool isTyping, CancellationToken cancellationToken) + { + var model = new TypingSignalEvent(isTyping, 0); + await SendTypingSignalAsync(accessToken, roomId, userId, model, cancellationToken); + } + public async Task SendTypingSignalAsync(string accessToken, + string roomId, string userId, TypingSignalEvent typingEvent, CancellationToken cancellationToken) + { + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/typing/{userId}"; + await httpClient.PutAsJsonAsync(path, typingEvent, cancellationToken); + } + + private static readonly string RoomMessagesFilter = HttpUtility.UrlEncode( + JsonConvert.SerializeObject( + new Dictionary + { + { "lazy_load_members", true } + })); + + public struct RoomMessagesResponse + { + public struct Unsigned + { + public long age; + } + + public string start; + + public RoomEventResponse[] chunk; + public RoomEventResponse[] state; + + public string end; + } + + public async Task> GetTimelineEventsAsync(string accessToken, string roomId, string fromEventId, Func> stopCallback, CancellationToken cancellationToken) + { + var events = new List(); + + bool hasHitFromEvent = false; + if (fromEventId == null) hasHitFromEvent = true; + string fromToken = null; + while (true) + { + var messagesToPull = 1000; + + var path = $"{ResourcePath}/rooms/{roomId}/messages?limit={messagesToPull}&dir=b&filter={RoomMessagesFilter}"; + if (fromToken != null) + { + path += $"&from={fromToken}"; + } + + HttpClient httpClient = CreateHttpClient(accessToken); + var response = await httpClient.GetAsJsonAsync(path, cancellationToken); + + foreach (var roomEvent in response.chunk) + { + if (roomEvent.EventId == fromEventId) + { + hasHitFromEvent = true; + } + + if (!hasHitFromEvent) + { + continue; + } + + var ev = BaseRoomEvent.Create(roomEvent.RoomId, roomEvent); + if (ev != null) + { + events.Add(ev); + } + else + { + Console.WriteLine($"Unable to concretize event: {JsonConvert.SerializeObject(roomEvent, Formatting.Indented)}"); + } + + if (ev != null) + { + if (await stopCallback.Invoke(ev)) + { + return events; + } + } + } + + fromToken = response.end; + + if ((response.chunk == null || response.chunk.Length == 0) && (response.state == null || response.state.Length == 0)) + { + break; + } + } + return events; + } + + public async Task GetString(string accessToken, string url, CancellationToken cancellationToken) + { + HttpClient httpClient = CreateHttpClient(accessToken); + return await httpClient.GetAsStringAsync(url, cancellationToken); + } } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Services/MediaService.cs b/Matrix.Sdk/Core/Infrastructure/Services/MediaService.cs new file mode 100644 index 0000000..05d1054 --- /dev/null +++ b/Matrix.Sdk/Core/Infrastructure/Services/MediaService.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using Matrix.Sdk.Core.Infrastructure.Extensions; +using Newtonsoft.Json; + +namespace Matrix.Sdk.Core.Infrastructure.Services +{ + public class MediaService : BaseApiService + { + public MediaService(IHttpClientFactory httpClientFactory) : base(httpClientFactory) + { + } + + private struct UploadResponse + { + public string content_uri; + } + + public async Task UploadImage(string accessToken, string filename, byte[] imageData, CancellationToken cancellationToken) + { + var extension = Path.GetExtension(filename); + if (extension != ".png") + { + throw new Exception($"only png uploads are supported: {filename}"); + } + + string encodedFilename = HttpUtility.UrlEncode(filename); + string url = $"{MediaPath}/upload?filename={encodedFilename}"; + + HttpClient httpClient = CreateHttpClient(accessToken); + using (var content = new ByteArrayContent(imageData)) + { + content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); + content.Headers.ContentLength = imageData.Length; + + var response = await httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(json).content_uri; + } + } + + public async Task GetMedia(string accessToken, string mxcUrl, CancellationToken cancellationToken) + { + HttpClient httpClient = CreateHttpClient(accessToken); + + var validMxcStart = $"mxc://{httpClient.BaseAddress.Host}/"; + if (!mxcUrl.StartsWith(validMxcStart)) + throw new Exception($"Invalid mxc url: {mxcUrl}"); + + var mxcId = mxcUrl.Substring(validMxcStart.Length); + + var path = $"{MediaPath}/download/{httpClient.BaseAddress.Host}/{mxcId}"; + return await httpClient.GetAsBytesAsync(path, cancellationToken); + } + } +} diff --git a/Matrix.Sdk/Core/Infrastructure/Services/RoomService.cs b/Matrix.Sdk/Core/Infrastructure/Services/RoomService.cs index 61425dd..1b9e060 100644 --- a/Matrix.Sdk/Core/Infrastructure/Services/RoomService.cs +++ b/Matrix.Sdk/Core/Infrastructure/Services/RoomService.cs @@ -1,4 +1,9 @@ -namespace Matrix.Sdk.Core.Infrastructure.Services +using System.Collections.Generic; +using System.Runtime.CompilerServices.Dto.Room.Manage; +using Matrix.Sdk.Core.Infrastructure.Dto.Event; +using Newtonsoft.Json; + +namespace Matrix.Sdk.Core.Infrastructure.Services { using System; using System.Net.Http; @@ -43,14 +48,15 @@ public async Task JoinRoomAsync(string accessToken, string roo } - public async Task GetJoinedRoomsAsync(string accessToken, + public async Task> GetJoinedRoomsAsync(string accessToken, CancellationToken cancellationToken) { HttpClient httpClient = CreateHttpClient(accessToken); - + var path = $"{ResourcePath}/joined_rooms"; - - return await httpClient.GetAsJsonAsync(path, cancellationToken); + var json = await httpClient.GetAsStringAsync(path, cancellationToken); + var obj = JsonConvert.DeserializeObject(json); + return obj.JoinedRoomIds; } public async Task LeaveRoomAsync(string accessToken, string roomId, @@ -62,5 +68,60 @@ public async Task LeaveRoomAsync(string accessToken, string roomId, await httpClient.PostAsync(path, cancellationToken); } + + public record RoomNameResponse + { + public string name; + } + public async Task GetRoomNameAsync(string accessToken, string roomId, CancellationToken cancellationToken) + { + var path = $"{ResourcePath}/rooms/{roomId}/state/m.room.name/"; + HttpClient httpClient = CreateHttpClient(accessToken); + var json = await httpClient.GetAsStringAsync(path, cancellationToken); + var payload = JsonConvert.DeserializeObject(json); + return payload.name; + } + + public async Task SetTopicAsync(string accessToken, + string roomId, + string topic, CancellationToken cancellationToken) + { + const string eventType = "m.room.topic"; + var model = new ChangeTopicRequest(topic); + + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/state/{eventType}"; + + return await httpClient.PutAsJsonAsync(path, model, cancellationToken); + } + + public async Task SetNameAsync(string accessToken, + string roomId, + string name, CancellationToken cancellationToken) + { + const string eventType = "m.room.name"; + var model = new ChangeNameRequest(name); + + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/state/{eventType}"; + + return await httpClient.PutAsJsonAsync(path, model, cancellationToken); + } + + public async Task SetAvatarAsync(string accessToken, + string roomId, + string url, CancellationToken cancellationToken) + { + const string eventType = "m.room.avatar"; + var model = new ChangeAvatarRequest(url); + + HttpClient httpClient = CreateHttpClient(accessToken); + + var path = $"{ResourcePath}/rooms/{roomId}/state/{eventType}"; + + return await httpClient.PutAsJsonAsync(path, model, cancellationToken); + } } } \ No newline at end of file diff --git a/Matrix.Sdk/Core/Infrastructure/Services/UserService.cs b/Matrix.Sdk/Core/Infrastructure/Services/UserService.cs index 3c80d84..89fb830 100644 --- a/Matrix.Sdk/Core/Infrastructure/Services/UserService.cs +++ b/Matrix.Sdk/Core/Infrastructure/Services/UserService.cs @@ -1,5 +1,9 @@ // ReSharper disable ArgumentsStyleNamedExpression +using System.Runtime.CompilerServices.Dto.User; +using System.Text; +using System.Web; + namespace Matrix.Sdk.Core.Infrastructure.Services { using System.Net.Http; @@ -35,5 +39,12 @@ public async Task LoginAsync(string user, string password, string return await httpClient.PostAsJsonAsync(path, model, cancellationToken); } + + public async Task GetProfile(string accessToken, string userId, CancellationToken cancellationToken) + { + HttpClient httpClient = CreateHttpClient(accessToken); + var path = $"{ResourcePath}/profile/{HttpUtility.HtmlEncode($"@{userId}:{httpClient.BaseAddress.Host}")}"; + return await httpClient.GetAsJsonAsync(path, cancellationToken); + } } } \ No newline at end of file diff --git a/Matrix.Sdk/IMatrixClient.cs b/Matrix.Sdk/IMatrixClient.cs index c1630e4..f471e86 100644 --- a/Matrix.Sdk/IMatrixClient.cs +++ b/Matrix.Sdk/IMatrixClient.cs @@ -1,9 +1,16 @@ +using System.Runtime.CompilerServices.Dto.User; +using Matrix.Sdk.Core.Domain.RoomEvent; +using Matrix.Sdk.Core.Infrastructure.Dto.Event; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync; +using Matrix.Sdk.Core.Infrastructure.Dto.Sync.Event.Room; +using Matrix.Sdk.Core.Infrastructure.Services; + namespace Matrix.Sdk { using System; using System.Collections.Generic; using System.Threading.Tasks; - using Core.Domain.MatrixRoom; + using Core.Domain.Room; using Core.Infrastructure.Dto.Room.Create; using Core.Infrastructure.Dto.Room.Join; @@ -39,9 +46,25 @@ public interface IMatrixClient Task JoinTrustedPrivateRoomAsync(string roomId); Task SendMessageAsync(string roomId, string message); + Task SendImageAsync(string roomId, string filename, byte[] imageData); Task> GetJoinedRoomsIdsAsync(); Task LeaveRoomAsync(string roomId); + + Task> GetHistory(string roomId, Func> stopCallback); + Task> GetHistory(string roomId, string fromEventId, Func> stopCallback); + + Task EditMessage(string roomId, string messageId, string newText); + Task SendTypingSignal(string roomId, TimeSpan timeout); + Task SendTypingSignal(string roomId, bool isTyping); + + Task GetString(string url); + Task GetRoomName(string id); + Task SetRoomTopicAsync(string roomId, string topic); + Task SetRoomNameAsync(string roomId, string name); + Task SetRoomAvatarAsync(string roomId, string url); + Task GetUserProfile(string fullUserId); + Task GetMxcImage(string mxcUrl); } } \ No newline at end of file diff --git a/Matrix.Sdk/Matrix.Sdk.csproj b/Matrix.Sdk/Matrix.Sdk.csproj index e95d297..e5b9423 100644 --- a/Matrix.Sdk/Matrix.Sdk.csproj +++ b/Matrix.Sdk/Matrix.Sdk.csproj @@ -14,13 +14,14 @@ Mikhail Tatarenko Matrix.Sdk This open-source library allows you to build .NET apps compatible with Matrix Protocol - http://www.matrix.org. - 1.0.8 - Copyright © Baking Bad 2019-2023 + 1.0.7 + Copyright © Baking Bad 2019-2022 enable netstandard2.0 + diff --git a/Matrix.Sdk/MatrixClient.cs b/Matrix.Sdk/MatrixClient.cs index 988d731..53d1be8 100644 --- a/Matrix.Sdk/MatrixClient.cs +++ b/Matrix.Sdk/MatrixClient.cs @@ -1,11 +1,14 @@ -namespace Matrix.Sdk +using System.Runtime.CompilerServices.Dto.User; +using Matrix.Sdk.Core.Domain.RoomEvent; + +namespace Matrix.Sdk { using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Core.Domain; - using Core.Domain.MatrixRoom; + using Core.Domain.Room; using Core.Domain.Services; using Core.Infrastructure.Dto.Event; using Core.Infrastructure.Dto.Login; @@ -25,6 +28,7 @@ public class MatrixClient : IMatrixClient private readonly UserService _userService; private readonly RoomService _roomService; private readonly EventService _eventService; + private readonly MediaService _mediaService; private string? _accessToken; @@ -34,12 +38,14 @@ public MatrixClient( IPollingService pollingService, UserService userService, RoomService roomService, - EventService eventService) + EventService eventService, + MediaService mediaService) { _pollingService = pollingService; _userService = userService; _roomService = roomService; _eventService = eventService; + _mediaService = mediaService; } public event EventHandler OnMatrixRoomEventsReceived; @@ -63,6 +69,7 @@ public async Task LoginAsync(Uri baseAddress, string user, string password, stri _userService.BaseAddress = baseAddress; _roomService.BaseAddress = baseAddress; _eventService.BaseAddress = baseAddress; + _mediaService.BaseAddress = baseAddress; BaseAddress = baseAddress; LoginResponse response = await _userService.LoginAsync(user, password, deviceId, _cts.Token); @@ -119,25 +126,66 @@ public async Task SendMessageAsync(string roomId, string message) return eventResponse.EventId; } + + public async Task SendImageAsync(string roomId, string filename, byte[] imageData) + { + string transactionId = CreateTransactionId(); + + var mxcUrl = await _mediaService.UploadImage(_accessToken!, filename, imageData, _cts.Token); + + EventResponse eventResponse = await _eventService.SendImageAsync(_accessToken!, + roomId, transactionId, filename, mxcUrl, _cts.Token); - public async Task> GetJoinedRoomsIdsAsync() + if (eventResponse.EventId == null) + throw new NullReferenceException(nameof(eventResponse.EventId)); + + return eventResponse.EventId; + } + + public async Task GetString(string url) { - JoinedRoomsResponse response = - await _roomService.GetJoinedRoomsAsync(_accessToken!, _cts.Token); + return await _eventService.GetString(_accessToken!, url, _cts.Token); + } - return response.JoinedRoomIds; + public async Task> GetJoinedRoomsIdsAsync() + { + return await _roomService.GetJoinedRoomsAsync(_accessToken!, _cts.Token); } public async Task LeaveRoomAsync(string roomId) => await _roomService.LeaveRoomAsync(_accessToken!, roomId, _cts.Token); + public async Task> GetHistory(string roomId, + Func> stopCallback) + { + return await GetHistory(roomId, null, stopCallback); + } + + public async Task> GetHistory(string roomId, string fromEventId, Func> stopCallback) + { + return await _eventService.GetTimelineEventsAsync(_accessToken!, roomId, null, stopCallback, _cts.Token); + } + + public async Task EditMessage(string roomId, string messageId, string newText) + { + string transactionId = CreateTransactionId(); + + EventResponse eventResponse = await _eventService.EditMessageAsync(_accessToken!, + roomId, transactionId, messageId, newText, _cts.Token); + + if (eventResponse.EventId == null) + throw new NullReferenceException(nameof(eventResponse.EventId)); + + return eventResponse.EventId; + } + private void OnSyncBatchReceived(object? sender, SyncBatchEventArgs syncBatchEventArgs) { if (sender is not IPollingService) throw new ArgumentException("sender is not polling service"); SyncBatch batch = syncBatchEventArgs.SyncBatch; - + OnMatrixRoomEventsReceived.Invoke(this, new MatrixRoomEventsEventArgs(batch.MatrixRoomEvents, batch.NextBatch)); } @@ -150,5 +198,45 @@ private string CreateTransactionId() return $"m{timestamp}.{counter}"; } + + public async Task SendTypingSignal(string roomId, TimeSpan timeout) + { + await _eventService.SendTypingSignalAsync(_accessToken!, roomId, UserId, timeout, _cts.Token); + } + + public async Task SendTypingSignal(string roomId, bool isTyping) + { + await _eventService.SendTypingSignalAsync(_accessToken!, roomId, UserId, isTyping, _cts.Token); + } + + public async Task GetRoomName(string roomId) + { + return await _roomService.GetRoomNameAsync(_accessToken!, roomId, _cts.Token); + } + + public async Task SetRoomTopicAsync(string roomId, string topic) + { + var transactionId = CreateTransactionId(); + return await _roomService.SetTopicAsync(_accessToken!, roomId, topic, _cts.Token); + } + public async Task SetRoomAvatarAsync(string roomId, string url) + { + var transactionId = CreateTransactionId(); + return await _roomService.SetAvatarAsync(_accessToken!, roomId, url, _cts.Token); + } + public async Task SetRoomNameAsync(string roomId, string name) + { + var transactionId = CreateTransactionId(); + return await _roomService.SetNameAsync(_accessToken!, roomId, name, _cts.Token); + } + public async Task GetUserProfile(string userId) + { + return await _userService.GetProfile(_accessToken!, userId, _cts.Token); + } + + public async Task GetMxcImage(string mxcUrl) + { + return await _mediaService.GetMedia(_accessToken!, mxcUrl, _cts.Token); + } } } \ No newline at end of file diff --git a/Matrix.Sdk/MatrixClientFactory.cs b/Matrix.Sdk/MatrixClientFactory.cs index b8c078f..7f8081d 100644 --- a/Matrix.Sdk/MatrixClientFactory.cs +++ b/Matrix.Sdk/MatrixClientFactory.cs @@ -33,13 +33,15 @@ public IMatrixClient Create(ILogger? logger = null) var eventService = new EventService(_httpClientFactory); var userService = new UserService(_httpClientFactory); var roomService = new RoomService(_httpClientFactory); + var mediaService = new MediaService(_httpClientFactory); var pollingService = new PollingService(eventService, logger); _client = new MatrixClient( pollingService, userService, roomService, - eventService); + eventService, + mediaService); return _client; } diff --git a/Matrix.Sdk/MatrixClientServiceExtensions.cs b/Matrix.Sdk/MatrixClientServiceExtensions.cs index f405649..b6de2f3 100644 --- a/Matrix.Sdk/MatrixClientServiceExtensions.cs +++ b/Matrix.Sdk/MatrixClientServiceExtensions.cs @@ -20,7 +20,7 @@ public static IServiceCollection AddMatrixClient(this IServiceCollection service services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - + services.AddSingleton(); services.AddTransient(); services.AddTransient();