diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs b/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
index a5711100b..a1988b98c 100644
--- a/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
@@ -1,133 +1,165 @@
// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
namespace DisCatSharp.Net;
public sealed partial class DiscordApiClient
{
///
/// Gets the current user async.
///
internal Task GetCurrentUserAsync()
=> this.GetUserAsync("@me");
///
/// Gets the user async.
///
/// The user_id.
internal Task GetUserAsync(ulong userId)
=> this.GetUserAsync(userId.ToString(CultureInfo.InvariantCulture));
///
/// Gets the user async.
///
/// The user_id.
internal async Task GetUserAsync(string userId)
{
var route = $"{Endpoints.USERS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var userRaw = JsonConvert.DeserializeObject(res.Response);
var duser = new DiscordUser(userRaw) { Discord = this.Discord };
return duser;
}
///
/// Modifies the current user async.
///
/// The username.
/// The base64_avatar.
internal async Task ModifyCurrentUserAsync(string username, Optional base64Avatar)
{
var pld = new RestUserUpdateCurrentPayload
{
Username = username,
AvatarBase64 = base64Avatar.ValueOrDefault(),
AvatarSet = base64Avatar.HasValue
};
var route = $"{Endpoints.USERS}{Endpoints.ME}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var userRaw = JsonConvert.DeserializeObject(res.Response);
return userRaw;
}
///
/// Gets the current user guilds async.
///
/// The limit.
/// The before.
/// The after.
internal async Task> GetCurrentUserGuildsAsync(int limit = 100, ulong? before = null, ulong? after = null)
{
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration)
.AddParameter($"limit", limit.ToString(CultureInfo.InvariantCulture));
if (before != null)
url.AddParameter("before", before.Value.ToString(CultureInfo.InvariantCulture));
if (after != null)
url.AddParameter("after", after.Value.ToString(CultureInfo.InvariantCulture));
var res = await this.DoRequestAsync(this.Discord, bucket, url.Build(), RestRequestMethod.GET, route).ConfigureAwait(false);
if (this.Discord is DiscordClient)
{
var guildsRaw = JsonConvert.DeserializeObject>(res.Response);
var glds = guildsRaw.Select(xug => (this.Discord as DiscordClient)?.GuildsInternal[xug.Id]);
return new ReadOnlyCollection(new List(glds));
}
else
{
return new ReadOnlyCollection(JsonConvert.DeserializeObject>(res.Response));
}
}
+
+ ///
+ /// Gets the users connections async.
+ ///
+ internal async Task> GetCurrentUserConnectionsAsync()
+ {
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CONNECTIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var connectionsRaw = JsonConvert.DeserializeObject>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
+
+ return new ReadOnlyCollection(new List(connectionsRaw));
+ }
+
+ ///
+ /// Gets the users connections async.
+ ///
+ internal async Task> GetUserConnectionsAsync(ulong user_id)
+ {
+ var route = $"{Endpoints.USERS}/:user_id{Endpoints.CONNECTIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { user_id = user_id.ToString() }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var connectionsRaw = JsonConvert.DeserializeObject>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
+
+ return new ReadOnlyCollection(new List(connectionsRaw));
+ }
}
diff --git a/DisCatSharp/Clients/Rest/DiscordApiClient.cs b/DisCatSharp/Clients/Rest/DiscordApiClient.cs
index badd33382..b89673ead 100644
--- a/DisCatSharp/Clients/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Clients/Rest/DiscordApiClient.cs
@@ -1,2045 +1,2026 @@
// 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.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DisCatSharp.Net;
///
/// Represents a discord api client.
///
public sealed partial class DiscordApiClient
{
///
/// The audit log reason header name.
///
private const string REASON_HEADER_NAME = "X-Audit-Log-Reason";
///
/// Gets the discord client.
///
internal BaseDiscordClient Discord { get; }
///
/// Gets the rest client.
///
internal RestClient Rest { get; }
///
/// Initializes a new instance of the class.
///
/// The client.
internal DiscordApiClient(BaseDiscordClient client)
{
this.Discord = client;
this.Rest = new RestClient(client);
}
///
/// Initializes a new instance of the class.
///
/// The proxy.
/// The timeout.
/// If true, use relative rate limit.
/// The logger.
internal DiscordApiClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRateLimit, ILogger logger) // This is for meta-clients, such as the webhook client
{
this.Rest = new RestClient(proxy, timeout, useRelativeRateLimit, logger);
}
///
/// Builds the query string.
///
/// The values.
/// Whether this query will be transmitted via POST.
private static string BuildQueryString(IDictionary values, bool post = false)
{
if (values == null || values.Count == 0)
return string.Empty;
var valsCollection = values.Select(xkvp =>
$"{WebUtility.UrlEncode(xkvp.Key)}={WebUtility.UrlEncode(xkvp.Value)}");
var vals = string.Join("&", valsCollection);
return !post ? $"?{vals}" : vals;
}
///
/// Prepares the message.
///
/// The msg_raw.
/// A DiscordMessage.
private DiscordMessage PrepareMessage(JToken msgRaw)
{
var author = msgRaw["author"].ToObject();
var ret = msgRaw.ToDiscordObject();
ret.Discord = this.Discord;
this.PopulateMessage(author, ret);
var referencedMsg = msgRaw["referenced_message"];
if (ret.MessageType == MessageType.Reply && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
{
author = referencedMsg["author"].ToObject();
ret.ReferencedMessage.Discord = this.Discord;
this.PopulateMessage(author, ret.ReferencedMessage);
}
if (ret.Channel != null)
return ret;
var channel = !ret.GuildId.HasValue
? new DiscordDmChannel
{
Id = ret.ChannelId,
Discord = this.Discord,
Type = ChannelType.Private
}
: new DiscordChannel
{
Id = ret.ChannelId,
GuildId = ret.GuildId,
Discord = this.Discord
};
ret.Channel = channel;
return ret;
}
///
/// Populates the message.
///
/// The author.
/// The message.
private void PopulateMessage(TransportUser author, DiscordMessage ret)
{
var guild = ret.Channel?.Guild;
//If this is a webhook, it shouldn't be in the user cache.
if (author.IsBot && int.Parse(author.Discriminator) == 0)
{
ret.Author = new DiscordUser(author) { Discord = this.Discord };
}
else
{
if (!this.Discord.UserCache.TryGetValue(author.Id, out var usr))
{
this.Discord.UserCache[author.Id] = usr = new DiscordUser(author) { Discord = this.Discord };
}
if (guild != null)
{
if (!guild.Members.TryGetValue(author.Id, out var mbr))
mbr = new DiscordMember(usr) { Discord = this.Discord, GuildId = guild.Id };
ret.Author = mbr;
}
else
{
ret.Author = usr;
}
}
ret.PopulateMentions();
if (ret.ReactionsInternal == null)
ret.ReactionsInternal = new List();
foreach (var xr in ret.ReactionsInternal)
xr.Emoji.Discord = this.Discord;
}
///
/// Executes a rest request.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The payload.
/// The ratelimit wait override.
internal Task DoRequestAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, string payload = null, double? ratelimitWaitOverride = null)
{
var req = new RestRequest(client, bucket, url, method, route, headers, payload, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart rest request for stickers.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The file.
/// The sticker name.
/// The sticker tag.
/// The sticker description.
/// The ratelimit wait override.
private Task DoStickerMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null,
DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
{
var req = new MultipartStickerWebRequest(client, bucket, url, method, route, headers, file, name, tags, description, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart request.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The values.
/// The files.
/// The ratelimit wait override.
private Task DoMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, IReadOnlyDictionary values = null,
IReadOnlyCollection files = null, double? ratelimitWaitOverride = null)
{
var req = new MultipartWebRequest(client, bucket, url, method, route, headers, values, files, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
- #region Connections
- ///
- /// Gets the users connections async.
- ///
-
- internal async Task> GetUserConnectionsAsync()
- {
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CONNECTIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
-
- var connectionsRaw = JsonConvert.DeserializeObject>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
-
- return new ReadOnlyCollection(new List(connectionsRaw));
- }
- #endregion
-
#region Webhooks
///
/// Creates the webhook async.
///
/// The channel_id.
/// The name.
/// The base64_avatar.
/// The reason.
internal async Task CreateWebhookAsync(ulong channelId, string name, Optional base64Avatar, string reason)
{
var pld = new RestWebhookPayload
{
Name = name,
AvatarBase64 = base64Avatar.ValueOrDefault(),
AvatarSet = base64Avatar.HasValue
};
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
ret.ApiClient = this;
return ret;
}
///
/// Gets the channel webhooks async.
///
/// The channel_id.
internal async Task> GetChannelWebhooksAsync(ulong channelId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var webhooksRaw = JsonConvert.DeserializeObject>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
return new ReadOnlyCollection(new List(webhooksRaw));
}
///
/// Gets the guild webhooks async.
///
/// The guild_id.
internal async Task> GetGuildWebhooksAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WEBHOOKS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var webhooksRaw = JsonConvert.DeserializeObject>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
return new ReadOnlyCollection(new List(webhooksRaw));
}
///
/// Gets the webhook async.
///
/// The webhook_id.
internal async Task GetWebhookAsync(ulong webhookId)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
ret.ApiClient = this;
return ret;
}
///
/// Gets the webhook with token async.
///
/// The webhook_id.
/// The webhook_token.
internal async Task GetWebhookWithTokenAsync(ulong webhookId, string webhookToken)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Token = webhookToken;
ret.Id = webhookId;
ret.Discord = this.Discord;
ret.ApiClient = this;
return ret;
}
///
/// Modifies the webhook async.
///
/// The webhook_id.
/// The channel id.
/// The name.
/// The base64_avatar.
/// The reason.
internal async Task ModifyWebhookAsync(ulong webhookId, ulong channelId, string name, Optional base64Avatar, string reason)
{
var pld = new RestWebhookPayload
{
Name = name,
AvatarBase64 = base64Avatar.ValueOrDefault(),
AvatarSet = base64Avatar.HasValue,
ChannelId = channelId
};
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
ret.ApiClient = this;
return ret;
}
///
/// Modifies the webhook async.
///
/// The webhook_id.
/// The name.
/// The base64_avatar.
/// The webhook_token.
/// The reason.
internal async Task ModifyWebhookAsync(ulong webhookId, string name, string base64Avatar, string webhookToken, string reason)
{
var pld = new RestWebhookPayload
{
Name = name,
AvatarBase64 = base64Avatar
};
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
ret.ApiClient = this;
return ret;
}
///
/// Deletes the webhook async.
///
/// The webhook_id.
/// The reason.
internal Task DeleteWebhookAsync(ulong webhookId, string reason)
{
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Deletes the webhook async.
///
/// The webhook_id.
/// The webhook_token.
/// The reason.
internal Task DeleteWebhookAsync(ulong webhookId, string webhookToken, string reason)
{
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Executes the webhook async.
///
/// The webhook_id.
/// The webhook_token.
/// The builder.
/// The thread_id.
internal async Task ExecuteWebhookAsync(ulong webhookId, string webhookToken, DiscordWebhookBuilder builder, string threadId)
{
builder.Validate();
if (builder.Embeds != null)
foreach (var embed in builder.Embeds)
if (embed.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var values = new Dictionary();
var pld = new RestWebhookExecutePayload
{
Content = builder.Content,
Username = builder.Username.ValueOrDefault(),
AvatarUrl = builder.AvatarUrl.ValueOrDefault(),
IsTts = builder.IsTts,
Embeds = builder.Embeds,
Components = builder.Components
};
if (builder.Mentions != null)
pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
if (builder.Files?.Count > 0)
{
ulong fileId = 0;
List attachments = new();
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName,
FileSize = null
};
attachments.Add(att);
fileId++;
}
pld.Attachments = attachments;
}
if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.Files?.Count > 0 || builder.IsTts == true || builder.Mentions != null)
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
foreach (var att in ret.Attachments)
att.Discord = this.Discord;
foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
ret.Discord = this.Discord;
return ret;
}
///
/// Executes the webhook slack async.
///
/// The webhook_id.
/// The webhook_token.
/// The json_payload.
/// The thread_id.
internal async Task ExecuteWebhookSlackAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.SLACK}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Executes the webhook github async.
///
/// The webhook_id.
/// The webhook_token.
/// The json_payload.
/// The thread_id.
internal async Task ExecuteWebhookGithubAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.GITHUB}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Edits the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The builder.
/// The thread_id.
internal async Task EditWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, DiscordWebhookBuilder builder, string threadId)
{
builder.Validate(true);
var pld = new RestWebhookMessageEditPayload
{
Content = builder.Content,
Embeds = builder.Embeds,
Mentions = builder.Mentions,
Components = builder.Components,
};
if (builder.Files?.Count > 0)
{
ulong fileId = 0;
List attachments = new();
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName,
FileSize = null
};
attachments.Add(att);
fileId++;
}
if (builder.Attachments != null && builder.Attachments?.Count() > 0)
attachments.AddRange(builder.Attachments);
pld.Attachments = attachments;
var values = new Dictionary
{
["payload_json"] = DiscordJson.SerializeObject(pld)
};
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: builder.Files);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
foreach (var att in ret.AttachmentsInternal)
att.Discord = this.Discord;
foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
return ret;
}
else
{
pld.Attachments = builder.Attachments;
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
foreach (var att in ret.AttachmentsInternal)
att.Discord = this.Discord;
return ret;
}
}
///
/// Edits the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The builder.
/// The thread_id.
internal Task EditWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, DiscordWebhookBuilder builder, ulong threadId) =>
this.EditWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), builder, threadId.ToString());
///
/// Gets the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The thread_id.
internal async Task GetWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Gets the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
internal Task GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
///
/// Gets the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The thread_id.
internal Task GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
///
/// Deletes the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The thread_id.
internal async Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
{
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
///
/// Deletes the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
///
/// Deletes the webhook message async.
///
/// The webhook_id.
/// The webhook_token.
/// The message_id.
/// The thread_id.
internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
#endregion
#region Reactions
///
/// Creates the reaction async.
///
/// The channel_id.
/// The message_id.
/// The emoji.
internal Task CreateReactionAsync(ulong channelId, ulong messageId, string emoji)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
///
/// Deletes the own reaction async.
///
/// The channel_id.
/// The message_id.
/// The emoji.
internal Task DeleteOwnReactionAsync(ulong channelId, ulong messageId, string emoji)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
///
/// Deletes the user reaction async.
///
/// The channel_id.
/// The message_id.
/// The user_id.
/// The emoji.
/// The reason.
internal Task DeleteUserReactionAsync(ulong channelId, ulong messageId, ulong userId, string emoji, string reason)
{
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
///
/// Gets the reactions async.
///
/// The channel_id.
/// The message_id.
/// The emoji.
/// The after_id.
/// The limit.
internal async Task> GetReactionsAsync(ulong channelId, ulong messageId, string emoji, ulong? afterId = null, int limit = 25)
{
var urlParams = new Dictionary();
if (afterId.HasValue)
urlParams["after"] = afterId.Value.ToString(CultureInfo.InvariantCulture);
urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var reactersRaw = JsonConvert.DeserializeObject>(res.Response);
var reacters = new List();
foreach (var xr in reactersRaw)
{
var usr = new DiscordUser(xr) { Discord = this.Discord };
usr = this.Discord.UserCache.AddOrUpdate(xr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
reacters.Add(usr);
}
return new ReadOnlyCollection(new List(reacters));
}
///
/// Deletes the all reactions async.
///
/// The channel_id.
/// The message_id.
/// The reason.
internal Task DeleteAllReactionsAsync(ulong channelId, ulong messageId, string reason)
{
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
///
/// Deletes the reactions emoji async.
///
/// The channel_id.
/// The message_id.
/// The emoji.
internal Task DeleteReactionsEmojiAsync(ulong channelId, ulong messageId, string emoji)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
#endregion
#region Emoji
///
/// Gets the guild emojis async.
///
/// The guild_id.
internal async Task> GetGuildEmojisAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var emojisRaw = JsonConvert.DeserializeObject>(res.Response);
this.Discord.Guilds.TryGetValue(guildId, out var gld);
var users = new Dictionary();
var emojis = new List();
foreach (var rawEmoji in emojisRaw)
{
var xge = rawEmoji.ToObject();
xge.Guild = gld;
var xtu = rawEmoji["user"]?.ToObject();
if (xtu != null)
{
if (!users.ContainsKey(xtu.Id))
{
var user = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
users[user.Id] = user;
}
xge.User = users[xtu.Id];
}
emojis.Add(xge);
}
return new ReadOnlyCollection(emojis);
}
///
/// Gets the guild emoji async.
///
/// The guild_id.
/// The emoji_id.
internal async Task GetGuildEmojiAsync(ulong guildId, ulong emojiId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
this.Discord.Guilds.TryGetValue(guildId, out var gld);
var emojiRaw = JObject.Parse(res.Response);
var emoji = emojiRaw.ToObject();
emoji.Guild = gld;
var xtu = emojiRaw["user"]?.ToObject();
if (xtu != null)
emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
return emoji;
}
///
/// Creates the guild emoji async.
///
/// The guild_id.
/// The name.
/// The imageb64.
/// The roles.
/// The reason.
internal async Task CreateGuildEmojiAsync(ulong guildId, string name, string imageb64, IEnumerable roles, string reason)
{
var pld = new RestGuildEmojiCreatePayload
{
Name = name,
ImageB64 = imageb64,
Roles = roles?.ToArray()
};
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
this.Discord.Guilds.TryGetValue(guildId, out var gld);
var emojiRaw = JObject.Parse(res.Response);
var emoji = emojiRaw.ToObject();
emoji.Guild = gld;
var xtu = emojiRaw["user"]?.ToObject();
emoji.User = xtu != null
? gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu)
: this.Discord.CurrentUser;
return emoji;
}
///
/// Modifies the guild emoji async.
///
/// The guild_id.
/// The emoji_id.
/// The name.
/// The roles.
/// The reason.
internal async Task ModifyGuildEmojiAsync(ulong guildId, ulong emojiId, string name, IEnumerable roles, string reason)
{
var pld = new RestGuildEmojiModifyPayload
{
Name = name,
Roles = roles?.ToArray()
};
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
this.Discord.Guilds.TryGetValue(guildId, out var gld);
var emojiRaw = JObject.Parse(res.Response);
var emoji = emojiRaw.ToObject();
emoji.Guild = gld;
var xtu = emojiRaw["user"]?.ToObject();
if (xtu != null)
emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
return emoji;
}
///
/// Deletes the guild emoji async.
///
/// The guild_id.
/// The emoji_id.
/// The reason.
internal Task DeleteGuildEmojiAsync(ulong guildId, ulong emojiId, string reason)
{
var headers = new Dictionary();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
#endregion
#region Stickers
///
/// Gets a sticker.
///
/// The sticker id.
internal async Task GetStickerAsync(ulong stickerId)
{
var route = $"{Endpoints.STICKERS}/:sticker_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {sticker_id = stickerId}, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JObject.Parse(res.Response).ToDiscordObject();
ret.Discord = this.Discord;
return ret;
}
///
/// Gets the sticker packs.
///
internal async Task> GetStickerPacksAsync()
{
var route = $"{Endpoints.STICKERPACKS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response)["sticker_packs"] as JArray;
var ret = json.ToDiscordObject();
return ret.ToList();
}
///
/// Gets the guild stickers.
///
/// The guild id.
internal async Task> GetGuildStickersAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId}, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JArray.Parse(res.Response);
var ret = json.ToDiscordObject();
for (var i = 0; i < ret.Length; i++)
{
var stkr = ret[i];
stkr.Discord = this.Discord;
if (json[i]["user"] is JObject obj) // Null = Missing stickers perm //
{
var tsr = obj.ToDiscordObject();
var usr = new DiscordUser(tsr) {Discord = this.Discord};
usr = this.Discord.UserCache.AddOrUpdate(tsr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
stkr.User = usr;
}
}
return ret.ToList();
}
///
/// Gets a guild sticker.
///
/// The guild id.
/// The sticker id.
internal async Task GetGuildStickerAsync(ulong guildId, ulong stickerId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var ret = json.ToDiscordObject();
if (json["user"] is not null) // Null = Missing stickers perm //
{
var tsr = json["user"].ToDiscordObject();
var usr = new DiscordUser(tsr) {Discord = this.Discord};
usr = this.Discord.UserCache.AddOrUpdate(tsr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
ret.User = usr;
}
ret.Discord = this.Discord;
return ret;
}
///
/// Creates the guild sticker.
///
/// The guild id.
/// The name.
/// The description.
/// The tags.
/// The file.
/// The reason.
internal async Task CreateGuildStickerAsync(ulong guildId, string name, string description, string tags, DiscordMessageFile file, string reason)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId}, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var res = await this.DoStickerMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, file, name, tags, description);
var ret = JObject.Parse(res.Response).ToDiscordObject();
ret.Discord = this.Discord;
return ret;
}
///
/// Modifies the guild sticker.
///
/// The guild id.
/// The sticker id.
/// The name.
/// The description.
/// The tags.
/// The reason.
internal async Task ModifyGuildStickerAsync(ulong guildId, ulong stickerId, Optional name, Optional description, Optional tags, string reason)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var pld = new RestStickerModifyPayload()
{
Name = name,
Description = description,
Tags = tags
};
var values = new Dictionary
{
["payload_json"] = DiscordJson.SerializeObject(pld)
};
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route);
var ret = JObject.Parse(res.Response).ToDiscordObject();
ret.Discord = this.Discord;
return null;
}
///
/// Deletes the guild sticker async.
///
/// The guild id.
/// The sticker id.
/// The reason.
internal async Task DeleteGuildStickerAsync(ulong guildId, ulong stickerId, string reason)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, sticker_id = stickerId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
#endregion
#region Application Commands
///
/// Gets the global application commands.
///
/// The application id.
/// Whether to get the full localization dict.
internal async Task> GetGlobalApplicationCommandsAsync(ulong applicationId, bool withLocalizations = false)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
var querydict = new Dictionary
{
["with_localizations"] = withLocalizations.ToString().ToLower()
};
var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject>(res.Response);
foreach (var app in ret)
app.Discord = this.Discord;
return ret.ToList();
}
///
/// Bulk overwrites the global application commands.
///
/// The application id.
/// The commands.
internal async Task> BulkOverwriteGlobalApplicationCommandsAsync(ulong applicationId, IEnumerable commands)
{
var pld = new List();
foreach (var command in commands)
{
pld.Add(new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission/*,
Nsfw = command.IsNsfw*/
});
}
this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject>(res.Response);
foreach (var app in ret)
app.Discord = this.Discord;
return ret.ToList();
}
///
/// Creates a global application command.
///
/// The applicationid.
/// The command.
internal async Task CreateGlobalApplicationCommandAsync(ulong applicationId, DiscordApplicationCommand command)
{
var pld = new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission/*,
Nsfw = command.IsNsfw*/
};
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Gets a global application command.
///
/// The application id.
/// The command id.
internal async Task GetGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Edits a global application command.
///
/// The application id.
/// The command id.
/// The name.
/// The description.
/// The options.
/// The localizations of the name.
/// The localizations of the description.
/// The default member permissions.
/// The dm permission.
/// Whether this command is marked as NSFW.
internal async Task EditGlobalApplicationCommandAsync(ulong applicationId, ulong commandId,
Optional name, Optional description, Optional> options,
Optional nameLocalization, Optional descriptionLocalization,
Optional defaultMemberPermission, Optional dmPermission, Optional isNsfw)
{
var pld = new RestApplicationCommandEditPayload
{
Name = name,
Description = description,
Options = options,
DefaultMemberPermission = defaultMemberPermission,
DmPermission = dmPermission,
NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault()/*,
Nsfw = isNsfw*/
};
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Deletes a global application command.
///
/// The application_id.
/// The command_id.
internal async Task DeleteGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
///
/// Gets the guild application commands.
///
/// The application id.
/// The guild id.
/// Whether to get the full localization dict.
internal async Task> GetGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, bool withLocalizations = false)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
var querydict = new Dictionary
{
["with_localizations"] = withLocalizations.ToString().ToLower()
};
var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject>(res.Response);
foreach (var app in ret)
app.Discord = this.Discord;
return ret.ToList();
}
///
/// Bulk overwrites the guild application commands.
///
/// The application id.
/// The guild id.
/// The commands.
internal async Task> BulkOverwriteGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, IEnumerable commands)
{
var pld = new List();
foreach (var command in commands)
{
pld.Add(new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission/*,
Nsfw = command.IsNsfw*/
});
}
this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId, guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject>(res.Response);
foreach (var app in ret)
app.Discord = this.Discord;
return ret.ToList();
}
///
/// Creates a guild application command.
///
/// The application id.
/// The guild id.
/// The command.
internal async Task CreateGuildApplicationCommandAsync(ulong applicationId, ulong guildId, DiscordApplicationCommand command)
{
var pld = new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission/*,
Nsfw = command.IsNsfw*/
};
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Gets a guild application command.
///
/// The application id.
/// The guild id.
/// The command id.
internal async Task GetGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Edits a guild application command.
///
/// The application id.
/// The guild id.
/// The command id.
/// The name.
/// The description.
/// The options.
/// The localizations of the name.
/// The localizations of the description.
/// The default member permissions.
/// The dm permission.
/// Whether this command is marked as NSFW.
internal async Task EditGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId,
Optional name, Optional description, Optional> options,
Optional nameLocalization, Optional descriptionLocalization,
Optional defaultMemberPermission, Optional dmPermission, Optional isNsfw)
{
var pld = new RestApplicationCommandEditPayload
{
Name = name,
Description = description,
Options = options,
DefaultMemberPermission = defaultMemberPermission,
DmPermission = dmPermission,
NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault()/*,
Nsfw = isNsfw*/
};
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Deletes a guild application command.
///
/// The application id.
/// The guild id.
/// The command id.
internal async Task DeleteGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
#region Permissions 2.1
///
/// Gets the guild application command permissions.
///
/// The target application id.
/// The target guild id.
internal async Task> GetGuildApplicationCommandPermissionsAsync(ulong applicationId, ulong guildId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}{Endpoints.PERMISSIONS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject>(res.Response);
foreach (var app in ret)
app.Discord = this.Discord;
return ret.ToList();
}
///
/// Gets a guild application command permission.
///
/// The target application id.
/// The target guild id.
/// The target command id.
internal async Task GetGuildApplicationCommandPermissionAsync(ulong applicationId, ulong guildId, ulong commandId)
{
var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id{Endpoints.PERMISSIONS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
#endregion
///
/// Creates the interaction response.
///
/// The interaction id.
/// The interaction token.
/// The type.
/// The builder.
internal async Task CreateInteractionResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionResponseBuilder builder)
{
if (builder?.Embeds != null)
foreach (var embed in builder.Embeds)
if (embed.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
RestInteractionResponsePayload pld;
if (type != InteractionResponseType.AutoCompleteResult)
{
var data = builder != null ? new DiscordInteractionApplicationCommandCallbackData
{
Content = builder.Content ?? null,
Embeds = builder.Embeds ?? null,
IsTts = builder.IsTts,
Mentions = builder.Mentions ?? null,
Flags = builder.IsEphemeral ? MessageFlags.Ephemeral : null,
Components = builder.Components ?? null,
Choices = null
} : null;
pld = new RestInteractionResponsePayload
{
Type = type,
Data = data
};
if (builder != null && builder.Files != null && builder.Files.Count > 0)
{
ulong fileId = 0;
List attachments = new();
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName,
FileSize = null
};
attachments.Add(att);
fileId++;
}
pld.Attachments = attachments;
pld.Data.Attachments = attachments;
}
}
else
{
pld = new RestInteractionResponsePayload
{
Type = type,
Data = new DiscordInteractionApplicationCommandCallbackData
{
Content = null,
Embeds = null,
IsTts = null,
Mentions = null,
Flags = null,
Components = null,
Choices = builder.Choices,
Attachments = null
},
Attachments = null
};
}
var values = new Dictionary();
if (builder != null)
if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts == true || builder.Mentions != null || builder.Files?.Count > 0)
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "false").Build();
if (builder != null)
{
await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files);
foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
}
else
{
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
}
}
///
/// Creates the interaction response.
///
/// The interaction id.
/// The interaction token.
/// The type.
/// The builder.
internal async Task CreateInteractionModalResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionModalBuilder builder)
{
if (builder.ModalComponents.Any(mc => mc.Components.Any(c => c.Type != ComponentType.InputText && c.Type != ComponentType.Select)))
throw new NotSupportedException("Can't send any other type then Input Text or Select as Modal Component.");
var pld = new RestInteractionModalResponsePayload
{
Type = type,
Data = new DiscordInteractionApplicationCommandModalCallbackData
{
Title = builder.Title,
CustomId = builder.CustomId,
ModalComponents = builder.ModalComponents
}
};
var values = new Dictionary();
var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
}
///
/// Gets the original interaction response.
///
/// The application id.
/// The interaction token.
internal Task GetOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
this.GetWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
///
/// Edits the original interaction response.
///
/// The application id.
/// The interaction token.
/// The builder.
internal Task EditOriginalInteractionResponseAsync(ulong applicationId, string interactionToken, DiscordWebhookBuilder builder) =>
this.EditWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, builder, null);
///
/// Deletes the original interaction response.
///
/// The application id.
/// The interaction token.
internal Task DeleteOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
this.DeleteWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
///
/// Creates the followup message.
///
/// The application id.
/// The interaction token.
/// The builder.
internal async Task CreateFollowupMessageAsync(ulong applicationId, string interactionToken, DiscordFollowupMessageBuilder builder)
{
builder.Validate();
if (builder.Embeds != null)
foreach (var embed in builder.Embeds)
if (embed.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var values = new Dictionary();
var pld = new RestFollowupMessageCreatePayload
{
Content = builder.Content,
IsTts = builder.IsTts,
Embeds = builder.Embeds,
Flags = builder.Flags,
Components = builder.Components
};
if (builder.Files != null && builder.Files.Count > 0)
{
ulong fileId = 0;
List attachments = new();
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName,
FileSize = null
};
attachments.Add(att);
fileId++;
}
pld.Attachments = attachments;
}
if (builder.Mentions != null)
pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts == true || builder.Mentions != null || builder.Files?.Count > 0)
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.WEBHOOKS}/:application_id/:interaction_token";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, interaction_token = interactionToken }, out var path);
var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
foreach (var att in ret.AttachmentsInternal)
{
att.Discord = this.Discord;
}
foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
ret.Discord = this.Discord;
return ret;
}
///
/// Gets the followup message.
///
/// The application id.
/// The interaction token.
/// The message id.
internal Task GetFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
this.GetWebhookMessageAsync(applicationId, interactionToken, messageId);
///
/// Edits the followup message.
///
/// The application id.
/// The interaction token.
/// The message id.
/// The builder.
internal Task EditFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId, DiscordWebhookBuilder builder) =>
this.EditWebhookMessageAsync(applicationId, interactionToken, messageId.ToString(), builder, null);
///
/// Deletes the followup message.
///
/// The application id.
/// The interaction token.
/// The message id.
internal Task DeleteFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
this.DeleteWebhookMessageAsync(applicationId, interactionToken, messageId);
#endregion
#region Misc
///
/// Gets the current application info async.
///
internal Task GetCurrentApplicationInfoAsync()
=> this.GetApplicationInfoAsync("@me");
///
/// Gets the application info async.
///
/// The application_id.
internal Task GetApplicationInfoAsync(ulong applicationId)
=> this.GetApplicationInfoAsync(applicationId.ToString(CultureInfo.InvariantCulture));
///
/// Gets the application info async.
///
/// The application_id.
private async Task GetApplicationInfoAsync(string applicationId)
{
var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
return JsonConvert.DeserializeObject(res.Response);
}
///
/// Gets the application assets async.
///
/// The application.
internal async Task> GetApplicationAssetsAsync(DiscordApplication application)
{
var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id{Endpoints.ASSETS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { application_id = application.Id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var assets = JsonConvert.DeserializeObject>(res.Response);
foreach (var asset in assets)
{
asset.Discord = application.Discord;
asset.Application = application;
}
return new ReadOnlyCollection(new List(assets));
}
///
/// Gets the gateway info async.
///
internal async Task GetGatewayInfoAsync()
{
var headers = Utilities.GetBaseHeaders();
var route = Endpoints.GATEWAY;
if (this.Discord.Configuration.TokenType == TokenType.Bot)
route += Endpoints.BOT;
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route, headers).ConfigureAwait(false);
var info = JObject.Parse(res.Response).ToObject();
info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
return info;
}
#endregion
}
diff --git a/DisCatSharp/Clients/Standalone/DiscordClient.cs b/DisCatSharp/Clients/Standalone/DiscordClient.cs
index 3121ce9da..7a8277780 100644
--- a/DisCatSharp/Clients/Standalone/DiscordClient.cs
+++ b/DisCatSharp/Clients/Standalone/DiscordClient.cs
@@ -1,1303 +1,1303 @@
// 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