Skip to content

Commit

Permalink
More Chat APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
drasticactions committed May 23, 2024
1 parent b30889c commit e4c102b
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 23 deletions.
105 changes: 82 additions & 23 deletions src/FishyFlip/BlueskyChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ namespace FishyFlip;
/// </summary>
public sealed class BlueskyChat
{
private ATProtocol proto;
private readonly ATProtocol proto;
private readonly Dictionary<string, string> chatHeaders;

/// <summary>
/// Initializes a new instance of the <see cref="BlueskyChat"/> class.
Expand All @@ -18,6 +19,10 @@ public sealed class BlueskyChat
internal BlueskyChat(ATProtocol proto)
{
this.proto = proto;
this.chatHeaders = new Dictionary<string, string>
{
{ Constants.ATProtoProxy.Proxy, Constants.ATProtoProxy.BskyChat },
};
}

private ATProtocolOptions Options => this.proto.Options;
Expand Down Expand Up @@ -46,12 +51,7 @@ internal BlueskyChat(ATProtocol proto)
url += $"&limit={limit}";
}

var headers = new Dictionary<string, string>
{
{ Constants.ATProtoProxy.Proxy, Constants.ATProtoProxy.BskyChat },
};

return this.Client.Get<ConversationMessages>(url, this.Options.SourceGenerationContext.ConversationMessages, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, headers);
return this.Client.Get<ConversationMessages>(url, this.Options.SourceGenerationContext.ConversationMessages, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
Expand All @@ -70,12 +70,7 @@ internal BlueskyChat(ATProtocol proto)
url += $"&cursor={cursor}";
}

var headers = new Dictionary<string, string>
{
{ Constants.ATProtoProxy.Proxy, Constants.ATProtoProxy.BskyChat },
};

