Browse Source

Stickers pt2

pull/1923/head
quin lynch 3 years ago
parent
commit
9b5a5e498c
25 changed files with 1185 additions and 172 deletions
  1. +280
    -70
      src/Discord.Net.Core/Discord.Net.Core.xml
  2. +4
    -3
      src/Discord.Net.Core/Entities/Messages/IMessage.cs
  3. +2
    -2
      src/Discord.Net.Core/Entities/Stickers/ISticker.cs
  4. +29
    -0
      src/Discord.Net.Core/Entities/Stickers/IStickerItem.cs
  5. +63
    -0
      src/Discord.Net.Core/Entities/Stickers/StickerPack.cs
  6. +2
    -0
      src/Discord.Net.Rest/API/Common/Sticker.cs
  7. +117
    -27
      src/Discord.Net.Rest/Discord.Net.Rest.xml
  8. +4
    -4
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  9. +111
    -0
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  10. +73
    -0
      src/Discord.Net.Rest/Entities/Messages/CustomSticker.cs
  11. +2
    -2
      src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
  12. +7
    -7
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  13. +28
    -24
      src/Discord.Net.Rest/Entities/Messages/Sticker.cs
  14. +9
    -10
      src/Discord.Net.Rest/Entities/Messages/StickerItem.cs
  15. +14
    -0
      src/Discord.Net.WebSocket/BaseSocketClient.cs
  16. +1
    -1
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj
  17. +173
    -0
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
  18. +68
    -0
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  19. +83
    -0
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  20. +11
    -1
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  21. +2
    -2
      src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
  22. +24
    -7
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
  23. +2
    -2
      src/Discord.Net.WebSocket/Entities/Stickers/SocketCustomSticker.cs
  24. +16
    -10
      src/Discord.Net.WebSocket/Entities/Stickers/SocketSticker.cs
  25. +60
    -0
      src/Discord.Net.WebSocket/Entities/Stickers/SocketUnknownSticker.cs

+ 280
- 70
src/Discord.Net.Core/Discord.Net.Core.xml View File

@@ -212,6 +212,16 @@
A URL pointing to the Spotify track.
</returns>
</member>
<member name="M:Discord.CDN.GetStickerUrl(System.UInt64,Discord.StickerFormatType)">
<summary>
Gets a stickers url based off the id and format.
</summary>
<param name="stickerId">The id of the sticker.</param>
<param name="format">The format of the sticker</param>
<returns>
A URL to the sticker.
</returns>
</member>
<member name="T:Discord.Commands.ICommandContext">
<summary>
Represents a context of a command. This may include the client, guild, channel, user, and message.
@@ -3121,6 +3131,14 @@
A read-only collection of all custom emotes for this guild.
</returns>
</member>
<member name="P:Discord.IGuild.Stickers">
<summary>
Gets a collection of all custom stickers for this guild.
</summary>
<returns>
A read-only collection of all custom stickers for this guild.
</returns>
</member>
<member name="P:Discord.IGuild.Features">
<summary>
Gets a collection of all extra features added to this guild.
@@ -3914,6 +3932,52 @@
A task that represents the asynchronous removal operation.
</returns>
</member>
<member name="M:Discord.IGuild.CreateStickerAsync(System.String,System.String,System.Collections.Generic.IEnumerable{System.String},Discord.Image,Discord.RequestOptions)">
<summary>
Creates a new sticker in this guild.
</summary>
<param name="name">The name of the sticker.</param>
<param name="description">The description of the sticker.</param>
<param name="tags">The tags of the sticker.</param>
<param name="image">The image of the new emote.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous creation operation. The task result contains the created sticker.
</returns>
</member>
<member name="M:Discord.IGuild.GetStickerAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets a specific sticker within this guild.
</summary>
<param name="id">The id of the sticker to get.</param>
<param name="mode">The <see cref="T:Discord.CacheMode" /> that determines whether the object should be fetched from cache.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains the sticker found with the
specified <paramref name="id"/>; <see langword="null" /> if none is found.
</returns>
</member>
<member name="M:Discord.IGuild.GetStickersAsync(Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets a collection of all stickers within this guild.
</summary>
<param name="mode">The <see cref="T:Discord.CacheMode" /> that determines whether the object should be fetched from cache.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains a read-only collection
of stickers found within the guild.
</returns>
</member>
<member name="M:Discord.IGuild.DeleteStickerAsync(Discord.ICustomSticker,Discord.RequestOptions)">
<summary>
Deletes a sticker within this guild.
</summary>
<param name="sticker">The sticker to delete.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous removal operation.
</returns>
</member>
<member name="M:Discord.IGuild.GetApplicationCommandsAsync(Discord.RequestOptions)">
<summary>
Gets this guilds slash commands commands
@@ -7282,7 +7346,14 @@
The <see cref="T:Discord.IMessageComponent"/>'s attached to this message
</summary>
</member>
<!-- Badly formed XML comment ignored for member "P:Discord.IMessage.Stickers" -->
<member name="P:Discord.IMessage.Stickers">
<summary>
Gets all stickers items included in this message.
</summary>
<returns>
A read-only collection of sticker item objects.
</returns>
</member>
<member name="P:Discord.IMessage.Flags">
<summary>
Gets the flags related to this message.
@@ -7411,75 +7482,6 @@
The <see cref="T:Discord.IEmote" /> used in the reaction.
</summary>
</member>
<member name="T:Discord.ISticker">
<summary>
Represents a discord sticker.
</summary>
</member>
<member name="P:Discord.ISticker.Id">
<summary>
Gets the ID of this sticker.
</summary>
<returns>
A snowflake ID associated with this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.PackId">
<summary>
Gets the ID of the pack of this sticker.
</summary>
<returns>
A snowflake ID associated with the pack of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Name">
<summary>
Gets the name of this sticker.
</summary>
<returns>
A <see langword="string"/> with the name of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Description">
<summary>
Gets the description of this sticker.
</summary>
<returns>
A <see langword="string"/> with the description of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Tags">
<summary>
Gets the list of tags of this sticker.
</summary>
<returns>
A read-only list with the tags of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Asset">
<summary>
Gets the asset hash of this sticker.
</summary>
<returns>
A <see langword="string"/> with the asset hash of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.PreviewAsset">
<summary>
Gets the preview asset hash of this sticker.
</summary>
<returns>
A <see langword="string"/> with the preview asset hash of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.FormatType">
<summary>
Gets the format type of this sticker.
</summary>
<returns>
A <see cref="T:Discord.StickerFormatType"/> with the format type of this sticker.
</returns>
</member>
<member name="T:Discord.ISystemMessage">
<summary>
Represents a generic message sent by the system.
@@ -9329,6 +9331,214 @@
otherwise <see langword="false"/>.
</returns>
</member>
<member name="T:Discord.ICustomSticker">
<summary>
Represents a custom sticker within a guild.
</summary>
</member>
<member name="P:Discord.ICustomSticker.AuthorId">
<summary>
Gets the users id who uploaded the sticker.
</summary>
<remarks>
In order to get the author id, the bot needs the MANAGE_EMOJIS_AND_STICKERS permission.
</remarks>
</member>
<member name="P:Discord.ICustomSticker.Guild">
<summary>
Gets the guild that this custom sticker is in.
</summary>
</member>
<member name="M:Discord.ICustomSticker.ModifyAsync(System.Action{Discord.StickerProperties},Discord.RequestOptions)">
<summary>
Modifies this sticker.
</summary>
<remarks>
This method modifies this sticker with the specified properties. To see an example of this
method and what properties are available, please refer to <see cref="T:Discord.StickerProperties"/>.
<br/>
<br/>
The bot needs the MANAGE_EMOJIS_AND_STICKERS permission within the guild in order to modify stickers.
</remarks>
<example>
<para>The following example replaces the name of the sticker with <c>kekw</c>.</para>
<code language="cs">
await sticker.ModifyAsync(x =&gt; x.Name = "kekw");
</code>
</example>
<param name="func">A delegate containing the properties to modify the sticker with.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous modification operation.
</returns>
</member>
<member name="M:Discord.ICustomSticker.DeleteAsync(Discord.RequestOptions)">
<summary>
Deletes the current sticker.
</summary>
<remakrs>
The bot neeeds the MANAGE_EMOJIS_AND_STICKERS permission inside the guild in order to delete stickers.
</remakrs>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous deletion operation.
</returns>
</member>
<member name="T:Discord.ISticker">
<summary>
Represents a discord sticker.
</summary>
</member>
<member name="P:Discord.ISticker.Id">
<summary>
Gets the ID of this sticker.
</summary>
<returns>
A snowflake ID associated with this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.PackId">
<summary>
Gets the ID of the pack of this sticker.
</summary>
<returns>
A snowflake ID associated with the pack of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Name">
<summary>
Gets the name of this sticker.
</summary>
<returns>
A <see langword="string"/> with the name of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Description">
<summary>
Gets the description of this sticker.
</summary>
<returns>
A <see langword="string"/> with the description of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Tags">
<summary>
Gets the list of tags of this sticker.
</summary>
<returns>
A read-only list with the tags of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Asset">
<summary>
Gets the asset hash of this sticker.
</summary>
<returns>
A <see langword="string"/> with the asset hash of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.PreviewAsset">
<summary>
Gets the preview asset hash of this sticker.
</summary>
<returns>
A <see langword="string"/> with the preview asset hash of this sticker.
</returns>
</member>
<member name="P:Discord.ISticker.Format">
<summary>
Gets the format type of this sticker.
</summary>
<returns>
A <see cref="T:Discord.StickerFormatType"/> with the format type of this sticker.
</returns>
</member>
<member name="M:Discord.ISticker.GetStickerUrl">
<summary>
Gets the image url for this sticker.
</summary>
</member>
<member name="T:Discord.IStickerItem">
<summary>
Represents a partial sticker item received with a message.
</summary>
</member>
<member name="P:Discord.IStickerItem.Id">
<summary>
The id of the sticker.
</summary>
</member>
<member name="P:Discord.IStickerItem.Name">
<summary>
The name of the sticker.
</summary>
</member>
<member name="P:Discord.IStickerItem.Format">
<summary>
The format of the sticker.
</summary>
</member>
<member name="T:Discord.StickerPack`1">
<summary>
Represents a discord sticker pack.
</summary>
<typeparam name="TSticker">The type of the stickers within the collection</typeparam>
</member>
<member name="P:Discord.StickerPack`1.Id">
<summary>
Gets the id of the sticker pack.
</summary>
</member>
<member name="P:Discord.StickerPack`1.Stickers">
<summary>
Gets a collection of the stickers in the pack.
</summary>
</member>
<member name="P:Discord.StickerPack`1.Name">
<summary>
Gets the name of the sticker pack.
</summary>
</member>
<member name="P:Discord.StickerPack`1.SkuId">
<summary>
Gets the id of the pack's SKU.
</summary>
</member>
<member name="P:Discord.StickerPack`1.CoverStickerId">
<summary>
Gets the id of a sticker in the pack which is shown as the pack's icon.
</summary>
</member>
<member name="P:Discord.StickerPack`1.Description">
<summary>
Gets the description of the sticker pack.
</summary>
</member>
<member name="P:Discord.StickerPack`1.BannerAssetId">
<summary>
Gets the id of the sticker pack's banner image
</summary>
</member>
<member name="T:Discord.StickerProperties">
<summary>
Represents a class used to modify stickers.
</summary>
</member>
<member name="P:Discord.StickerProperties.Name">
<summary>
Gets or sets the name of the sticker.
</summary>
</member>
<member name="P:Discord.StickerProperties.Description">
<summary>
Gets or sets the description of the sticker.
</summary>
</member>
<member name="P:Discord.StickerProperties.Tags">
<summary>
Gets or sets the tags of the sticker.
</summary>
</member>
<member name="T:Discord.ITeam">
<summary>
Represents a Discord Team.


