@@ -19,6 +19,7 @@ using PresenceModel = Discord.API.Presence;
using RoleModel = Discord.API.Role;
using UserModel = Discord.API.User;
using VoiceStateModel = Discord.API.VoiceState;
using StickerModel = Discord.API.Sticker;
namespace Discord.WebSocket
{
@@ -36,7 +37,9 @@ namespace Discord.WebSocket
private ConcurrentDictionary<ulong, SocketGuildUser> _members;
private ConcurrentDictionary<ulong, SocketRole> _roles;
private ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates;
private ConcurrentDictionary<ulong, SocketCustomSticker> _stickers;
private ImmutableArray<GuildEmote> _emotes;
private ImmutableArray<string> _features;
private AudioClient _audioClient;
#pragma warning restore IDISP002, IDISP006
@@ -322,6 +325,11 @@ namespace Discord.WebSocket
}
/// <inheritdoc />
public IReadOnlyCollection<GuildEmote> Emotes => _emotes;
/// <summary>
/// Gets a collection of all custom stickers for this guild.
/// </summary>
public IReadOnlyCollection<SocketCustomSticker> Stickers
=> _stickers.Select(x => x.Value).ToImmutableArray();
/// <inheritdoc />
public IReadOnlyCollection<string> Features => _features;
/// <summary>
@@ -440,6 +448,8 @@ namespace Discord.WebSocket
}
_voiceStates = voiceStates;
_syncPromise = new TaskCompletionSource<bool>();
_downloaderPromise = new TaskCompletionSource<bool>();
var _ = _syncPromise.TrySetResultAsync(true);
@@ -509,6 +519,23 @@ namespace Discord.WebSocket
}
}
_roles = roles;
if (model.Stickers != null)
{
var stickers = new ConcurrentDictionary<ulong, SocketCustomSticker>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Stickers.Length * 1.05));
for (int i = 0; i < model.Stickers.Length; i++)
{
var sticker = model.Stickers[i];
if (sticker.User.IsSpecified)
AddOrUpdateUser(sticker.User.Value);
var entity = SocketCustomSticker.Create(Discord, sticker, this, sticker.User.IsSpecified ? sticker.User.Value.Id : null);
stickers.TryAdd(sticker.Id, entity);
}
}
else
_stickers = new ConcurrentDictionary<ulong, SocketCustomSticker>(ConcurrentHashSet.DefaultConcurrencyLevel, 7);
}
/*internal void Update(ClientState state, GuildSyncModel model) //TODO remove? userbot related
{
@@ -898,6 +925,33 @@ namespace Discord.WebSocket
return role;
}
internal SocketCustomSticker AddSticker(StickerModel model)
{
if (model.User.IsSpecified)
AddOrUpdateUser(model.User.Value);
var sticker = SocketCustomSticker.Create(Discord, model, this, model.User.IsSpecified ? model.User.Value.Id : null);
_stickers[model.Id] = sticker;
return sticker;
}
internal SocketCustomSticker AddOrUpdateSticker(StickerModel model)
{
if (_stickers.TryGetValue(model.Id, out SocketCustomSticker sticker))
_stickers[model.Id].Update(model);
else
sticker = AddSticker(model);
return sticker;
}
internal SocketCustomSticker RemoveSticker(ulong id)
{
if (_stickers.TryRemove(id, out SocketCustomSticker sticker))
return sticker;
return null;
}
//Users
/// <inheritdoc />
public Task<RestGuildUser> AddGuildUserAsync(ulong id, string accessToken, Action<AddGuildUserProperties> func = null, RequestOptions options = null)
@@ -1109,6 +1163,92 @@ namespace Discord.WebSocket
public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null)
=> GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options);
//Stickers
/// <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="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>
public async ValueTask<SocketCustomSticker> GetStickerAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
var sticker = _stickers[id];
if (sticker != null)
return sticker;
if (mode == CacheMode.CacheOnly)
return null;
var model = await Discord.ApiClient.GetGuildStickerAsync(this.Id, id, options).ConfigureAwait(false);
if (model == null)
return null;
return AddOrUpdateSticker(model);
}
/// <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>
/// <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 ValueTask<IReadOnlyCollection<SocketCustomSticker>> GetStickersAsync(CacheMode mode = CacheMode.AllowDownload,
RequestOptions options = null)
{
if (this.Stickers.Count > 0)
return this.Stickers;
if (mode == CacheMode.CacheOnly)
return ImmutableArray.Create<SocketCustomSticker>();
var models = await Discord.ApiClient.ListGuildStickersAsync(this.Id, options).ConfigureAwait(false);
List<SocketCustomSticker> stickers = new();
foreach (var model in models)
{
stickers.Add(AddOrUpdateSticker(model));
}
return 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<SocketCustomSticker> 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 AddOrUpdateSticker(model);
}
/// <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(SocketCustomSticker sticker, RequestOptions options = null)
=> sticker.DeleteAsync(options);
//Voice States
internal async Task<SocketVoiceState> AddOrUpdateVoiceStateAsync(ClientState state, VoiceStateModel model)
{
@@ -1332,6 +1472,8 @@ namespace Discord.WebSocket
int? IGuild.ApproximateMemberCount => null;
/// <inheritdoc />
int? IGuild.ApproximatePresenceCount => null;
/// <inheritdoc />
IReadOnlyCollection<ICustomSticker> IGuild.Stickers => Stickers;
/// <inheritdoc />
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options)
@@ -1481,6 +1623,13 @@ namespace Discord.WebSocket
/// <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)
=> 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();
void IDisposable.Dispose()
{
@@ -1489,6 +1638,6 @@ namespace Discord.WebSocket
_audioClient?.Dispose();
}
}
}