return this.Client.Get<ConversationList>(url, this.Options.SourceGenerationContext.ConversationList, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, headers);
return this.Client.Get<ConversationList>(url, this.Options.SourceGenerationContext.ConversationList, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
Expand All @@ -87,12 +82,20 @@ internal BlueskyChat(ATProtocol proto)
public Task<Result<ConversationView?>> GetConversationAsync(string convoId, CancellationToken cancellationToken = default)
{
var url = $"{Constants.Urls.Bluesky.Chat.Convo.GetConvo}?convoId={convoId}";
var headers = new Dictionary<string, string>
{
{ Constants.ATProtoProxy.Proxy, Constants.ATProtoProxy.BskyChat },
};
return this.Client.Get<ConversationView>(url, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, this.chatHeaders);
}

return this.Client.Get<ConversationView>(url, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, headers);
/// <summary>
/// Retrieves a conversation for members asynchronously.
/// </summary>
/// <param name="members">The ATDid of the member of the conversation.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{Conversation?}"/> that encapsulates the result of the operation.</returns>
public Task<Result<ConversationView?>> GetConversationForMembersAsync(ATDid[] members, CancellationToken cancellationToken = default)
{
var commaSeparatedMembers = string.Join(",", members.Select(x => x.ToString()));
var url = $"{Constants.Urls.Bluesky.Chat.Convo.GetConvoForMembers}?members={commaSeparatedMembers}";
return this.Client.Get<ConversationView>(url, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
Expand All @@ -106,11 +109,67 @@ public Task<Result<MessageView>> SendMessageAsync(string convoId, string message
{
var url = $"{Constants.Urls.Bluesky.Chat.Convo.SendMessage}";
var createMessage = new CreateMessage(convoId, new CreateMessageMessage(message));
var headers = new Dictionary<string, string>
{
{ Constants.ATProtoProxy.Proxy, Constants.ATProtoProxy.BskyChat },
};

return this.Client.Post<CreateMessage, MessageView>(url, this.Options.SourceGenerationContext.CreateMessage, this.Options.SourceGenerationContext.MessageView, this.Options.JsonSerializerOptions, createMessage, cancellationToken, this.Options.Logger, headers);
return this.Client.Post<CreateMessage, MessageView>(url, this.Options.SourceGenerationContext.CreateMessage, this.Options.SourceGenerationContext.MessageView, this.Options.JsonSerializerOptions, createMessage, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
/// Update the read status of a conversation asynchronously.
/// </summary>
/// <param name="convoId">The unique identifier of the conversation.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{Conversation?}"/> that encapsulates the result of the operation.</returns>
public Task<Result<ConversationView>> UpdateReadAsync(string convoId, CancellationToken cancellationToken = default)
{
var updateRead = new UpdateRead(convoId);
return this.Client.Post<UpdateRead, ConversationView>(Constants.Urls.Bluesky.Chat.Convo.UpdateRead, this.Options.SourceGenerationContext.UpdateRead, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, updateRead, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
/// Mute a conversation asynchronously.
/// </summary>
/// <param name="convoId">The unique identifier of the conversation.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{Conversation?}"/> that encapsulates the result of the operation.</returns>
public Task<Result<ConversationView>> MuteConvoAsync(string convoId, CancellationToken cancellationToken = default)
{
var updateRead = new UpdateRead(convoId);
return this.Client.Post<UpdateRead, ConversationView>(Constants.Urls.Bluesky.Chat.Convo.MuteConvo, this.Options.SourceGenerationContext.UpdateRead, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, updateRead, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
/// Unmute a conversation asynchronously.
/// </summary>
/// <param name="convoId">The unique identifier of the conversation.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{Conversation?}"/> that encapsulates the result of the operation.</returns>
public Task<Result<ConversationView>> UnmuteConvoAsync(string convoId, CancellationToken cancellationToken = default)
{
var updateRead = new UpdateRead(convoId);
return this.Client.Post<UpdateRead, ConversationView>(Constants.Urls.Bluesky.Chat.Convo.UnmuteConvo, this.Options.SourceGenerationContext.UpdateRead, this.Options.SourceGenerationContext.ConversationView, this.Options.JsonSerializerOptions, updateRead, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
/// Retrieves the log asynchronously.
/// </summary>
/// <param name="cursor">The cursor for pagination in the log.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{LogResponse?}"/> that encapsulates the result of the operation.</returns>
public Task<Result<LogResponse?>> GetLogAsync(string cursor, CancellationToken cancellationToken = default)
{
var url = $"{Constants.Urls.Bluesky.Chat.Convo.GetLog}?cursor={cursor}";
return this.Client.Get<LogResponse>(url, this.Options.SourceGenerationContext.LogResponse, this.Options.JsonSerializerOptions, cancellationToken, this.Options.Logger, this.chatHeaders);
}

/// <summary>
/// Leaves a conversation asynchronously.
/// </summary>
/// <param name="convoId">The unique identifier of the conversation.</param>
/// <param name="cancellationToken">An optional token to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Result{LeaveConvoResponse}"/> that encapsulates the result of the operation.</returns>
public Task<Result<LeaveConvoResponse>> LeaveConvoAsync(string convoId, CancellationToken cancellationToken = default)
{
var updateRead = new UpdateRead(convoId);
return this.Client.Post<UpdateRead, LeaveConvoResponse>(Constants.Urls.Bluesky.Chat.Convo.LeaveConvo, this.Options.SourceGenerationContext.UpdateRead, this.Options.SourceGenerationContext.LeaveConvoResponse, this.Options.JsonSerializerOptions, updateRead, cancellationToken, this.Options.Logger, this.chatHeaders);
}
}
5 changes: 5 additions & 0 deletions src/FishyFlip/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ public static class ActorTypes
public static class ConversationTypes
{
public const string MessageView = "chat.bsky.convo.defs#messageView";
public const string DeletedMessageView = "chat.bsky.convo.defs#deletedMessageView";
public const string LogBeginConvo = "chat.bsky.convo.defs#logBeginConvo";
public const string LogLeaveConvo = "chat.bsky.convo.defs#logLeaveConvo";
public const string LogCreateMessage = "chat.bsky.convo.defs#logCreateMessage";
public const string LogDeleteMessage = "chat.bsky.convo.defs#logDeleteMessage";
}

public static class DeclarationTypes
Expand Down
50 changes: 50 additions & 0 deletions src/FishyFlip/Models/DeletedMessageView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// <copyright file="DeletedMessageView.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models;

/// <summary>
/// Represents a view of a message in a chat conversation.
/// </summary>
public class DeletedMessageView : ATRecord
{
/// <summary>
/// Initializes a new instance of the <see cref="DeletedMessageView"/> class.
/// </summary>
/// <param name="id">The unique identifier of the message.</param>
/// <param name="rev">The revision of the message.</param>
/// <param name="sender">The sender of the message.</param>
/// <param name="sentAt">The date and time when the message was sent.</param>
/// <param name="type">The type of the message. If not provided, defaults to <see cref="Constants.ConversationTypes.DeletedMessageView"/>.</param>
[JsonConstructor]
public DeletedMessageView(string id, string rev, ChatSender sender, DateTime sentAt, string? type = default)
: base(type)
{
this.Id = id;
this.Rev = rev;
this.Sender = sender;
this.SentAt = sentAt;
this.Type = type ?? Constants.ConversationTypes.DeletedMessageView;
}

/// <summary>
/// Gets the unique identifier of the message.
/// </summary>
public string Id { get; }

/// <summary>
/// Gets the revision of the message.
/// </summary>
public string Rev { get; }

/// <summary>
/// Gets the sender of the message.
/// </summary>
public ChatSender Sender { get; }

/// <summary>
/// Gets the date and time when the message was sent.
/// </summary>
public DateTime SentAt { get; }
}
11 changes: 11 additions & 0 deletions src/FishyFlip/Models/Internal/UpdateRead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// <copyright file="UpdateRead.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models.Internal;

/// <summary>
/// Update Conversation Read.
/// </summary>
/// <param name="ConvoId">Conversation Id.</param>
public record UpdateRead(string ConvoId);
12 changes: 12 additions & 0 deletions src/FishyFlip/Models/LeaveConvoResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// <copyright file="LeaveConvoResponse.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models;

/// <summary>
/// Response to leaving a conversation.
/// </summary>
/// <param name="ConvoId">Conversation id.</param>
/// <param name="Rev">Rev Id.</param>
public record LeaveConvoResponse(string ConvoId, string Rev);
35 changes: 35 additions & 0 deletions src/FishyFlip/Models/LogBeginConvo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <copyright file="LogBeginConvo.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models;

/// <summary>
/// Represents a log begin conversation message.
/// </summary>
public class LogBeginConvo : ATRecord
{
/// <summary>
/// Initializes a new instance of the <see cref="LogBeginConvo"/> class.
/// </summary>
/// <param name="convoId">The conversation ID.</param>
/// <param name="rev">The revision.</param>
/// <param name="type">The type of the record. Optional.</param>
public LogBeginConvo(string convoId, string rev, string? type = default)
: base(type)
{
this.ConvoId = convoId;
this.Rev = rev;
this.Type = type ?? Constants.ConversationTypes.LogBeginConvo;
}

/// <summary>
/// Gets the conversation ID.
/// </summary>
public string ConvoId { get; }

/// <summary>
/// Gets the revision.
/// </summary>
public string Rev { get; }
}
42 changes: 42 additions & 0 deletions src/FishyFlip/Models/LogCreateMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// <copyright file="LogCreateMessage.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models;

/// <summary>
/// Represents a log creation message.
/// </summary>
public class LogCreateMessage : ATRecord
{
/// <summary>
/// Initializes a new instance of the <see cref="LogCreateMessage"/> class.
/// </summary>
/// <param name="convoId">The conversation ID.</param>
/// <param name="message">The message view.</param>
/// <param name="rev">The revision.</param>
/// <param name="type">The type of the record. Optional.</param>
public LogCreateMessage(string convoId, MessageView message, string rev, string? type = default)
: base(type)
{
this.ConvoId = convoId;
this.Message = message;
this.Rev = rev;
this.Type = type ?? Constants.ConversationTypes.LogCreateMessage;
}

/// <summary>
/// Gets the conversation ID.
/// </summary>
public string ConvoId { get; }

/// <summary>
/// Gets the message view.
/// </summary>
public MessageView Message { get; }

/// <summary>
/// Gets the revision.
/// </summary>
public string Rev { get; }
}
42 changes: 42 additions & 0 deletions src/FishyFlip/Models/LogDeleteMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// <copyright file="LogDeleteMessage.cs" company="Drastic Actions">
// Copyright (c) Drastic Actions. All rights reserved.
// </copyright>

namespace FishyFlip.Models;

/// <summary>
/// Represents a log delete message.
/// </summary>
public class LogDeleteMessage : ATRecord
{
/// <summary>
/// Initializes a new instance of the <see cref="LogDeleteMessage"/> class.
/// </summary>
/// <param name="convoId">The conversation ID.</param>
/// <param name="message">The message view.</param>
/// <param name="rev">The revision.</param>
/// <param name="type">The type of the record. Optional.</param>
public LogDeleteMessage(string convoId, DeletedMessageView message, string rev, string? type = default)
: base(type)
{
this.ConvoId = convoId;
this.Message = message;
this.Rev = rev;
this.Type = type ?? Constants.ConversationTypes.LogDeleteMessage;
}

/// <summary>
/// Gets the conversation ID.
/// </summary>
public string ConvoId { get; }

/// <summary>
/// Gets the message view.
/// </summary>
public DeletedMessageView Message { get; }

/// <summary>
/// Gets the revision.
/// </summary>
public string Rev { get; }
}
Loading

0 comments on commit e4c102b

Please sign in to comment.