+ 4
- 3
src/Discord.Net.Core/Entities/Messages/IMessage.cs View File

@@ -169,12 +169,13 @@ namespace Discord
/// </summary>
IReadOnlyCollection<IMessageComponent> Components { get; }

/// Gets all stickers included in this message.
/// <summary>
/// Gets all stickers items included in this message.
/// </summary>
/// <returns>
/// A read-only collection of sticker objects.
/// A read-only collection of sticker item objects.
/// </returns>
IReadOnlyCollection<ISticker> Stickers { get; }
IReadOnlyCollection<IStickerItem> Stickers { get; }
/// <summary>
/// Gets the flags related to this message.


+ 2
- 2
src/Discord.Net.Core/Entities/Stickers/ISticker.cs View File

@@ -7,7 +7,7 @@ namespace Discord
/// <summary>
/// Represents a discord sticker.
/// </summary>
public interface ISticker
public interface ISticker : IStickerItem
{
/// <summary>
/// Gets the ID of this sticker.
@@ -64,7 +64,7 @@ namespace Discord
/// <returns>
/// A <see cref="StickerFormatType"/> with the format type of this sticker.
/// </returns>
StickerFormatType FormatType { get; }
StickerFormatType Format { get; }

/// <summary>
/// Gets the image url for this sticker.


+ 29
- 0
src/Discord.Net.Core/Entities/Stickers/IStickerItem.cs View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// Represents a partial sticker item received with a message.
/// </summary>
public interface IStickerItem
{
/// <summary>
/// The id of the sticker.
/// </summary>
ulong Id { get; }

/// <summary>
/// The name of the sticker.
/// </summary>
string Name { get; }

/// <summary>
/// The format of the sticker.
/// </summary>
StickerFormatType Format { get; }
}
}

+ 63
- 0
src/Discord.Net.Core/Entities/Stickers/StickerPack.cs View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// Represents a discord sticker pack.
/// </summary>
/// <typeparam name="TSticker">The type of the stickers within the collection</typeparam>
public class StickerPack<TSticker> where TSticker : ISticker
{
/// <summary>
/// Gets the id of the sticker pack.
/// </summary>
public ulong Id { get; }

/// <summary>
/// Gets a collection of the stickers in the pack.
/// </summary>
public IReadOnlyCollection<TSticker> Stickers { get; }

/// <summary>
/// Gets the name of the sticker pack.
/// </summary>
public string Name { get; }

/// <summary>
/// Gets the id of the pack's SKU.
/// </summary>
public ulong SkuId { get; }

/// <summary>
/// Gets the id of a sticker in the pack which is shown as the pack's icon.
/// </summary>
public ulong? CoverStickerId { get; }

/// <summary>
/// Gets the description of the sticker pack.
/// </summary>
public string Description { get; }

/// <summary>
/// Gets the id of the sticker pack's banner image
/// </summary>
public ulong BannerAssetId { get; }

internal StickerPack(string name, ulong id, ulong skuid, ulong? coverStickerId, string description, ulong bannerAssetId, IEnumerable<TSticker> stickers)
{
this.Name = name;
this.Id = id;
this.SkuId = skuid;
this.CoverStickerId = coverStickerId;
this.Description = description;
this.BannerAssetId = bannerAssetId;

this.Stickers = stickers.ToImmutableArray();
}
}
}

