Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,88 @@ namespace Remora.Discord.API.Abstractions.Rest;
[PublicAPI]
public interface IDiscordRestEmojiAPI
{
/// <summary>
/// Gets a list of emojis for the given application.
/// </summary>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IReadOnlyList<IEmoji>>> ListApplicationEmojisAync
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aync

(
Snowflake applicationID,
CancellationToken ct = default
);

/// <summary>
/// Gets the emoji on the given application with the given ID.
/// </summary>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="emojiID">The ID of the emoji.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IEmoji>> GetApplicationEmojiAsync
(
Snowflake applicationID,
Snowflake emojiID,
CancellationToken ct = default
);

/// <summary>
/// Creates a new emoji for the given application with the given parameters.
/// </summary>
/// <remarks>
/// Any streams passed to this method will be disposed of at the end of the call. If you want to reuse the streams
/// afterwards, ensure that what you pass is a copy that the method can take ownership of.
/// </remarks>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="name">The name of the new emoji.</param>
/// <param name="image">The image data.</param>
/// <param name="reason">The reason to mark the action in the audit log with.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A creation result which may or may not have succeeded.</returns>
Task<Result<IEmoji>> CreateApplicationEmojiAsync
(
Snowflake applicationID,
string name,
Stream image,
Optional<string> reason = default,
CancellationToken ct = default
);

/// <summary>
/// Modifies the given emoji.
/// </summary>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="emojiID">The ID of the emoji.</param>
/// <param name="name">The new name of the emoji.</param>
/// <param name="reason">The reason to mark the action in the audit log with.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A modification result which may or may not have succeeded.</returns>
Task<Result<IEmoji>> ModifyApplicationEmojiAsync
(
Snowflake applicationID,
Snowflake emojiID,
Optional<string> name = default,
Optional<string> reason = default,
CancellationToken ct = default
);

/// <summary>
/// Deletes the given emoji.
/// </summary>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="emojiID">The ID of the emoji.</param>
/// <param name="reason">The reason to mark the action in the audit log with.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A deletion result which may or may not have succeeded.</returns>
Task<Result> DeleteApplicationEmojiAsync
(
Snowflake applicationID,
Snowflake emojiID,
Optional<string> reason = default,
CancellationToken ct = default
);

/// <summary>
/// Gets a list of emojis for the given guild.
/// </summary>
Expand All @@ -56,8 +138,7 @@ Task<Result<IReadOnlyList<IEmoji>>> ListGuildEmojisAsync
/// <param name="emojiID">The ID of the emoji.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IEmoji>> GetGuildEmojiAsync
(
Task<Result<IEmoji>> GetGuildEmojiAsync(
Comment on lines -59 to +141
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably an error with auto-formatting?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think my Rider did that automatically

Snowflake guildID,
Snowflake emojiID,
CancellationToken ct = default
Expand Down
109 changes: 109 additions & 0 deletions Backend/Remora.Discord.Rest/API/Emoji/DiscordRestEmojiAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,115 @@ ICacheProvider rateLimitCache
{
}

/// <inheritdoc />
public Task<Result<IReadOnlyList<IEmoji>>> ListApplicationEmojisAync
(
Snowflake applicationID,
CancellationToken ct = default
)
{
return this.RestHttpClient.GetAsync<IReadOnlyList<IEmoji>>
(
$"applications/{applicationID}/emojis",
"items",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by no means your fault - it's Discord's, somewhat, but this fails with an exception when the app doesn't have any emojis.

The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true.

The returned JSON is in fact, correct: {"items":[]}, but STJ just...dies.

b => b.WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual Task<Result<IEmoji>> GetApplicationEmojiAsync
(
Snowflake applicationID,
Snowflake emojiID,
CancellationToken ct = default
)
{
return this.RestHttpClient.GetAsync<IEmoji>
(
$"applications/{applicationID}/emojis/{emojiID}",
b => b.WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual async Task<Result<IEmoji>> CreateApplicationEmojiAsync
(
Snowflake applicationID,
string name,
Stream image,
Optional<string> reason = default,
CancellationToken ct = default
)
{
if (image.Length > 256000)
{
return new NotSupportedError("Image too large (max 256k).");
}

var packImage = await ImagePacker.PackImageAsync(image, ct);
if (!packImage.IsSuccess)
{
return Result<IEmoji>.FromError(packImage);
}

var emojiData = packImage.Entity;

return await this.RestHttpClient.PostAsync<IEmoji>
(
$"applications/{applicationID}/emojis",
b => b
.AddAuditLogReason(reason)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating application emojis doesn't support the X-AuditLog-Reason header

.WithJson
(
json =>
{
json.WriteString("name", name);
json.WriteString("image", emojiData);
}
)
.WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual Task<Result<IEmoji>> ModifyApplicationEmojiAsync
(
Snowflake applicationID,
Snowflake emojiID,
Optional<string> name = default,
Optional<string> reason = default,
CancellationToken ct = default
)
{
return this.RestHttpClient.PatchAsync<IEmoji>
(
$"applications/{applicationID}/emojis/{emojiID}",
b => b
.AddAuditLogReason(reason)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

.WithJson(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: allman paranthesis (open and closing should be on their on lines)

json =>
{
json.Write("name", name, this.JsonOptions);
}
).WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual Task<Result> DeleteApplicationEmojiAsync(Snowflake applicationID, Snowflake emojiID, Optional<string> reason = default, CancellationToken ct = default)
{
return this.RestHttpClient.DeleteAsync
(
$"applications/{applicationID}/emojis/{emojiID}",
b => b.AddAuditLogReason(reason).WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual Task<Result<IReadOnlyList<IEmoji>>> ListGuildEmojisAsync
(
Expand Down