diff --git a/DisCatSharp/Clients/DiscordClient.cs b/DisCatSharp/Clients/DiscordClient.cs
index f9424b1c0..0c2d1df4a 100644
--- a/DisCatSharp/Clients/DiscordClient.cs
+++ b/DisCatSharp/Clients/DiscordClient.cs
@@ -1,1329 +1,1341 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.EventArgs;
using DisCatSharp.Exceptions;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace DisCatSharp
{
///
/// A Discord API wrapper.
///
public sealed partial class DiscordClient : BaseDiscordClient
{
#region Internal Fields/Properties
internal bool IsShard = false;
///
/// Gets the message cache.
///
internal RingBuffer MessageCache { get; }
private List _extensions = new();
private StatusUpdate _status;
///
/// Gets the connection lock.
///
private readonly ManualResetEventSlim _connectionLock = new(true);
#endregion
#region Public Fields/Properties
///
/// Gets the gateway protocol version.
///
public int GatewayVersion { get; internal set; }
///
/// Gets the gateway session information for this client.
///
public GatewayInfo GatewayInfo { get; internal set; }
///
/// Gets the gateway URL.
///
public Uri GatewayUri { get; internal set; }
///
/// Gets the total number of shards the bot is connected to.
///
public int ShardCount => this.GatewayInfo != null
? this.GatewayInfo.ShardCount
: this.Configuration.ShardCount;
///
/// Gets the currently connected shard ID.
///
public int ShardId
=> this.Configuration.ShardId;
///
/// Gets the intents configured for this client.
///
public DiscordIntents Intents
=> this.Configuration.Intents;
///
/// Gets a dictionary of guilds that this client is in. The dictionary's key is the guild ID. Note that the
/// guild objects in this dictionary will not be filled in if the specific guilds aren't available (the
/// or events haven't been fired yet)
///
public override IReadOnlyDictionary Guilds { get; }
internal ConcurrentDictionary GuildsInternal = new();
///
/// Gets the websocket latency for this client.
///
public int Ping
=> Volatile.Read(ref this._ping);
private int _ping;
///
/// Gets the collection of presences held by this client.
///
public IReadOnlyDictionary Presences
=> this._presencesLazy.Value;
internal Dictionary PresencesInternal = new();
private Lazy> _presencesLazy;
///
/// Gets the collection of presences held by this client.
///
public IReadOnlyDictionary EmbeddedActivities
=> this._embeddedActivitiesLazy.Value;
internal Dictionary EmbeddedActivitiesInternal = new();
private Lazy> _embeddedActivitiesLazy;
#endregion
#region Constructor/Internal Setup
///
/// Initializes a new instance of .
///
/// Specifies configuration parameters.
public DiscordClient(DiscordConfiguration config)
: base(config)
{
if (this.Configuration.MessageCacheSize > 0)
{
var intents = this.Configuration.Intents;
this.MessageCache = intents.HasIntent(DiscordIntents.GuildMessages) || intents.HasIntent(DiscordIntents.DirectMessages)
? new RingBuffer(this.Configuration.MessageCacheSize)
: null;
}
this.InternalSetup();
this.Guilds = new ReadOnlyConcurrentDictionary(this.GuildsInternal);
}
///
/// Internal setup of the Client.
///
internal void InternalSetup()
{
this._clientErrored = new AsyncEvent("CLIENT_ERRORED", EventExecutionLimit, this.Goof);
this._socketErrored = new AsyncEvent("SOCKET_ERRORED", EventExecutionLimit, this.Goof);
this._socketOpened = new AsyncEvent("SOCKET_OPENED", EventExecutionLimit, this.EventErrorHandler);
this._socketClosed = new AsyncEvent("SOCKET_CLOSED", EventExecutionLimit, this.EventErrorHandler);
this._ready = new AsyncEvent("READY", EventExecutionLimit, this.EventErrorHandler);
this._resumed = new AsyncEvent("RESUMED", EventExecutionLimit, this.EventErrorHandler);
this._channelCreated = new AsyncEvent("CHANNEL_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._channelUpdated = new AsyncEvent("CHANNEL_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._channelDeleted = new AsyncEvent("CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._dmChannelDeleted = new AsyncEvent("DM_CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._channelPinsUpdated = new AsyncEvent("CHANNEL_PINS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildCreated = new AsyncEvent("GUILD_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._guildAvailable = new AsyncEvent("GUILD_AVAILABLE", EventExecutionLimit, this.EventErrorHandler);
this._guildUpdated = new AsyncEvent("GUILD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildDeleted = new AsyncEvent("GUILD_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._guildUnavailable = new AsyncEvent("GUILD_UNAVAILABLE", EventExecutionLimit, this.EventErrorHandler);
this._guildDownloadCompletedEv = new AsyncEvent("GUILD_DOWNLOAD_COMPLETED", EventExecutionLimit, this.EventErrorHandler);
this._inviteCreated = new AsyncEvent("INVITE_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._inviteDeleted = new AsyncEvent("INVITE_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._messageCreated = new AsyncEvent("MESSAGE_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._presenceUpdated = new AsyncEvent("PRESENCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildBanAdded = new AsyncEvent("GUILD_BAN_ADD", EventExecutionLimit, this.EventErrorHandler);
this._guildBanRemoved = new AsyncEvent("GUILD_BAN_REMOVED", EventExecutionLimit, this.EventErrorHandler);
this._guildEmojisUpdated = new AsyncEvent("GUILD_EMOJI_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildStickersUpdated = new AsyncEvent("GUILD_STICKER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildIntegrationsUpdated = new AsyncEvent("GUILD_INTEGRATIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberAdded = new AsyncEvent("GUILD_MEMBER_ADD", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberRemoved = new AsyncEvent("GUILD_MEMBER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberUpdated = new AsyncEvent("GUILD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildRoleCreated = new AsyncEvent("GUILD_ROLE_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._guildRoleUpdated = new AsyncEvent("GUILD_ROLE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildRoleDeleted = new AsyncEvent("GUILD_ROLE_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._messageAcknowledged = new AsyncEvent("MESSAGE_ACKNOWLEDGED", EventExecutionLimit, this.EventErrorHandler);
this._messageUpdated = new AsyncEvent("MESSAGE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._messageDeleted = new AsyncEvent("MESSAGE_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._messagesBulkDeleted = new AsyncEvent("MESSAGE_BULK_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._interactionCreated = new AsyncEvent("INTERACTION_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._componentInteractionCreated = new AsyncEvent("COMPONENT_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
this._contextMenuInteractionCreated = new AsyncEvent("CONTEXT_MENU_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
this._typingStarted = new AsyncEvent("TYPING_STARTED", EventExecutionLimit, this.EventErrorHandler);
this._userSettingsUpdated = new AsyncEvent("USER_SETTINGS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._userUpdated = new AsyncEvent("USER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._voiceStateUpdated = new AsyncEvent("VOICE_STATE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._voiceServerUpdated = new AsyncEvent("VOICE_SERVER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildMembersChunked = new AsyncEvent("GUILD_MEMBERS_CHUNKED", EventExecutionLimit, this.EventErrorHandler);
this._unknownEvent = new AsyncEvent("UNKNOWN_EVENT", EventExecutionLimit, this.EventErrorHandler);
this._messageReactionAdded = new AsyncEvent("MESSAGE_REACTION_ADDED", EventExecutionLimit, this.EventErrorHandler);
this._messageReactionRemoved = new AsyncEvent("MESSAGE_REACTION_REMOVED", EventExecutionLimit, this.EventErrorHandler);
this._messageReactionsCleared = new AsyncEvent("MESSAGE_REACTIONS_CLEARED", EventExecutionLimit, this.EventErrorHandler);
this._messageReactionRemovedEmoji = new AsyncEvent("MESSAGE_REACTION_REMOVED_EMOJI", EventExecutionLimit, this.EventErrorHandler);
this._webhooksUpdated = new AsyncEvent("WEBHOOKS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._heartbeated = new AsyncEvent("HEARTBEATED", EventExecutionLimit, this.EventErrorHandler);
this._applicationCommandCreated = new AsyncEvent("APPLICATION_COMMAND_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._applicationCommandUpdated = new AsyncEvent("APPLICATION_COMMAND_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._applicationCommandDeleted = new AsyncEvent("APPLICATION_COMMAND_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._guildApplicationCommandCountUpdated = new AsyncEvent("GUILD_APPLICATION_COMMAND_COUNTS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._applicationCommandPermissionsUpdated = new AsyncEvent("APPLICATION_COMMAND_PERMISSIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildIntegrationCreated = new AsyncEvent("INTEGRATION_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._guildIntegrationUpdated = new AsyncEvent("INTEGRATION_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildIntegrationDeleted = new AsyncEvent("INTEGRATION_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._stageInstanceCreated = new AsyncEvent("STAGE_INSTANCE_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._stageInstanceUpdated = new AsyncEvent("STAGE_INSTANCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._stageInstanceDeleted = new AsyncEvent("STAGE_INSTANCE_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._threadCreated = new AsyncEvent("THREAD_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._threadUpdated = new AsyncEvent("THREAD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._threadDeleted = new AsyncEvent("THREAD_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._threadListSynced = new AsyncEvent("THREAD_LIST_SYNCED", EventExecutionLimit, this.EventErrorHandler);
this._threadMemberUpdated = new AsyncEvent("THREAD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._threadMembersUpdated = new AsyncEvent("THREAD_MEMBERS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._zombied = new AsyncEvent("ZOMBIED", EventExecutionLimit, this.EventErrorHandler);
this._payloadReceived = new AsyncEvent("PAYLOAD_RECEIVED", EventExecutionLimit, this.EventErrorHandler);
this._guildScheduledEventCreated = new AsyncEvent("GUILD_SCHEDULED_EVENT_CREATED", EventExecutionLimit, this.EventErrorHandler);
this._guildScheduledEventUpdated = new AsyncEvent("GUILD_SCHEDULED_EVENT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildScheduledEventDeleted = new AsyncEvent("GUILD_SCHEDULED_EVENT_DELETED", EventExecutionLimit, this.EventErrorHandler);
this._guildScheduledEventUserAdded = new AsyncEvent("GUILD_SCHEDULED_EVENT_USER_ADDED", EventExecutionLimit, this.EventErrorHandler);
this._guildScheduledEventUserRemoved = new AsyncEvent("GUILD_SCHEDULED_EVENT_USER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
this._embeddedActivityUpdated = new AsyncEvent("EMBEDDED_ACTIVITY_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberTimeoutAdded = new AsyncEvent("GUILD_MEMBER_TIMEOUT_ADDED", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberTimeoutChanged = new AsyncEvent("GUILD_MEMBER_TIMEOUT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
this._guildMemberTimeoutRemoved = new AsyncEvent("GUILD_MEMBER_TIMEOUT_REMOVED", EventExecutionLimit, this.EventErrorHandler);
this.GuildsInternal.Clear();
this._presencesLazy = new Lazy>(() => new ReadOnlyDictionary(this.PresencesInternal));
this._embeddedActivitiesLazy = new Lazy>(() => new ReadOnlyDictionary(this.EmbeddedActivitiesInternal));
}
#endregion
#region Client Extension Methods
///
/// Registers an extension with this client.
///
/// Extension to register.
public void AddExtension(BaseExtension ext)
{
ext.Setup(this);
this._extensions.Add(ext);
}
///
/// Retrieves a previously registered extension from this client.
///
/// The type of extension to retrieve.
/// The requested extension.
public T GetExtension() where T : BaseExtension
=> this._extensions.FirstOrDefault(x => x.GetType() == typeof(T)) as T;
#endregion
#region Public Connection Methods
///
/// Connects to the gateway.
///
/// The activity to set. Defaults to null.
/// The optional status to set. Defaults to null.
/// Since when is the client performing the specified activity. Defaults to null.
/// Thrown when an invalid token was provided.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task ConnectAsync(DiscordActivity activity = null, UserStatus? status = null, DateTimeOffset? idlesince = null)
{
// Check if connection lock is already set, and set it if it isn't
if (!this._connectionLock.Wait(0))
throw new InvalidOperationException("This client is already connected.");
this._connectionLock.Set();
var w = 7500;
var i = 5;
var s = false;
Exception cex = null;
if (activity == null && status == null && idlesince == null)
this._status = null;
else
{
var sinceUnix = idlesince != null ? (long?)Utilities.GetUnixTime(idlesince.Value) : null;
this._status = new StatusUpdate()
{
Activity = new TransportActivity(activity),
Status = status ?? UserStatus.Online,
IdleSince = sinceUnix,
IsAfk = idlesince != null,
ActivityInternal = activity
};
}
if (!this.IsShard)
{
if (this.Configuration.TokenType != TokenType.Bot)
this.Logger.LogWarning(LoggerEvents.Misc, "You are logging in with a token that is not a bot token. This is not officially supported by Discord, and can result in your account being terminated if you aren't careful.");
this.Logger.LogInformation(LoggerEvents.Startup, "Lib {0}, version {1}", this.BotLibrary, this.VersionString);
}
while (i-- > 0 || this.Configuration.ReconnectIndefinitely)
{
try
{
await this.InternalConnectAsync().ConfigureAwait(false);
s = true;
break;
}
catch (UnauthorizedException e)
{
FailConnection(this._connectionLock);
throw new Exception("Authentication failed. Check your token and try again.", e);
}
catch (PlatformNotSupportedException)
{
FailConnection(this._connectionLock);
throw;
}
catch (NotImplementedException)
{
FailConnection(this._connectionLock);
throw;
}
catch (Exception ex)
{
FailConnection(null);
cex = ex;
if (i <= 0 && !this.Configuration.ReconnectIndefinitely) break;
this.Logger.LogError(LoggerEvents.ConnectionFailure, ex, "Connection attempt failed, retrying in {0}s", w / 1000);
await Task.Delay(w).ConfigureAwait(false);
if (i > 0)
w *= 2;
}
}
if (!s && cex != null)
{
this._connectionLock.Set();
throw new Exception("Could not connect to Discord.", cex);
}
// non-closure, hence args
static void FailConnection(ManualResetEventSlim cl) =>
// unlock this (if applicable) so we can let others attempt to connect
cl?.Set();
}
///
/// Reconnects to the gateway.
///
/// Whether to start a new session.
public Task ReconnectAsync(bool startNewSession = false)
=> this.InternalReconnectAsync(startNewSession, code: startNewSession ? 1000 : 4002);
///
/// Disconnects from the gateway.
///
public async Task DisconnectAsync()
{
this.Configuration.AutoReconnect = false;
if (this.WebSocketClient != null)
await this.WebSocketClient.DisconnectAsync().ConfigureAwait(false);
}
#endregion
#region Public REST Methods
///
/// Gets a user.
///
/// Id of the user
/// Whether to fetch the user again. Defaults to true.
/// The requested user.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task GetUserAsync(ulong userId, bool fetch = true)
{
if (!fetch)
{
return this.TryGetCachedUserInternal(userId, out var usr) ? usr : new DiscordUser { Id = userId, Discord = this };
}
else
{
var usr = await this.ApiClient.GetUserAsync(userId).ConfigureAwait(false);
usr = this.UserCache.AddOrUpdate(userId, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
old.BannerHash = usr.BannerHash;
old.BannerColorInternal = usr.BannerColorInternal;
return old;
});
return usr;
}
}
///
/// Gets a channel.
///
/// The id of the channel to get.
/// The requested channel.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task GetChannelAsync(ulong id)
=> this.InternalGetCachedChannel(id) ?? await this.ApiClient.GetChannelAsync(id).ConfigureAwait(false);
///
/// Gets a thread.
///
/// The id of the thread to get.
/// The requested thread.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task GetThreadAsync(ulong id)
=> this.InternalGetCachedThread(id) ?? await this.ApiClient.GetThreadAsync(id).ConfigureAwait(false);
///
/// Sends a normal message.
///
/// The channel to send to.
/// The message content to send.
/// The message that was sent.
/// Thrown when the client does not have the permission.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SendMessageAsync(DiscordChannel channel, string content)
=> this.ApiClient.CreateMessageAsync(channel.Id, content, embeds: null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message with an embed.
///
/// The channel to send to.
/// The embed to attach to the message.
/// The message that was sent.
/// Thrown when the client does not have the permission.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SendMessageAsync(DiscordChannel channel, DiscordEmbed embed)
=> this.ApiClient.CreateMessageAsync(channel.Id, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message with content and an embed.
///
/// Channel to send to.
/// The message content to send.
/// The embed to attach to the message.
/// The message that was sent.
/// Thrown when the client does not have the permission.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SendMessageAsync(DiscordChannel channel, string content, DiscordEmbed embed)
=> this.ApiClient.CreateMessageAsync(channel.Id, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message with the .
///
/// The channel to send the message to.
/// The message builder.
/// The message that was sent.
/// Thrown when the client does not have the permission if TTS is false and if TTS is true.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SendMessageAsync(DiscordChannel channel, DiscordMessageBuilder builder)
=> this.ApiClient.CreateMessageAsync(channel.Id, builder);
///
/// Sends a message with an .
///
/// The channel to send the message to.
/// The message builder.
/// The message that was sent.
/// Thrown when the client does not have the permission if TTS is false and if TTS is true.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SendMessageAsync(DiscordChannel channel, Action action)
{
var builder = new DiscordMessageBuilder();
action(builder);
return this.ApiClient.CreateMessageAsync(channel.Id, builder);
}
///
/// Creates a guild. This requires the bot to be in less than 10 guilds total.
///
/// Name of the guild.
/// Voice region of the guild.
/// Stream containing the icon for the guild.
/// Verification level for the guild.
/// Default message notification settings for the guild.
/// System channel flags fopr the guild.
/// The created guild.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task CreateGuildAsync(string name, string region = null, Optional icon = default, VerificationLevel? verificationLevel = null,
DefaultMessageNotifications? defaultMessageNotifications = null, SystemChannelFlags? systemChannelFlags = null)
{
var iconb64 = Optional.FromNoValue();
if (icon.HasValue && icon.Value != null)
using (var imgtool = new ImageTool(icon.Value))
iconb64 = imgtool.GetBase64();
else if (icon.HasValue)
iconb64 = null;
return this.ApiClient.CreateGuildAsync(name, region, iconb64, verificationLevel, defaultMessageNotifications, systemChannelFlags);
}
///
/// Creates a guild from a template. This requires the bot to be in less than 10 guilds total.
///
/// The template code.
/// Name of the guild.
/// Stream containing the icon for the guild.
/// The created guild.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task CreateGuildFromTemplateAsync(string code, string name, Optional icon = default)
{
var iconb64 = Optional.FromNoValue();
if (icon.HasValue && icon.Value != null)
using (var imgtool = new ImageTool(icon.Value))
iconb64 = imgtool.GetBase64();
else if (icon.HasValue)
iconb64 = null;
return this.ApiClient.CreateGuildFromTemplateAsync(code, name, iconb64);
}
///
/// Executes a raw request.
///
///
///
/// var request = await Client.ExecuteRawRequestAsync(RestRequestMethod.GET, $"{Endpoints.CHANNELS}/243184972190742178964/{Endpoints.INVITES}");
/// List<DiscordInvite> invites = DiscordJson.ToDiscordObject<List<DiscordInvite>>(request.Response);
///
///
/// The method.
/// The route.
/// The route parameters.
/// The json body.
/// The addditional headers.
/// The ratelimit wait override.
/// Thrown when the ressource does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
/// A awaitable RestResponse
[Obsolete("This is no longer needed. Use DiscordClient.RestClient instead.", false)]
public async Task ExecuteRawRequestAsync(RestRequestMethod method, string route, object routeParams, string jsonBody = null, Dictionary additionalHeaders = null, double? ratelimitWaitOverride = null)
{
var bucket = this.ApiClient.Rest.GetBucket(method, route, routeParams, out var path);
var url = Utilities.GetApiUriFor(path, this.Configuration);
var res = await this.ApiClient.DoRequestAsync(this, bucket, url, method, route, additionalHeaders, DiscordJson.SerializeObject(jsonBody), ratelimitWaitOverride);
return res;
}
///
/// Gets a guild.
/// Setting to true will make a REST request.
///
/// The guild ID to search for.
/// Whether to include approximate presence and member counts in the returned guild.
/// The requested Guild.
/// Thrown when the guild does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task GetGuildAsync(ulong id, bool? withCounts = null)
{
if (this.GuildsInternal.TryGetValue(id, out var guild) && (!withCounts.HasValue || !withCounts.Value))
return guild;
guild = await this.ApiClient.GetGuildAsync(id, withCounts).ConfigureAwait(false);
var channels = await this.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false);
foreach (var channel in channels) guild.ChannelsInternal[channel.Id] = channel;
return guild;
}
///
/// Gets a guild preview.
///
/// The guild ID.
///
/// Thrown when the guild does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetGuildPreviewAsync(ulong id)
=> this.ApiClient.GetGuildPreviewAsync(id);
///
/// Gets an invite.
///
/// The invite code.
/// Whether to include presence and total member counts in the returned invite.
/// Whether to include the expiration date in the returned invite.
/// The scheduled event id.
/// The requested Invite.
/// Thrown when the invite does not exists.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetInviteByCodeAsync(string code, bool? withCounts = null, bool? withExpiration = null, ulong? scheduledEventId = null)
=> this.ApiClient.GetInviteAsync(code, withCounts, withExpiration, scheduledEventId);
///
/// Gets a list of user connections.
///
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task> GetConnectionsAsync()
=> this.ApiClient.GetUserConnectionsAsync();
///
/// Gets a sticker.
///
/// The requested sticker.
/// The id of the sticker.
/// Thrown when the sticker does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetStickerAsync(ulong id)
=> this.ApiClient.GetStickerAsync(id);
///
/// Gets all nitro sticker packs.
///
/// List of sticker packs.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task> GetStickerPacksAsync()
=> this.ApiClient.GetStickerPacksAsync();
///
/// Gets the In-App OAuth Url.
///
/// Defaults to .
/// Redirect Uri.
/// Defaults to .
/// The OAuth Url
public Uri GetInAppOAuth(Permissions permissions = Permissions.None, OAuthScopes scopes = OAuthScopes.BOT_DEFAULT, string redir = null)
{
permissions &= PermissionMethods.FullPerms;
// hey look, it's not all annoying and blue :P
return new Uri(new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
.AddParameter("client_id", this.CurrentApplication.Id.ToString(CultureInfo.InvariantCulture))
.AddParameter("scope", OAuth.ResolveScopes(scopes))
.AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
.AddParameter("state", "")
.AddParameter("redirect_uri", redir ?? "")
.ToString());
}
///
/// Gets a webhook.
///
/// The target webhook id.
/// The requested webhook.
/// Thrown when the webhook does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetWebhookAsync(ulong id)
=> this.ApiClient.GetWebhookAsync(id);
///
/// Gets a webhook.
///
/// The target webhook id.
/// The target webhook token.
/// The requested webhook.
/// Thrown when the webhook does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetWebhookWithTokenAsync(ulong id, string token)
=> this.ApiClient.GetWebhookWithTokenAsync(id, token);
///
/// Updates current user's activity and status.
///
/// Activity to set.
/// Status of the user.
/// Since when is the client performing the specified activity.
///
public Task UpdateStatusAsync(DiscordActivity activity = null, UserStatus? userStatus = null, DateTimeOffset? idleSince = null)
=> this.InternalUpdateStatusAsync(activity, userStatus, idleSince);
///
/// Edits current user.
///
/// New username.
/// New avatar.
/// The modified user.
/// Thrown when the user does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task UpdateCurrentUserAsync(string username = null, Optional avatar = default)
{
var av64 = Optional.FromNoValue();
if (avatar.HasValue && avatar.Value != null)
using (var imgtool = new ImageTool(avatar.Value))
av64 = imgtool.GetBase64();
else if (avatar.HasValue)
av64 = null;
var usr = await this.ApiClient.ModifyCurrentUserAsync(username, av64).ConfigureAwait(false);
this.CurrentUser.Username = usr.Username;
this.CurrentUser.Discriminator = usr.Discriminator;
this.CurrentUser.AvatarHash = usr.AvatarHash;
return this.CurrentUser;
}
///
/// Gets a guild template by the code.
///
/// The code of the template.
/// The guild template for the code.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetTemplateAsync(string code)
=> this.ApiClient.GetTemplateAsync(code);
///
/// Gets all the global application commands for this application.
///
/// A list of global application commands.
public Task> GetGlobalApplicationCommandsAsync() =>
this.ApiClient.GetGlobalApplicationCommandsAsync(this.CurrentApplication.Id);
///
/// Overwrites the existing global application commands. New commands are automatically created and missing commands are automatically deleted.
///
/// The list of commands to overwrite with.
/// The list of global commands.
public Task> BulkOverwriteGlobalApplicationCommandsAsync(IEnumerable commands) =>
this.ApiClient.BulkOverwriteGlobalApplicationCommandsAsync(this.CurrentApplication.Id, commands);
///
/// Creates or overwrites a global application command.
///
/// The command to create.
/// The created command.
public Task CreateGlobalApplicationCommandAsync(DiscordApplicationCommand command) =>
this.ApiClient.CreateGlobalApplicationCommandAsync(this.CurrentApplication.Id, command);
///
/// Gets a global application command by its id.
///
/// The id of the command to get.
/// The command with the id.
public Task GetGlobalApplicationCommandAsync(ulong commandId) =>
this.ApiClient.GetGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
///
/// Edits a global application command.
///
/// The id of the command to edit.
/// Action to perform.
/// The edited command.
public async Task EditGlobalApplicationCommandAsync(ulong commandId, Action action)
{
var mdl = new ApplicationCommandEditModel();
action(mdl);
var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
return await this.ApiClient.EditGlobalApplicationCommandAsync(applicationId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.DefaultPermission, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
}
///
/// Deletes a global application command.
///
/// The id of the command to delete.
public Task DeleteGlobalApplicationCommandAsync(ulong commandId) =>
this.ApiClient.DeleteGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
///
/// Gets all the application commands for a guild.
///
/// The id of the guild to get application commands for.
/// A list of application commands in the guild.
public Task> GetGuildApplicationCommandsAsync(ulong guildId) =>
this.ApiClient.GetGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId);
///
/// Overwrites the existing application commands in a guild. New commands are automatically created and missing commands are automatically deleted.
///
/// The id of the guild.
/// The list of commands to overwrite with.
/// The list of guild commands.
public Task> BulkOverwriteGuildApplicationCommandsAsync(ulong guildId, IEnumerable commands) =>
this.ApiClient.BulkOverwriteGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId, commands);
///
/// Creates or overwrites a guild application command.
///
/// The id of the guild to create the application command in.
/// The command to create.
/// The created command.
public Task CreateGuildApplicationCommandAsync(ulong guildId, DiscordApplicationCommand command) =>
this.ApiClient.CreateGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, command);
///
/// Gets a application command in a guild by its id.
///
/// The id of the guild the application command is in.
/// The id of the command to get.
/// The command with the id.
public Task GetGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
this.ApiClient.GetGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
///
/// Edits a application command in a guild.
///
/// The id of the guild the application command is in.
/// The id of the command to edit.
/// Action to perform.
/// The edited command.
public async Task EditGuildApplicationCommandAsync(ulong guildId, ulong commandId, Action action)
{
var mdl = new ApplicationCommandEditModel();
action(mdl);
var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
return await this.ApiClient.EditGuildApplicationCommandAsync(applicationId, guildId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.DefaultPermission, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
}
///
/// Deletes a application command in a guild.
///
/// The id of the guild to delete the application command in.
/// The id of the command.
public Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
this.ApiClient.DeleteGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
///
/// Gets all command permissions for a guild.
///
/// The target guild.
public Task> GetGuildApplicationCommandPermissionsAsync(ulong guildId) =>
this.ApiClient.GetGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId);
///
/// Gets the permissions for a guild command.
///
/// The target guild.
/// The target command id.
public Task GetApplicationCommandPermissionAsync(ulong guildId, ulong commandId) =>
this.ApiClient.GetGuildApplicationCommandPermissionAsync(this.CurrentApplication.Id, guildId, commandId);
///
/// Overwrites the existing permissions for a application command in a guild. New permissions are automatically created and missing permissions are deleted.
/// A command takes up to 10 permission overwrites.
///
/// The id of the guild.
/// The id of the command.
/// List of permissions.
public Task OverwriteGuildApplicationCommandPermissionsAsync(ulong guildId, ulong commandId, IEnumerable permissions) =>
this.ApiClient.OverwriteGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId, commandId, permissions);
///
/// Overwrites the existing application command permissions in a guild. New permissions are automatically created and missing permissions are deleted.
/// Each command takes up to 10 permission overwrites.
///
/// The id of the guild.
/// The list of permissions to overwrite with.
public Task> BulkOverwriteGuildApplicationCommandsAsync(ulong guildId, IEnumerable permissionsOverwrites) =>
this.ApiClient.BulkOverwriteGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId, permissionsOverwrites);
+
+ ///
+ /// Overwrites the default permissions for the application commands.
+ ///
+ /// The id of the guild.
+ /// Permission for @everyone.
+ /// Permission for all members in all channels.
+ /// List of permissions.
+ ///
+ public Task OverwriteDefaultGuildApplicationCommandPermissionsAsync(ulong guildId, bool everyonePermission, bool allChannelsPermission, IEnumerable permissions)
+ => this.ApiClient.OverwriteDefaultGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId, everyonePermission, allChannelsPermission, permissions);
+
#endregion
#region Internal Caching Methods
///
/// Gets the internal chached threads.
///
/// The target thread id.
/// The requested thread.
internal DiscordThreadChannel InternalGetCachedThread(ulong threadId)
{
foreach (var guild in this.Guilds.Values)
if (guild.Threads.TryGetValue(threadId, out var foundThread))
return foundThread;
return null;
}
///
/// Gets the internal chached scheduled event.
///
/// The target scheduled event id.
/// The requested scheduled event.
internal DiscordScheduledEvent InternalGetCachedScheduledEvent(ulong scheduledEventId)
{
foreach (var guild in this.Guilds.Values)
if (guild.ScheduledEvents.TryGetValue(scheduledEventId, out var foundScheduledEvent))
return foundScheduledEvent;
return null;
}
///
/// Gets the internal chached channel.
///
/// The target channel id.
/// The requested channel.
internal DiscordChannel InternalGetCachedChannel(ulong channelId)
{
foreach (var guild in this.Guilds.Values)
if (guild.Channels.TryGetValue(channelId, out var foundChannel))
return foundChannel;
return null;
}
///
/// Gets the internal chached guild.
///
/// The target guild id.
/// The requested guild.
internal DiscordGuild InternalGetCachedGuild(ulong? guildId)
{
if (this.GuildsInternal != null && guildId.HasValue)
{
if (this.GuildsInternal.TryGetValue(guildId.Value, out var guild))
return guild;
}
return null;
}
///
/// Updates a message.
///
/// The message to update.
/// The author to update.
/// The guild to update.
/// The member to update.
private void UpdateMessage(DiscordMessage message, TransportUser author, DiscordGuild guild, TransportMember member)
{
if (author != null)
{
var usr = new DiscordUser(author) { Discord = this };
if (member != null)
member.User = author;
message.Author = this.UpdateUser(usr, guild?.Id, guild, member);
}
var channel = this.InternalGetCachedChannel(message.ChannelId);
if (channel != null) return;
channel = !message.GuildId.HasValue
? new DiscordDmChannel
{
Id = message.ChannelId,
Discord = this,
Type = ChannelType.Private
}
: new DiscordChannel
{
Id = message.ChannelId,
Discord = this
};
message.Channel = channel;
}
///
/// Updates a scheduled event.
///
/// The scheduled event to update.
/// The guild to update.
/// The updated scheduled event.
private DiscordScheduledEvent UpdateScheduledEvent(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
{
if (scheduledEvent != null)
{
_ = guild.ScheduledEventsInternal.AddOrUpdate(scheduledEvent.Id, scheduledEvent, (id, old) =>
{
old.Discord = this;
old.Description = scheduledEvent.Description;
old.ChannelId = scheduledEvent.ChannelId;
old.EntityId = scheduledEvent.EntityId;
old.EntityType = scheduledEvent.EntityType;
old.EntityMetadata = scheduledEvent.EntityMetadata;
old.PrivacyLevel = scheduledEvent.PrivacyLevel;
old.Name = scheduledEvent.Name;
old.Status = scheduledEvent.Status;
old.UserCount = scheduledEvent.UserCount;
old.ScheduledStartTimeRaw = scheduledEvent.ScheduledStartTimeRaw;
old.ScheduledEndTimeRaw = scheduledEvent.ScheduledEndTimeRaw;
return old;
});
}
return scheduledEvent;
}
///
/// Updates a user.
///
/// The user to update.
/// The guild id to update.
/// The guild to update.
/// The member to update.
/// The updated user.
private DiscordUser UpdateUser(DiscordUser usr, ulong? guildId, DiscordGuild guild, TransportMember mbr)
{
if (mbr != null)
{
if (mbr.User != null)
{
usr = new DiscordUser(mbr.User) { Discord = this };
_ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
usr = new DiscordMember(mbr) { Discord = this, GuildId = guildId.Value };
}
var intents = this.Configuration.Intents;
DiscordMember member = default;
if (!intents.HasAllPrivilegedIntents() || guild.IsLarge) // we have the necessary privileged intents, no need to worry about caching here unless guild is large.
{
if (guild?.MembersInternal.TryGetValue(usr.Id, out member) == false)
{
if (intents.HasIntent(DiscordIntents.GuildMembers) || this.Configuration.AlwaysCacheMembers) // member can be updated by events, so cache it
{
guild.MembersInternal.TryAdd(usr.Id, (DiscordMember)usr);
}
}
else if (intents.HasIntent(DiscordIntents.GuildPresences) || this.Configuration.AlwaysCacheMembers) // we can attempt to update it if it's already in cache.
{
if (!intents.HasIntent(DiscordIntents.GuildMembers)) // no need to update if we already have the member events
{
_ = guild.MembersInternal.TryUpdate(usr.Id, (DiscordMember)usr, member);
}
}
}
}
else if (usr.Username != null) // check if not a skeleton user
{
_ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
}
return usr;
}
///
/// Updates the cached scheduled events in a guild.
///
/// The guild.
/// The raw events.
private void UpdateCachedScheduledEvent(DiscordGuild guild, JArray rawEvents)
{
if (this._disposed)
return;
if (rawEvents != null)
{
guild.ScheduledEventsInternal.Clear();
foreach (var xj in rawEvents)
{
var xtm = xj.ToDiscordObject();
xtm.Discord = this;
guild.ScheduledEventsInternal[xtm.Id] = xtm;
}
}
}
///
/// Updates the cached guild.
///
/// The new guild.
/// The raw members.
private void UpdateCachedGuild(DiscordGuild newGuild, JArray rawMembers)
{
if (this._disposed)
return;
if (!this.GuildsInternal.ContainsKey(newGuild.Id))
this.GuildsInternal[newGuild.Id] = newGuild;
var guild = this.GuildsInternal[newGuild.Id];
if (newGuild.ChannelsInternal != null && newGuild.ChannelsInternal.Count > 0)
{
foreach (var channel in newGuild.ChannelsInternal.Values)
{
if (guild.ChannelsInternal.TryGetValue(channel.Id, out _)) continue;
foreach (var overwrite in channel.PermissionOverwritesInternal)
{
overwrite.Discord = this;
overwrite.ChannelId = channel.Id;
}
guild.ChannelsInternal[channel.Id] = channel;
}
}
if (newGuild.ThreadsInternal != null && newGuild.ThreadsInternal.Count > 0)
{
foreach (var thread in newGuild.ThreadsInternal.Values)
{
if (guild.ThreadsInternal.TryGetValue(thread.Id, out _)) continue;
guild.ThreadsInternal[thread.Id] = thread;
}
}
if (newGuild.ScheduledEventsInternal != null && newGuild.ScheduledEventsInternal.Count > 0)
{
foreach (var @event in newGuild.ScheduledEventsInternal.Values)
{
if (guild.ScheduledEventsInternal.TryGetValue(@event.Id, out _)) continue;
guild.ScheduledEventsInternal[@event.Id] = @event;
}
}
foreach (var newEmoji in newGuild.EmojisInternal.Values)
_ = guild.EmojisInternal.GetOrAdd(newEmoji.Id, _ => newEmoji);
foreach (var newSticker in newGuild.StickersInternal.Values)
_ = guild.StickersInternal.GetOrAdd(newSticker.Id, _ => newSticker);
foreach (var newStageInstance in newGuild.StageInstancesInternal.Values)
_ = guild.StageInstancesInternal.GetOrAdd(newStageInstance.Id, _ => newStageInstance);
if (rawMembers != null)
{
guild.MembersInternal.Clear();
foreach (var xj in rawMembers)
{
var xtm = xj.ToDiscordObject();
var xu = new DiscordUser(xtm.User) { Discord = this };
_ = this.UserCache.AddOrUpdate(xtm.User.Id, xu, (id, old) =>
{
old.Username = xu.Username;
old.Discriminator = xu.Discriminator;
old.AvatarHash = xu.AvatarHash;
old.PremiumType = xu.PremiumType;
return old;
});
guild.MembersInternal[xtm.User.Id] = new DiscordMember(xtm) { Discord = this, GuildId = guild.Id };
}
}
foreach (var role in newGuild.RolesInternal.Values)
{
if (guild.RolesInternal.TryGetValue(role.Id, out _)) continue;
role.GuildId = guild.Id;
guild.RolesInternal[role.Id] = role;
}
guild.Name = newGuild.Name;
guild.AfkChannelId = newGuild.AfkChannelId;
guild.AfkTimeout = newGuild.AfkTimeout;
guild.DefaultMessageNotifications = newGuild.DefaultMessageNotifications;
guild.RawFeatures = newGuild.RawFeatures;
guild.IconHash = newGuild.IconHash;
guild.MfaLevel = newGuild.MfaLevel;
guild.OwnerId = newGuild.OwnerId;
guild.VoiceRegionId = newGuild.VoiceRegionId;
guild.SplashHash = newGuild.SplashHash;
guild.VerificationLevel = newGuild.VerificationLevel;
guild.WidgetEnabled = newGuild.WidgetEnabled;
guild.WidgetChannelId = newGuild.WidgetChannelId;
guild.ExplicitContentFilter = newGuild.ExplicitContentFilter;
guild.PremiumTier = newGuild.PremiumTier;
guild.PremiumSubscriptionCount = newGuild.PremiumSubscriptionCount;
guild.PremiumProgressBarEnabled = newGuild.PremiumProgressBarEnabled;
guild.BannerHash = newGuild.BannerHash;
guild.Description = newGuild.Description;
guild.VanityUrlCode = newGuild.VanityUrlCode;
guild.SystemChannelId = newGuild.SystemChannelId;
guild.SystemChannelFlags = newGuild.SystemChannelFlags;
guild.DiscoverySplashHash = newGuild.DiscoverySplashHash;
guild.MaxMembers = newGuild.MaxMembers;
guild.MaxPresences = newGuild.MaxPresences;
guild.ApproximateMemberCount = newGuild.ApproximateMemberCount;
guild.ApproximatePresenceCount = newGuild.ApproximatePresenceCount;
guild.MaxVideoChannelUsers = newGuild.MaxVideoChannelUsers;
guild.PreferredLocale = newGuild.PreferredLocale;
guild.RulesChannelId = newGuild.RulesChannelId;
guild.PublicUpdatesChannelId = newGuild.PublicUpdatesChannelId;
guild.ApplicationId = newGuild.ApplicationId;
// fields not sent for update:
// - guild.Channels
// - voice states
// - guild.JoinedAt = new_guild.JoinedAt;
// - guild.Large = new_guild.Large;
// - guild.MemberCount = Math.Max(new_guild.MemberCount, guild._members.Count);
// - guild.Unavailable = new_guild.Unavailable;
}
///
/// Populates the message reactions and cache.
///
/// The message.
/// The author.
/// The member.
private void PopulateMessageReactionsAndCache(DiscordMessage message, TransportUser author, TransportMember member)
{
var guild = message.Channel?.Guild ?? this.InternalGetCachedGuild(message.GuildId);
this.UpdateMessage(message, author, guild, member);
if (message.ReactionsInternal == null)
message.ReactionsInternal = new List();
foreach (var xr in message.ReactionsInternal)
xr.Emoji.Discord = this;
if (this.Configuration.MessageCacheSize > 0 && message.Channel != null)
this.MessageCache?.Add(message);
}
#endregion
#region Disposal
~DiscordClient()
{
this.Dispose();
}
///
/// Whether the client is disposed.
///
private bool _disposed;
///
/// Disposes the client.
///
public override void Dispose()
{
if (this._disposed)
return;
this._disposed = true;
GC.SuppressFinalize(this);
this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
this.ApiClient.Rest.Dispose();
this.CurrentUser = null;
var extensions = this._extensions; // prevent _extensions being modified during dispose
this._extensions = null;
foreach (var extension in extensions)
if (extension is IDisposable disposable)
disposable.Dispose();
try
{
this._cancelTokenSource?.Cancel();
this._cancelTokenSource?.Dispose();
}
catch { }
this.GuildsInternal = null;
this._heartbeatTask = null;
}
#endregion
}
}