+ 2
- 0
src/Discord.Net.Rest/API/Common/Sticker.cs View File

@@ -21,6 +21,8 @@ namespace Discord.API
public string PreviewAsset { get; set; }
[JsonProperty("format_type")]
public StickerFormatType FormatType { get; set; }
[JsonProperty("guild_id")]
public Optional<ulong> GuildId { get; set; }
[JsonProperty("user")]
public Optional<User> User { get; set; }
}


+ 117
- 27
src/Discord.Net.Rest/Discord.Net.Rest.xml View File

@@ -3527,6 +3527,50 @@
<member name="M:Discord.Rest.RestGuild.DeleteEmoteAsync(Discord.GuildEmote,Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.Rest.RestGuild.CreateStickerAsync(System.String,System.String,System.Collections.Generic.IEnumerable{System.String},Discord.Image,Discord.RequestOptions)">
<summary>
Creates a new sticker in this guild.
</summary>
<param name="name">The name of the sticker.</param>
<param name="description">The description of the sticker.</param>
<param name="tags">The tags of the sticker.</param>
<param name="image">The image of the new emote.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous creation operation. The task result contains the created sticker.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.GetStickerAsync(System.UInt64,Discord.RequestOptions)">
<summary>
Gets a specific sticker within this guild.
</summary>
<param name="id">The id of the sticker to get.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains the sticker found with the
specified <paramref name="id"/>; <see langword="null" /> if none is found.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.GetStickersAsync(Discord.RequestOptions)">
<summary>
Gets a collection of all stickers within this guild.
</summary>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains a read-only collection
of stickers found within the guild.
</returns>
</member>
<member name="M:Discord.Rest.RestGuild.DeleteStickerAsync(Discord.Rest.CustomSticker,Discord.RequestOptions)">
<summary>
Deletes a sticker within this guild.
</summary>
<param name="sticker">The sticker to delete.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous removal operation.
</returns>
</member>
<member name="P:Discord.Rest.RestGuild.Discord#IGuild#Available">
<inheritdoc />
</member>
@@ -3970,6 +4014,33 @@
<member name="P:Discord.Rest.RestInviteMetadata.CreatedAt">
<inheritdoc />
</member>
<member name="T:Discord.Rest.CustomSticker">
<summary>
Represents a Rest-based custom sticker within a guild.
</summary>
</member>
<member name="P:Discord.Rest.CustomSticker.AuthorId">
<summary>
Gets the users id who uploaded the sticker.
</summary>
<remarks>
In order to get the author id, the bot needs the MANAGE_EMOJIS_AND_STICKERS permission.
</remarks>
</member>
<member name="P:Discord.Rest.CustomSticker.Guild">
<summary>
Gets the guild that this custom sticker is in.
</summary>
<remarks>
<b>Note</b>: This property can be <see langword="null"/> if the sticker wasnt fetched from a guild.
</remarks>
</member>
<member name="M:Discord.Rest.CustomSticker.DeleteAsync(Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="M:Discord.Rest.CustomSticker.ModifyAsync(System.Action{Discord.StickerProperties},Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="F:Discord.Rest.MessageHelper.InlineCodeRegex">
<summary>
Regex used to check if some text is formatted as inline code.
@@ -4268,6 +4339,52 @@
<inheritdoc />
<exception cref="T:System.InvalidOperationException">This operation may only be called on a <see cref="T:Discord.INewsChannel"/> channel.</exception>
</member>
<member name="T:Discord.Rest.Sticker">
<inheritdoc cref="T:Discord.ISticker"/>
</member>
<member name="P:Discord.Rest.Sticker.PackId">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.Name">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.Description">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.Tags">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.Asset">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.PreviewAsset">
<inheritdoc />
</member>
<member name="P:Discord.Rest.Sticker.Format">
<inheritdoc />
</member>
<member name="M:Discord.Rest.Sticker.GetStickerUrl">
<inheritdoc/>
</member>
<member name="T:Discord.Rest.StickerItem">
<summary>
Represents a partial sticker received in a message.
</summary>
</member>
<member name="P:Discord.Rest.StickerItem.Name">
<inheritdoc/>
</member>
<member name="P:Discord.Rest.StickerItem.Format">
<inheritdoc/>
</member>
<member name="M:Discord.Rest.StickerItem.ResolveStickerAsync">
<summary>
Resolves this sticker item by fetching the <see cref="T:Discord.Rest.Sticker"/> from the API.
</summary>
<returns>
A task representing the download operation, the result of the task is a sticker object.
</returns>
</member>
<member name="T:Discord.Rest.RestApplication">
<summary>
Represents a REST-based entity that contains information about a Discord application created via the developer portal.
@@ -4870,32 +4987,5 @@
A string containing the filename of this attachment.
</returns>
</member>
<member name="T:Discord.Sticker">
<inheritdoc cref="T:Discord.ISticker"/>
</member>
<member name="P:Discord.Sticker.Id">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.PackId">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.Name">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.Description">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.Tags">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.Asset">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.PreviewAsset">
<inheritdoc />
</member>
<member name="P:Discord.Sticker.FormatType">
<inheritdoc />
</member>
</members>
</doc>

+ 4
- 4
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -559,7 +559,7 @@ namespace Discord.Rest
return await client.ApiClient.CreateGuildStickerAsync(apiArgs, guild.Id, options).ConfigureAwait(false);
}

public static async Task<API.Sticker> ModifyStickerAsync(BaseDiscordClient client, IGuild guild, ISticker sticker, Action<StickerProperties> func,
public static async Task<API.Sticker> ModifyStickerAsync(BaseDiscordClient client, ulong guildId, ISticker sticker, Action<StickerProperties> func,
RequestOptions options = null)
{
if (func == null)
@@ -577,10 +577,10 @@ namespace Discord.Rest
Optional<string>.Unspecified
};

return await client.ApiClient.ModifyStickerAsync(apiArgs, guild.Id, sticker.Id, options).ConfigureAwait(false);
return await client.ApiClient.ModifyStickerAsync(apiArgs, guildId, sticker.Id, options).ConfigureAwait(false);
}

public static async Task DeleteStickerAsync(BaseDiscordClient client, IGuild guild, ISticker sticker, RequestOptions options = null)
=> await client.ApiClient.DeleteStickerAsync(guild.Id, sticker.Id, options).ConfigureAwait(false);
public static async Task DeleteStickerAsync(BaseDiscordClient client, ulong guildId, ISticker sticker, RequestOptions options = null)
=> await client.ApiClient.DeleteStickerAsync(guildId, sticker.Id, options).ConfigureAwait(false);
}
}

+ 111
- 0
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -19,6 +19,7 @@ namespace Discord.Rest
{
private ImmutableDictionary<ulong, RestRole> _roles;
private ImmutableArray<GuildEmote> _emotes;
private ImmutableArray<CustomSticker> _stickers;
private ImmutableArray<string> _features;

/// <inheritdoc />
@@ -112,6 +113,7 @@ namespace Discord.Rest
public IReadOnlyCollection<RestRole> Roles => _roles.ToReadOnlyCollection();
/// <inheritdoc />
public IReadOnlyCollection<GuildEmote> Emotes => _emotes;
public IReadOnlyCollection<CustomSticker> Stickers => _stickers;
/// <inheritdoc />
public IReadOnlyCollection<string> Features => _features;

@@ -190,6 +192,23 @@ namespace Discord.Rest
}
_roles = roles.ToImmutable();

if (model.Stickers != null)
{
var stickers = new ImmutableArray<CustomSticker>();
for (int i = 0; i < model.Stickers.Length; i++)
{
var sticker = model.Stickers[i];
var entity = CustomSticker.Create(Discord, sticker, this, sticker.User.IsSpecified ? sticker.User.Value.Id : null);

stickers.Add(entity);
}

_stickers = stickers;
}
else
_stickers = new ImmutableArray<CustomSticker>();

Available = true;
}
internal void Update(WidgetModel model)
@@ -908,6 +927,78 @@ namespace Discord.Rest
public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null)
=> GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options);

//Stickers
/// <summary>
/// Creates a new sticker in this guild.
/// </summary>
/// <param name="name">The name of the sticker.</param>
/// <param name="description">The description of the sticker.</param>
/// <param name="tags">The tags of the sticker.</param>
/// <param name="image">The image of the new emote.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous creation operation. The task result contains the created sticker.
/// </returns>
public async Task<CustomSticker> CreateStickerAsync(string name, string description, IEnumerable<string> tags, Image image, RequestOptions options = null)
{
var model = await GuildHelper.CreateStickerAsync(Discord, this, name, description, tags, image, options).ConfigureAwait(false);

return CustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null);
}
/// <summary>
/// Gets a specific sticker within this guild.
/// </summary>
/// <param name="id">The id of the sticker to get.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains the sticker found with the
/// specified <paramref name="id"/>; <see langword="null" /> if none is found.
/// </returns>
public async Task<CustomSticker> GetStickerAsync(ulong id, RequestOptions options = null)
{
var model = await Discord.ApiClient.GetGuildStickerAsync(this.Id, id, options).ConfigureAwait(false);

if (model == null)
return null;

return CustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null);
}
/// <summary>
/// Gets a collection of all stickers within this guild.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of stickers found within the guild.
/// </returns>
public async Task<IReadOnlyCollection<CustomSticker>> GetStickersAsync(RequestOptions options = null)
{
var models = await Discord.ApiClient.ListGuildStickersAsync(this.Id, options).ConfigureAwait(false);

if (models.Length == 0)
return null;

List<CustomSticker> stickers = new List<CustomSticker>();

foreach(var model in models)
{
var entity = CustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null);
stickers.Add(entity);
}

return stickers.ToImmutableArray();
}
/// <summary>
/// Deletes a sticker within this guild.
/// </summary>
/// <param name="sticker">The sticker to delete.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous removal operation.
/// </returns>
public Task DeleteStickerAsync(CustomSticker sticker, RequestOptions options = null)
=> sticker.DeleteAsync(options);

//IGuild
/// <inheritdoc />
bool IGuild.Available => Available;
@@ -918,6 +1009,8 @@ namespace Discord.Rest
/// <inheritdoc />
IReadOnlyCollection<IRole> IGuild.Roles => Roles;

IReadOnlyCollection<ICustomSticker> IGuild.Stickers => Stickers;

/// <inheritdoc />
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options)
=> await GetBansAsync(options).ConfigureAwait(false);
@@ -1169,5 +1262,23 @@ namespace Discord.Rest
/// <inheritdoc />
async Task<IReadOnlyCollection<IApplicationCommand>> IGuild.GetApplicationCommandsAsync (RequestOptions options)
=> await GetApplicationCommandsAsync(options).ConfigureAwait(false);
async Task<ICustomSticker> IGuild.CreateStickerAsync(string name, string description, IEnumerable<string> tags, Image image, RequestOptions options)
=> await CreateStickerAsync(name, description, tags, image, options);
async Task<ICustomSticker> IGuild.GetStickerAsync(ulong id, CacheMode mode, RequestOptions options)
{
if (mode != CacheMode.AllowDownload)
return null;

return await GetStickerAsync(id, options);
}
async Task<IReadOnlyCollection<ICustomSticker>> IGuild.GetStickersAsync(CacheMode mode, RequestOptions options)
{
if (mode != CacheMode.AllowDownload)
return null;

return await GetStickersAsync(options);
}
Task IGuild.DeleteStickerAsync(ICustomSticker sticker, RequestOptions options)
=> sticker.DeleteAsync();
}
}

+ 73
- 0
src/Discord.Net.Rest/Entities/Messages/CustomSticker.cs View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model = Discord.API.Sticker;

namespace Discord.Rest
{
/// <summary>
/// Represents a Rest-based custom sticker within a guild.
/// </summary>
public class CustomSticker : Sticker, ICustomSticker
{
/// <summary>
/// Gets the users id who uploaded the sticker.
/// </summary>
/// <remarks>
/// In order to get the author id, the bot needs the MANAGE_EMOJIS_AND_STICKERS permission.
/// </remarks>
public ulong? AuthorId { get; private set; }

/// <summary>
/// Gets the guild that this custom sticker is in.
/// </summary>
/// <remarks>
/// <b>Note</b>: This property can be <see langword="null"/> if the sticker wasnt fetched from a guild.
/// </remarks>
public RestGuild Guild { get; private set; }

private ulong GuildId { get; set; }

internal CustomSticker(BaseDiscordClient client, ulong id, RestGuild guild, ulong? authorId = null)
: base(client, id)
{
this.AuthorId = authorId;
this.Guild = guild;
}
internal CustomSticker(BaseDiscordClient client, ulong id, ulong guildId, ulong? authorId = null)
: base(client, id)
{
this.AuthorId = authorId;
this.GuildId = guildId;
}

internal static CustomSticker Create(BaseDiscordClient client, Model model, RestGuild guild, ulong? authorId = null)
{
var entity = new CustomSticker(client, model.Id, guild, authorId);
entity.Update(model);
return entity;
}

internal static CustomSticker Create(BaseDiscordClient client, Model model, ulong guildId, ulong? authorId = null)
{
var entity = new CustomSticker(client, model.Id, guildId, authorId);
entity.Update(model);
return entity;
}

/// <inheritdoc/>
public Task DeleteAsync(RequestOptions options = null)
=> GuildHelper.DeleteStickerAsync(Discord, GuildId, this, options);

/// <inheritdoc/>
public async Task ModifyAsync(Action<StickerProperties> func, RequestOptions options = null)
{
var model = await GuildHelper.ModifyStickerAsync(Discord, GuildId, this, func, options);
Update(model);
}

IGuild ICustomSticker.Guild => Guild;
}
}

+ 2
- 2
src/Discord.Net.Rest/Entities/Messages/RestMessage.cs View File

@@ -60,7 +60,7 @@ namespace Discord.Rest
/// <inheritdoc />
public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>();
/// <inheritdoc />
public virtual IReadOnlyCollection<Sticker> Stickers => ImmutableArray.Create<Sticker>();
public virtual IReadOnlyCollection<StickerItem> Stickers => ImmutableArray.Create<StickerItem>();

/// <inheritdoc />
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);
@@ -236,7 +236,7 @@ namespace Discord.Rest
IReadOnlyCollection<IMessageComponent> IMessage.Components => Components;

/// <inheritdoc />
IReadOnlyCollection<ISticker> IMessage.Stickers => Stickers;
IReadOnlyCollection<IStickerItem> IMessage.Stickers => Stickers;

/// <inheritdoc />
public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me });


+ 7
- 7
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -21,7 +21,7 @@ namespace Discord.Rest
private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>();
private ImmutableArray<ulong> _roleMentionIds = ImmutableArray.Create<ulong>();
private ImmutableArray<RestUser> _userMentions = ImmutableArray.Create<RestUser>();
private ImmutableArray<Sticker> _stickers = ImmutableArray.Create<Sticker>();
private ImmutableArray<StickerItem> _stickers = ImmutableArray.Create<StickerItem>();

/// <inheritdoc />
public override bool IsTTS => _isTTS;
@@ -46,7 +46,7 @@ namespace Discord.Rest
/// <inheritdoc />
public override IReadOnlyCollection<ITag> Tags => _tags;
/// <inheritdoc />
public override IReadOnlyCollection<Sticker> Stickers => _stickers;
public override IReadOnlyCollection<StickerItem> Stickers => _stickers;
/// <inheritdoc />
public IUserMessage ReferencedMessage => _referencedMessage;

@@ -136,18 +136,18 @@ namespace Discord.Rest
_referencedMessage = RestUserMessage.Create(Discord, Channel, refMsgAuthor, refMsg);
}

if (model.Stickers.IsSpecified)
if (model.StickerItems.IsSpecified)
{
var value = model.Stickers.Value;
var value = model.StickerItems.Value;
if (value.Length > 0)
{
var stickers = ImmutableArray.CreateBuilder<Sticker>(value.Length);
var stickers = ImmutableArray.CreateBuilder<StickerItem>(value.Length);
for (int i = 0; i < value.Length; i++)
stickers.Add(Sticker.Create(value[i]));
stickers.Add(new StickerItem(Discord, value[i]));
_stickers = stickers.ToImmutable();
}
else
_stickers = ImmutableArray.Create<Sticker>();
_stickers = ImmutableArray.Create<StickerItem>();
}
}



+ 28
- 24
src/Discord.Net.Rest/Entities/Messages/Sticker.cs View File

@@ -3,45 +3,49 @@ using System.Diagnostics;
using System.Linq;
using Model = Discord.API.Sticker;

namespace Discord
namespace Discord.Rest
{
/// <inheritdoc cref="ISticker"/>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class Sticker : ISticker
public class Sticker : RestEntity<ulong>, ISticker
{
/// <inheritdoc />
public ulong Id { get; }
public ulong PackId { get; internal set; }
/// <inheritdoc />
public ulong PackId { get; }
public string Name { get; internal set; }
/// <inheritdoc />
public string Name { get; }
public string Description { get; internal set; }
/// <inheritdoc />
public string Description { get; }
public IReadOnlyCollection<string> Tags { get; internal set; }
/// <inheritdoc />
public IReadOnlyCollection<string> Tags { get; }
public string Asset { get; internal set; }
/// <inheritdoc />
public string Asset { get; }
public string PreviewAsset { get; internal set; }
/// <inheritdoc />
public string PreviewAsset { get; }
/// <inheritdoc />
public StickerFormatType FormatType { get; }
public StickerFormatType Format { get; internal set; }

/// <inheritdoc/>
public string GetStickerUrl()
=> CDN.GetStickerUrl(this.Id, this.Format);

internal Sticker(ulong id, ulong packId, string name, string description, string[] tags, string asset, string previewAsset, StickerFormatType formatType)
internal Sticker(BaseDiscordClient client, ulong id)
: base(client, id) { }
internal static Sticker Create(BaseDiscordClient client, Model model)
{
Id = id;
PackId = packId;
Name = name;
Description = description;
Tags = tags.ToReadOnlyCollection();
Asset = asset;
PreviewAsset = previewAsset;
FormatType = formatType;
var entity = new Sticker(client, model.Id);
entity.Update(model);
return entity;
}
internal static Sticker Create(Model model)

internal void Update(Model model)
{
return new Sticker(model.Id, model.PackId, model.Name, model.Desription,
model.Tags.IsSpecified ? model.Tags.Value.Split(',').Select(x => x.Trim()).ToArray() : new string[0],
model.Asset, model.PreviewAsset, model.FormatType);
PackId = model.PackId;
Name = model.Name;
Description = model.Desription;
Tags = model.Tags.IsSpecified ? model.Tags.Value.Split(',').Select(x => x.Trim()).ToArray() : new string[0];
Asset = model.Asset;
PreviewAsset = model.PreviewAsset;
Format = model.FormatType;
}

private string DebuggerDisplay => $"{Name} ({Id})";


+ 9
- 10
src/Discord.Net.Rest/Entities/Messages/StickerItem.cs View File

@@ -10,17 +10,13 @@ namespace Discord.Rest
/// <summary>
/// Represents a partial sticker received in a message.
/// </summary>
public class StickerItem : RestEntity<ulong>
public class StickerItem : RestEntity<ulong>, IStickerItem
{
/// <summary>
/// The name of this sticker.
/// </summary>
public readonly string Name;
/// <inheritdoc/>
public string Name { get; }

/// <summary>
/// The format of this sticker.
/// </summary>
public readonly StickerFormatType Format;
/// <inheritdoc/>
public StickerFormatType Format { get; }

internal StickerItem(BaseDiscordClient client, Model model)
: base(client, model.Id)
@@ -40,7 +36,10 @@ namespace Discord.Rest
{
var model = await Discord.ApiClient.GetStickerAsync(this.Id);

return Sticker.Create(model);
if (model.GuildId.IsSpecified)
return CustomSticker.Create(Discord, model, model.GuildId.Value, model.User.IsSpecified ? model.User.Value.Id : null);
else
return Sticker.Create(Discord, model);
}
}
}

+ 14
- 0
src/Discord.Net.WebSocket/BaseSocketClient.cs View File

@@ -44,6 +44,10 @@ namespace Discord.WebSocket

internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;

/// <summary>
/// Gets a collection of default stickers.
/// </summary>
public abstract IReadOnlyCollection<StickerPack<SocketSticker>> DefaultStickerPacks { get; }
/// <summary>
/// Gets the current logged-in user.
/// </summary>
@@ -268,6 +272,16 @@ namespace Discord.WebSocket
/// </returns>
public Task<RestInviteMetadata> GetInviteAsync(string inviteId, RequestOptions options = null)
=> ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default);
/// <summary>
/// Gets a sticker.
/// </summary>
/// <param name="mode">Whether or not to allow downloading from the api.</param>
/// <param name="id">The id of the sticker to get.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A <see cref="SocketSticker"/> if found, otherwise <see langword="null"/>.
/// </returns>
public abstract Task<SocketSticker> GetStickerAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
// IDiscordClient
/// <inheritdoc />


+ 1
- 1
src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj View File

@@ -20,7 +20,7 @@
<FileVersion>3.0.1</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;</DefineConstants>
<DefineConstants>TRACE;DEBUG;DEBUG_PACKETS</DefineConstants>
</PropertyGroup>
<ItemGroup>


+ 173
- 0
src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml View File

@@ -118,6 +118,11 @@
Provides access to a REST-only client with a shared state from this client.
</summary>
</member>
<member name="P:Discord.WebSocket.BaseSocketClient.DefaultStickerPacks">
<summary>
Gets a collection of default stickers.
</summary>
</member>
<member name="P:Discord.WebSocket.BaseSocketClient.CurrentUser">
<summary>
Gets the current logged-in user.
@@ -349,6 +354,17 @@
A task that represents the asynchronous get operation. The task result contains the invite information.
</returns>
</member>
<member name="M:Discord.WebSocket.BaseSocketClient.GetStickerAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets a sticker.
</summary>
<param name="mode">Whether or not to allow downloading from the api.</param>
<param name="id">The id of the sticker to get.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A <see cref="T:Discord.WebSocket.SocketSticker"/> if found, otherwise <see langword="null"/>.
</returns>
</member>
<member name="M:Discord.WebSocket.BaseSocketClient.Discord#IDiscordClient#GetApplicationInfoAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
@@ -820,6 +836,9 @@
<member name="P:Discord.WebSocket.DiscordShardedClient.Activity">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.DiscordShardedClient.DefaultStickerPacks">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.DiscordShardedClient.Guilds">
<inheritdoc />
</member>
@@ -858,6 +877,9 @@
<member name="M:Discord.WebSocket.DiscordShardedClient.GetChannel(System.UInt64)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.DiscordShardedClient.GetStickerAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.DiscordShardedClient.GetUser(System.UInt64)">
<inheritdoc />
</member>
@@ -959,6 +981,9 @@
<member name="P:Discord.WebSocket.DiscordSocketClient.Guilds">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.DiscordSocketClient.DefaultStickerPacks">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.DiscordSocketClient.PrivateChannels">
<inheritdoc />
</member>
@@ -1083,6 +1108,16 @@
Clears cached users from the client.
</summary>
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.GetStickerAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.GetSticker(System.UInt64)">
<summary>
Gets a sticker.
</summary>
<param name="id">The unique identifier of the sticker.</param>
<returns>A sticker if found, otherwise <see langword="null"/>.</returns>
</member>
<member name="M:Discord.WebSocket.DiscordSocketClient.GetVoiceRegionsAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
@@ -3030,6 +3065,11 @@
<member name="P:Discord.WebSocket.SocketGuild.Emotes">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGuild.Stickers">
<summary>
Gets a collection of all custom stickers for this guild.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketGuild.Features">
<inheritdoc />
</member>
@@ -3438,6 +3478,59 @@
<member name="M:Discord.WebSocket.SocketGuild.DeleteEmoteAsync(Discord.GuildEmote,Discord.RequestOptions)">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.SocketGuild.GetStickerAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets a specific sticker within this guild.
</summary>
<param name="id">The id of the sticker to get.</param>
<param name="mode">The <see cref="T:Discord.CacheMode" /> that determines whether the object should be fetched from cache.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains the sticker found with the
specified <paramref name="id"/>; <see langword="null" /> if none is found.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketGuild.GetSticker(System.UInt64)">
<summary>
Gets a specific sticker within this guild.
</summary>
<param name="id">The id of the sticker to get.</param>
<returns>A sticker, if none is found then <see langword="null"/>.</returns>
</member>
<member name="M:Discord.WebSocket.SocketGuild.GetStickersAsync(Discord.CacheMode,Discord.RequestOptions)">
<summary>
Gets a collection of all stickers within this guild.
</summary>
<param name="mode">The <see cref="T:Discord.CacheMode" /> that determines whether the object should be fetched from cache.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous get operation. The task result contains a read-only collection
of stickers found within the guild.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketGuild.CreateStickerAsync(System.String,System.String,System.Collections.Generic.IEnumerable{System.String},Discord.Image,Discord.RequestOptions)">
<summary>
Creates a new sticker in this guild.
</summary>
<param name="name">The name of the sticker.</param>
<param name="description">The description of the sticker.</param>
<param name="tags">The tags of the sticker.</param>
<param name="image">The image of the new emote.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous creation operation. The task result contains the created sticker.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketGuild.DeleteStickerAsync(Discord.WebSocket.SocketCustomSticker,Discord.RequestOptions)">
<summary>
Deletes a sticker within this guild.
</summary>
<param name="sticker">The sticker to delete.</param>
<param name="options">The options to be used when sending the request.</param>
<returns>
A task that represents the asynchronous removal operation.
</returns>
</member>
<member name="M:Discord.WebSocket.SocketGuild.ToString">
<summary>
Gets the name of the guild.
@@ -3479,6 +3572,9 @@
<member name="P:Discord.WebSocket.SocketGuild.Discord#IGuild#ApproximatePresenceCount">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketGuild.Discord#IGuild#Stickers">
<inheritdoc />
</member>
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetBansAsync(Discord.RequestOptions)">
<inheritdoc />
</member>
@@ -4417,6 +4513,83 @@
<member name="P:Discord.WebSocket.SocketEntity`1.Id">
<inheritdoc />
</member>
<member name="P:Discord.WebSocket.SocketCustomSticker.Author">
<summary>
Gets the user that uploaded the guild sticker.
</summary>
<remarks>
<note>
This may return <see langword="null"/> in the WebSocket implementation due to incomplete user collection in
large guilds, or the bot doesnt have the MANAGE_EMOJIS_AND_STICKERS permission.
</note>
</remarks>
</member>
<member name="P:Discord.WebSocket.SocketCustomSticker.Guild">
<summary>
Gets the guild the sticker lives in.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketCustomSticker.AuthorId">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCustomSticker.ModifyAsync(System.Action{Discord.StickerProperties},Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketCustomSticker.DeleteAsync(Discord.RequestOptions)">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.PackId">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.Name">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.Description">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.Tags">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.Asset">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.PreviewAsset">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketSticker.Format">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketSticker.GetStickerUrl">
<inheritdoc/>
</member>
<member name="T:Discord.WebSocket.SocketUnknownSticker">
<summary>
Represents an unknown sticker received over the gateway.
</summary>
</member>
<member name="P:Discord.WebSocket.SocketUnknownSticker.Asset">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketUnknownSticker.Tags">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketUnknownSticker.Description">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketUnknownSticker.PackId">
<inheritdoc/>
</member>
<member name="P:Discord.WebSocket.SocketUnknownSticker.PreviewAsset">
<inheritdoc/>
</member>
<member name="M:Discord.WebSocket.SocketUnknownSticker.ResolveAsync(Discord.CacheMode,Discord.RequestOptions)">
<summary>
Attempts to try to find the sticker.
</summary>
<returns>
The sticker representing this unknown stickers Id, if none is found then <see langword="null"/>.
</returns>
</member>
<member name="T:Discord.WebSocket.SocketGroupUser">
<summary>
Represents a WebSocket-based group user.


+ 68
- 0
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Immutable;

namespace Discord.WebSocket
{
@@ -16,6 +17,7 @@ namespace Discord.WebSocket
private readonly bool _automaticShards;
private int[] _shardIds;
private DiscordSocketClient[] _shards;
private ImmutableArray<StickerPack<SocketSticker>> _defaultStickers;
private int _totalShards;
private SemaphoreSlim[] _identifySemaphores;
private object _semaphoreResetLock;
@@ -40,6 +42,10 @@ namespace Discord.WebSocket
return base.ApiClient;
}
}
/// <inheritdoc/>
public override IReadOnlyCollection<StickerPack<SocketSticker>> DefaultStickerPacks
=> _defaultStickers.ToReadOnlyCollection();

/// <inheritdoc />
public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount);
/// <inheritdoc />
@@ -77,6 +83,7 @@ namespace Discord.WebSocket
_shardIdsToIndex = new Dictionary<int, int>();
config.DisplayInitialLog = false;
_baseConfig = config;
_defaultStickers = new ImmutableArray<StickerPack<SocketSticker>>();

if (config.TotalShards == null)
_automaticShards = true;
@@ -155,6 +162,10 @@ namespace Discord.WebSocket
//Assume thread safe: already in a connection lock
for (int i = 0; i < _shards.Length; i++)
await _shards[i].LoginAsync(tokenType, token);

if(this._defaultStickers.Length == 0)
await DownloadDefaultStickersAsync().ConfigureAwait(false);

}
internal override async Task OnLogoutAsync()
{
@@ -247,6 +258,63 @@ namespace Discord.WebSocket
result += _shards[i].Guilds.Count;
return result;
}
/// <inheritdoc/>
public override async Task<SocketSticker> GetStickerAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
var sticker = _defaultStickers.FirstOrDefault(x => x.Stickers.Any(y => y.Id == id))?.Stickers.FirstOrDefault(x => x.Id == id);

if (sticker != null)
return sticker;

foreach (var guild in Guilds)
{
sticker = await guild.GetStickerAsync(id, CacheMode.CacheOnly).ConfigureAwait(false);

if (sticker != null)
return sticker;
}

if (mode == CacheMode.CacheOnly)
return null;

var model = await ApiClient.GetStickerAsync(id, options).ConfigureAwait(false);

if (model == null)
return null;


if (model.GuildId.IsSpecified)
{
var guild = GetGuild(model.GuildId.Value);
sticker = guild.AddOrUpdateSticker(model);
return sticker;
}
else
{
return SocketSticker.Create(_shards[0], model);
}
}
private async Task DownloadDefaultStickersAsync()
{
var models = await ApiClient.ListNitroStickerPacksAsync().ConfigureAwait(false);

foreach(var model in models.StickerPacks)
{
var stickers = model.Stickers.Select(x => SocketSticker.Create(_shards[0], x));

var pack = new StickerPack<SocketSticker>(
model.Name,
model.Id,
model.SkuId,
model.CoverStickerId.ToNullable(),
model.Description,
model.BannerAssetId,
stickers
);

_defaultStickers.Add(pack);
}
}

/// <inheritdoc />
public override SocketUser GetUser(ulong id)


+ 83
- 0
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -44,6 +44,7 @@ namespace Discord.WebSocket
private RestApplication _applicationInfo;
private bool _isDisposed;
private GatewayIntents _gatewayIntents;
private ImmutableArray<StickerPack<SocketSticker>> _defaultStickers;

/// <summary>
/// Provides access to a REST-only client with a shared state from this client.
@@ -76,6 +77,17 @@ namespace Discord.WebSocket
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
/// <inheritdoc />
public override IReadOnlyCollection<SocketGuild> Guilds => State.Guilds;
/// <inheritdoc/>
public override IReadOnlyCollection<StickerPack<SocketSticker>> DefaultStickerPacks
{
get
{
if (this._shardedClient != null)
return this._shardedClient.DefaultStickerPacks;
else
return _defaultStickers.ToReadOnlyCollection();
}
}
/// <inheritdoc />
public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels;
/// <summary>
@@ -137,6 +149,7 @@ namespace Discord.WebSocket
Rest = new DiscordSocketRestClient(config, ApiClient);
_heartbeatTimes = new ConcurrentQueue<long>();
_gatewayIntents = config.GatewayIntents;
_defaultStickers = new ImmutableArray<StickerPack<SocketSticker>>();

_stateLock = new SemaphoreSlim(1, 1);
_gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}");
@@ -195,6 +208,31 @@ namespace Discord.WebSocket
base.Dispose(disposing);
}

internal override async Task OnLoginAsync(TokenType tokenType, string token)
{
if(this._shardedClient != null && this._defaultStickers.Length == 0)
{
var models = await ApiClient.ListNitroStickerPacksAsync().ConfigureAwait(false);

foreach (var model in models.StickerPacks)
{
var stickers = model.Stickers.Select(x => SocketSticker.Create(this, x));

var pack = new StickerPack<SocketSticker>(
model.Name,
model.Id,
model.SkuId,
model.CoverStickerId.ToNullable(),
model.Description,
model.BannerAssetId,
stickers
);

_defaultStickers.Add(pack);
}
}
}

/// <inheritdoc />
internal override async Task OnLogoutAsync()
{
@@ -368,6 +406,51 @@ namespace Discord.WebSocket
internal void RemoveUser(ulong id)
=> State.RemoveUser(id);

/// <inheritdoc/>
public override async Task<SocketSticker> GetStickerAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
var sticker = _defaultStickers.FirstOrDefault(x => x.Stickers.Any(y => y.Id == id))?.Stickers.FirstOrDefault(x => x.Id == id);

if (sticker != null)
return sticker;

foreach(var guild in Guilds)
{
sticker = await guild.GetStickerAsync(id, CacheMode.CacheOnly).ConfigureAwait(false);

if (sticker != null)
return sticker;
}

if (mode == CacheMode.CacheOnly)
return null;

var model = await ApiClient.GetStickerAsync(id, options).ConfigureAwait(false);

if(model == null)
return null;

if (model.GuildId.IsSpecified)
{
var guild = State.GetGuild(model.GuildId.Value);
sticker = guild.AddOrUpdateSticker(model);
return sticker;
}
else
{
return SocketSticker.Create(this, model);
}
}

/// <summary>
/// Gets a sticker.
/// </summary>
/// <param name="id">The unique identifier of the sticker.</param>
/// <returns>A sticker if found, otherwise <see langword="null"/>.</returns>
public SocketSticker GetSticker(ulong id)
=> GetStickerAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();

/// <inheritdoc />
public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null)
{


+ 11
- 1
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -533,6 +533,8 @@ namespace Discord.WebSocket

stickers.TryAdd(sticker.Id, entity);
}

_stickers = stickers;
}
else
_stickers = new ConcurrentDictionary<ulong, SocketCustomSticker>(ConcurrentHashSet.DefaultConcurrencyLevel, 7);
@@ -1192,6 +1194,13 @@ namespace Discord.WebSocket
return AddOrUpdateSticker(model);
}
/// <summary>
/// Gets a specific sticker within this guild.
/// </summary>
/// <param name="id">The id of the sticker to get.</param>
/// <returns>A sticker, if none is found then <see langword="null"/>.</returns>
public SocketCustomSticker GetSticker(ulong id)
=> GetStickerAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
/// <summary>
/// Gets a collection of all stickers within this guild.
/// </summary>
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param>
@@ -1629,7 +1638,8 @@ namespace Discord.WebSocket
=> await GetStickerAsync(id, mode, options);
async Task<IReadOnlyCollection<ICustomSticker>> IGuild.GetStickersAsync(CacheMode mode, RequestOptions options)
=> await GetStickersAsync(mode, options);
Task IGuild.DeleteStickerAsync(ICustomSticker sticker, RequestOptions options) => throw new NotImplementedException();
Task IGuild.DeleteStickerAsync(ICustomSticker sticker, RequestOptions options)
=> DeleteStickerAsync(_stickers[sticker.Id], options);

void IDisposable.Dispose()
{


+ 2
- 2
src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs View File

@@ -106,7 +106,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>();
/// <inheritdoc />
public virtual IReadOnlyCollection<Sticker> Stickers => ImmutableArray.Create<Sticker>();
public virtual IReadOnlyCollection<SocketSticker> Stickers => ImmutableArray.Create<SocketSticker>();
/// <inheritdoc />
public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) });

@@ -261,7 +261,7 @@ namespace Discord.WebSocket
IReadOnlyCollection<IMessageComponent> IMessage.Components => Components;

/// <inheritdoc />
IReadOnlyCollection<ISticker> IMessage.Stickers => Stickers;
IReadOnlyCollection<IStickerItem> IMessage.Stickers => Stickers;

internal void AddReaction(SocketReaction reaction)
{


+ 24
- 7
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -23,7 +23,7 @@ namespace Discord.WebSocket
private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>();
private ImmutableArray<SocketRole> _roleMentions = ImmutableArray.Create<SocketRole>();
private ImmutableArray<SocketUser> _userMentions = ImmutableArray.Create<SocketUser>();
private ImmutableArray<Sticker> _stickers = ImmutableArray.Create<Sticker>();
private ImmutableArray<SocketSticker> _stickers = ImmutableArray.Create<SocketSticker>();

/// <inheritdoc />
public override bool IsTTS => _isTTS;
@@ -48,7 +48,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
public override IReadOnlyCollection<SocketUser> MentionedUsers => _userMentions;
/// <inheritdoc />
public override IReadOnlyCollection<Sticker> Stickers => _stickers;
public override IReadOnlyCollection<SocketSticker> Stickers => _stickers;
/// <inheritdoc />
public IUserMessage ReferencedMessage => _referencedMessage;

@@ -162,18 +162,35 @@ namespace Discord.WebSocket
_referencedMessage = SocketUserMessage.Create(Discord, state, refMsgAuthor, Channel, refMsg);
}

if (model.Stickers.IsSpecified)
if (model.StickerItems.IsSpecified)
{
var value = model.Stickers.Value;
var value = model.StickerItems.Value;
if (value.Length > 0)
{
var stickers = ImmutableArray.CreateBuilder<Sticker>(value.Length);
var stickers = ImmutableArray.CreateBuilder<SocketSticker>(value.Length);
for (int i = 0; i < value.Length; i++)
stickers.Add(Sticker.Create(value[i]));
{
var stickerItem = value[i];
SocketSticker sticker = null;

if (guild != null)
{
sticker = guild.GetSticker(stickerItem.Id);
}

if(sticker == null)
{
sticker = Discord.GetSticker(stickerItem.Id);
}

// if its still null, create an unknown
sticker = SocketUnknownSticker.Create(Discord, stickerItem);
}

_stickers = stickers.ToImmutable();
}
else
_stickers = ImmutableArray.Create<Sticker>();
_stickers = ImmutableArray.Create<SocketSticker>();
}
}



src/Discord.Net.WebSocket/Entities/Messages/SocketCustomSticker.cs → src/Discord.Net.WebSocket/Entities/Stickers/SocketCustomSticker.cs View File

@@ -51,7 +51,7 @@ namespace Discord.WebSocket
if(!Guild.CurrentUser.GuildPermissions.Has(GuildPermission.ManageEmojisAndStickers))
throw new InvalidOperationException($"Missing permission {nameof(GuildPermission.ManageEmojisAndStickers)}");

var model = await GuildHelper.ModifyStickerAsync(this.Discord, this.Guild, this, func, options);
var model = await GuildHelper.ModifyStickerAsync(this.Discord, this.Guild.Id, this, func, options);

this.Update(model);
}
@@ -59,7 +59,7 @@ namespace Discord.WebSocket
/// <inheritdoc/>
public async Task DeleteAsync(RequestOptions options = null)
{
await GuildHelper.DeleteStickerAsync(Discord, Guild, this, options);
await GuildHelper.DeleteStickerAsync(Discord, this.Guild.Id, this, options);
Guild.RemoveSticker(this.Id);
}


src/Discord.Net.WebSocket/Entities/Messages/SocketSticker.cs → src/Discord.Net.WebSocket/Entities/Stickers/SocketSticker.cs View File

@@ -12,36 +12,42 @@ namespace Discord.WebSocket
public class SocketSticker : SocketEntity<ulong>, ISticker
{
/// <inheritdoc/>
public ulong PackId { get; private set; }
public virtual ulong PackId { get; private set; }

/// <inheritdoc/>
public string Name { get; private set; }
public string Name { get; internal set; }

/// <inheritdoc/>
public string Description { get; private set; }
public virtual string Description { get; private set; }

/// <inheritdoc/>
public IReadOnlyCollection<string> Tags { get; private set; }
public virtual IReadOnlyCollection<string> Tags { get; private set; }

/// <inheritdoc/>
public string Asset { get; private set; }
public virtual string Asset { get; private set; }

/// <inheritdoc/>
public string PreviewAsset { get; private set; }
public virtual string PreviewAsset { get; private set; }

/// <inheritdoc/>
public StickerFormatType FormatType { get; private set; }
public StickerFormatType Format { get; internal set; }

/// <inheritdoc/>
public string GetStickerUrl()
=> CDN.GetStickerUrl(this.Id, this.FormatType);
=> CDN.GetStickerUrl(this.Id, this.Format);

internal SocketSticker(DiscordSocketClient client, ulong id)
: base(client, id) { }

internal static SocketSticker Create(DiscordSocketClient client, Model model)
{
var entity = new SocketSticker(client, model.Id);
SocketSticker entity;

if (model.GuildId.IsSpecified)
entity = new SocketCustomSticker(client, model.Id, client.GetGuild(model.GuildId.Value), model.User.IsSpecified ? model.User.Value.Id : null);
else
entity = new SocketSticker(client, model.Id);

entity.Update(model);
return entity;
}
@@ -53,7 +59,7 @@ namespace Discord.WebSocket
this.PackId = model.PackId;
this.Asset = model.Asset;
this.PreviewAsset = model.PreviewAsset;
this.FormatType = model.FormatType;
this.Format = model.FormatType;

if (model.Tags.IsSpecified)
{

+ 60
- 0
src/Discord.Net.WebSocket/Entities/Stickers/SocketUnknownSticker.cs View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model = Discord.API.StickerItem;

namespace Discord.WebSocket
{
/// <summary>
/// Represents an unknown sticker received over the gateway.
/// </summary>
public class SocketUnknownSticker : SocketSticker
{
/// <inheritdoc/>
public override string Asset
=> null;

/// <inheritdoc/>
public override IReadOnlyCollection<string> Tags
=> null;

/// <inheritdoc/>
public override string Description
=> null;

/// <inheritdoc/>
public override ulong PackId
=> 0;

/// <inheritdoc/>
public override string PreviewAsset
=> null;

internal SocketUnknownSticker(DiscordSocketClient client, ulong id)
: base(client, id) { }

internal static SocketUnknownSticker Create(DiscordSocketClient client, Model model)
{
var entity = new SocketUnknownSticker(client, model.Id);
entity.Update(model);
return entity;
}

internal void Update(Model model)
{
this.Name = model.Name;
this.Format = model.FormatType;
}

/// <summary>
/// Attempts to try to find the sticker.
/// </summary>
/// <returns>
/// The sticker representing this unknown stickers Id, if none is found then <see langword="null"/>.
/// </returns>
public Task<SocketSticker> ResolveAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
=> Discord.GetStickerAsync(this.Id, mode, options);
}
}

Loading…
Cancel
Save