diff --git a/DisCatSharp.sln b/DisCatSharp.sln
index 0aba3f024..fc35422ad 100644
--- a/DisCatSharp.sln
+++ b/DisCatSharp.sln
@@ -1,151 +1,150 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.31911.260
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp", "DisCatSharp\DisCatSharp.csproj", "{EB3D8310-DFAD-4295-97F9-82E253647583}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext", "DisCatSharp.VoiceNext\DisCatSharp.VoiceNext.csproj", "{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4255B64D-92EC-46B3-BC3B-ED2C3A8073EE}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
BUILDING.md = BUILDING.md
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
CONTRIBUTING.md = CONTRIBUTING.md
LICENSE.md = LICENSE.md
README.md = README.md
SECURITY.md = SECURITY.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{C8ED55FB-E028-468D-955F-1534C20274EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Interactivity", "DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{DD32BEC3-0189-479F-86DC-CCF95E5634A9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F953F5D0-F0C9-41E6-ADBF-60A76D295899}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.config = .nuget\NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{84464D70-687B-40A8-836D-C4F737698969}"
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
.github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
.github\dependabot.yml = .github\dependabot.yml
DisCatSharp.targets = DisCatSharp.targets
docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1
.github\workflows\docs.yml = .github\workflows\docs.yml
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
Library.targets = Library.targets
NuGet.targets = NuGet.targets
oneclick-rebuild.ps1 = oneclick-rebuild.ps1
Package.targets = Package.targets
rebuild-all.ps1 = rebuild-all.ps1
rebuild-docs.ps1 = rebuild-docs.ps1
rebuild-lib.ps1 = rebuild-lib.ps1
Version.targets = Version.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{430C28D8-5F85-4D6E-AA68-211549435245}"
ProjectSection(SolutionItems) = preProject
.github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md
.github\CODEOWNERS = .github\CODEOWNERS
.github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
.github\workflows\docs-preview.yml = .github\workflows\docs-preview.yml
.github\workflows\docs.yml = .github\workflows\docs.yml
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
.github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md
.github\FUNDING.yml = .github\FUNDING.yml
.github\pull_request_template.md = .github\pull_request_template.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext.Natives", "DisCatSharp.VoiceNext.Natives\DisCatSharp.VoiceNext.Natives.csproj", "{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Common", "DisCatSharp.Common\DisCatSharp.Common.csproj", "{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.ApplicationCommands", "DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.Build.0 = Release|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.Build.0 = Release|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.Build.0 = Release|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.Build.0 = Release|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.Build.0 = Release|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.Build.0 = Release|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.Build.0 = Release|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.Build.0 = Release|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.Build.0 = Release|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.Build.0 = Release|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.Build.0 = Release|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.Build.0 = Release|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.Build.0 = Release|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {F953F5D0-F0C9-41E6-ADBF-60A76D295899} = {84464D70-687B-40A8-836D-C4F737698969}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7}
EndGlobalSection
EndGlobal
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/ApplicationCommands/DiscordApiClient.BaseMethods.ApplicationCommands.cs b/DisCatSharp/Clients/Rest/BaseMethods/ApplicationCommands/DiscordApiClient.BaseMethods.ApplicationCommands.cs
new file mode 100644
index 000000000..9e350115e
--- /dev/null
+++ b/DisCatSharp/Clients/Rest/BaseMethods/ApplicationCommands/DiscordApiClient.BaseMethods.ApplicationCommands.cs
@@ -0,0 +1,476 @@
+// 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.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;
+
+public sealed partial class DiscordApiClient
+{
+
+ ///
+ /// 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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
+
+
+ ///
+ /// 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.ExecuteMultipartRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteMultipartRestRequest(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);
+}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Applications/DiscordApiClient.BaseMethods.Applications.cs b/DisCatSharp/Clients/Rest/BaseMethods/Applications/DiscordApiClient.BaseMethods.Applications.cs
new file mode 100644
index 000000000..fbefba930
--- /dev/null
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Applications/DiscordApiClient.BaseMethods.Applications.cs
@@ -0,0 +1,96 @@
+// 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.Text;
+using System.Threading.Tasks;
+
+using DisCatSharp.Entities;
+using DisCatSharp.Enums;
+using DisCatSharp.Net.Abstractions;
+using DisCatSharp.Net.Serialization;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace DisCatSharp.Net;
+
+public sealed partial class DiscordApiClient
+{
+ ///
+ /// 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.ExecuteRestRequest(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.ExecuteRestRequest(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));
+ }
+}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Channels/DiscordApiClient.BaseMethods.Channels.cs b/DisCatSharp/Clients/Rest/BaseMethods/Channels/DiscordApiClient.BaseMethods.Channels.cs
index 9200645ed..24f8fc7f8 100644
--- a/DisCatSharp/Clients/Rest/BaseMethods/Channels/DiscordApiClient.BaseMethods.Channels.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Channels/DiscordApiClient.BaseMethods.Channels.cs
@@ -1,168 +1,168 @@
// 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.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 channel async.
///
/// The channel_id.
internal async Task GetChannelAsync(ulong channelId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id";
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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
foreach (var xo in ret.PermissionOverwritesInternal)
{
xo.Discord = this.Discord;
xo.ChannelId = ret.Id;
}
return ret;
}
///
/// Deletes the channel async.
///
/// The channel_id.
/// The reason.
internal Task DeleteChannelAsync(ulong channelId, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.CHANNELS}/:channel_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Adds the group dm recipient async.
///
/// The channel_id.
/// The user_id.
/// The access_token.
/// The nickname.
internal Task AddGroupDmRecipientAsync(ulong channelId, ulong userId, string accessToken, string nickname)
{
var pld = new RestChannelGroupDmRecipientAddPayload
{
AccessToken = accessToken,
Nickname = nickname
};
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
}
///
/// Removes the group dm recipient async.
///
/// The channel_id.
/// The user_id.
internal Task RemoveGroupDmRecipientAsync(ulong channelId, ulong userId)
{
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
///
/// Creates the group dm async.
///
/// The access_tokens.
/// The nicks.
internal async Task CreateGroupDmAsync(IEnumerable accessTokens, IDictionary nicks)
{
var pld = new RestUserGroupDmCreatePayload
{
AccessTokens = accessTokens,
Nicknames = nicks
};
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, 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)).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Creates the dm async.
///
/// The recipient_id.
internal async Task CreateDmAsync(ulong recipientId)
{
var pld = new RestUserDmCreatePayload
{
Recipient = recipientId
};
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, 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)).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
}
diff --git a/DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs b/DisCatSharp/Clients/Rest/BaseMethods/Gateway/DiscordApiClient.BaseMethods.Gateway.cs
similarity index 74%
copy from DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs
copy to DisCatSharp/Clients/Rest/BaseMethods/Gateway/DiscordApiClient.BaseMethods.Gateway.cs
index 9d129d982..463e185dc 100644
--- a/DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Gateway/DiscordApiClient.BaseMethods.Gateway.cs
@@ -1,62 +1,61 @@
// 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.Text;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DisCatSharp.Net;
public sealed partial class DiscordApiClient
{
///
- /// Gets the guild preview async.
+ /// Gets the gateway info async.
///
- /// The guild_id.
-
- internal async Task GetGuildPreviewAsync(ulong guildId)
+ internal async Task GetGatewayInfoAsync()
{
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PREVIEW}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ 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).ConfigureAwait(false);
-
- var ret = JsonConvert.DeserializeObject(res.Response);
- ret.Discord = this.Discord;
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route, headers).ConfigureAwait(false);
- return ret;
+ var info = JObject.Parse(res.Response).ToObject();
+ info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
+ return info;
}
-
}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Invites/DiscordApiClient.BaseMethods.Invites.cs b/DisCatSharp/Clients/Rest/BaseMethods/Invites/DiscordApiClient.BaseMethods.Invites.cs
index bee2d4756..1e04b9532 100644
--- a/DisCatSharp/Clients/Rest/BaseMethods/Invites/DiscordApiClient.BaseMethods.Invites.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Invites/DiscordApiClient.BaseMethods.Invites.cs
@@ -1,186 +1,186 @@
// 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.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 guild invites async.
///
/// The guild_id.
internal async Task> GetGuildInvitesAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INVITES}";
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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var invitesRaw = JsonConvert.DeserializeObject>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
return new ReadOnlyCollection(new List(invitesRaw));
}
///
/// Gets the invite async.
///
/// The invite_code.
/// If true, with_counts.
/// If true, with_expiration.
/// The scheduled event id to get.
internal async Task GetInviteAsync(string inviteCode, bool? withCounts, bool? withExpiration, ulong? guildScheduledEventId)
{
var urlParams = new Dictionary();
if (withCounts.HasValue)
urlParams["with_counts"] = withCounts?.ToString();
if (withExpiration.HasValue)
urlParams["with_expiration"] = withExpiration?.ToString();
if (guildScheduledEventId.HasValue)
urlParams["guild_scheduled_event_id"] = guildScheduledEventId?.ToString();
var route = $"{Endpoints.INVITES}/:invite_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {invite_code = inviteCode }, out var path);
var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Deletes the invite async.
///
/// The invite_code.
/// The reason.
internal async Task DeleteInviteAsync(string inviteCode, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.INVITES}/:invite_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {invite_code = inviteCode }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
///
/// Gets the channel invites async.
///
/// The channel_id.
internal async Task> GetChannelInvitesAsync(ulong channelId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var invitesRaw = JsonConvert.DeserializeObject>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
return new ReadOnlyCollection(new List(invitesRaw));
}
///
/// Creates the channel invite async.
///
/// The channel_id.
/// The max_age.
/// The max_uses.
/// The target_type.
/// The target_application.
/// The target_user.
/// If true, temporary.
/// If true, unique.
/// The reason.
internal async Task CreateChannelInviteAsync(ulong channelId, int maxAge, int maxUses, TargetType? targetType, TargetActivity? targetApplication, ulong? targetUser, bool temporary, bool unique, string reason)
{
var pld = new RestChannelInviteCreatePayload
{
MaxAge = maxAge,
MaxUses = maxUses,
TargetType = targetType,
TargetApplication = targetApplication,
TargetUserId = targetUser,
Temporary = temporary,
Unique = unique
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
return ret;
}
/*
* Disabled due to API restrictions
*
* internal async Task InternalAcceptInvite(string invite_code)
* {
* this.Discord.DebugLogger.LogMessage(LogLevel.Warning, "REST API", "Invite accept endpoint was used; this account is now likely unverified", DateTime.Now);
*
* var url = new Uri($"{Utils.GetApiBaseUri(this.Configuration), Endpoints.INVITES}/{invite_code));
* var bucket = this.Rest.GetBucket(0, MajorParameterType.Unbucketed, url, HttpRequestMethod.POST);
- * var res = await this.DoRequestAsync(this.Discord, bucket, url, HttpRequestMethod.POST).ConfigureAwait(false);
+ * var res = await this.ExecuteRestRequest(this.Discord, bucket, url, HttpRequestMethod.POST).ConfigureAwait(false);
*
* var ret = JsonConvert.DeserializeObject(res.Response);
* ret.Discord = this.Discord;
*
* return ret;
* }
*/
}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Messages/DiscordApiClient.BaseMethods.Messages.cs b/DisCatSharp/Clients/Rest/BaseMethods/Messages/DiscordApiClient.BaseMethods.Messages.cs
index a998f13fb..63c821f5e 100644
--- a/DisCatSharp/Clients/Rest/BaseMethods/Messages/DiscordApiClient.BaseMethods.Messages.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Messages/DiscordApiClient.BaseMethods.Messages.cs
@@ -1,450 +1,450 @@
// 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.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json.Linq;
namespace DisCatSharp.Net;
public sealed partial class DiscordApiClient
{
///
/// Gets the message async.
///
/// The channel_id.
/// The message_id.
internal async Task GetMessageAsync(ulong channelId, ulong messageId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, 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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
///
/// Creates the message async.
///
/// The channel_id.
/// The content.
/// The embeds.
/// The sticker.
/// The reply message id.
/// If true, mention reply.
/// If true, fail on invalid reply.
internal async Task CreateMessageAsync(ulong channelId, string content, IEnumerable embeds, DiscordSticker sticker, ulong? replyMessageId, bool mentionReply, bool failOnInvalidReply)
{
if (content != null && content.Length > 2000)
throw new ArgumentException("Message content length cannot exceed 2000 characters.");
if (!embeds?.Any() ?? true)
{
if (content == null && sticker == null)
throw new ArgumentException("You must specify message content, a sticker or an embed.");
if (content.Length == 0)
throw new ArgumentException("Message content must not be empty.");
}
if (embeds != null)
foreach (var embed in embeds)
if (embed.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var pld = new RestChannelMessageCreatePayload
{
HasContent = content != null,
Content = content,
StickersIds = sticker is null ? Array.Empty() : new[] {sticker.Id},
IsTts = false,
HasEmbed = embeds?.Any() ?? false,
Embeds = embeds
};
if (replyMessageId != null)
pld.MessageReference = new InternalDiscordMessageReference { MessageId = replyMessageId, FailIfNotExists = failOnInvalidReply };
if (replyMessageId != null)
pld.Mentions = new DiscordMentions(Mentions.All, true, mentionReply);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
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, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
///
/// Creates the message async.
///
/// The channel_id.
/// The builder.
internal async Task CreateMessageAsync(ulong channelId, DiscordMessageBuilder builder)
{
builder.Validate();
if (builder.Embeds != null)
foreach (var embed in builder.Embeds)
if (embed?.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var pld = new RestChannelMessageCreatePayload
{
HasContent = builder.Content != null,
Content = builder.Content,
StickersIds = builder.Sticker is null ? Array.Empty() : new[] {builder.Sticker.Id},
IsTts = builder.IsTts,
HasEmbed = builder.Embeds != null,
Embeds = builder.Embeds,
Components = builder.Components
};
if (builder.ReplyId != null)
pld.MessageReference = new InternalDiscordMessageReference { MessageId = builder.ReplyId, FailIfNotExists = builder.FailOnInvalidReply };
pld.Mentions = new DiscordMentions(builder.Mentions ?? Mentions.All, builder.Mentions?.Any() ?? false, builder.MentionOnReply);
if (builder.Files.Count == 0)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
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, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
else
{
ulong fileId = 0;
List attachments = new(builder.Files.Count);
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName
};
attachments.Add(att);
fileId++;
}
pld.Attachments = attachments;
var values = new Dictionary
{
["payload_json"] = DiscordJson.SerializeObject(pld)
};
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
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.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
+ var res = await this.ExecuteMultipartRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
foreach (var file in builder.FilesInternal.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
return ret;
}
}
///
/// Gets the channel messages async.
///
/// The channel id.
/// The limit.
/// The before.
/// The after.
/// The around.
internal async Task> GetChannelMessagesAsync(ulong channelId, int limit, ulong? before, ulong? after, ulong? around)
{
var urlParams = new Dictionary();
if (around != null)
urlParams["around"] = around?.ToString(CultureInfo.InvariantCulture);
if (before != null)
urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
if (after != null)
urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
if (limit > 0)
urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var msgsRaw = JArray.Parse(res.Response);
var msgs = new List();
foreach (var xj in msgsRaw)
msgs.Add(this.PrepareMessage(xj));
return new ReadOnlyCollection(new List(msgs));
}
///
/// Gets the channel message async.
///
/// The channel_id.
/// The message_id.
internal async Task GetChannelMessageAsync(ulong channelId, ulong messageId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, 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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
///
/// Edits the message async.
///
/// The channel_id.
/// The message_id.
/// The content.
/// The embeds.
/// The mentions.
/// The components.
/// The suppress_embed.
/// The files.
/// The attachments to keep.
internal async Task EditMessageAsync(ulong channelId, ulong messageId, Optional content, Optional> embeds, Optional> mentions, IReadOnlyList components, Optional suppressEmbed, IReadOnlyCollection files, Optional> attachments)
{
if (embeds.HasValue && embeds.Value != null)
foreach (var embed in embeds.Value)
if (embed.Timestamp != null)
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var pld = new RestChannelMessageEditPayload
{
HasContent = content.HasValue,
Content = content.ValueOrDefault(),
HasEmbed = embeds.HasValue && (embeds.Value?.Any() ?? false),
Embeds = embeds.HasValue && (embeds.Value?.Any() ?? false) ? embeds.Value : null,
Components = components,
Flags = suppressEmbed.HasValue && (bool)suppressEmbed ? MessageFlags.SuppressedEmbeds : null,
Mentions = mentions
.Map(m => new DiscordMentions(m ?? Mentions.None, false, mentions.Value?.OfType().Any() ?? false))
.ValueOrDefault()
};
if (files?.Count > 0)
{
ulong fileId = 0;
List attachmentsNew = new();
foreach (var file in files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName
};
attachmentsNew.Add(att);
fileId++;
}
if (attachments.HasValue && attachments.Value.Any())
attachmentsNew.AddRange(attachments.Value);
pld.Attachments = attachmentsNew;
var values = new Dictionary
{
["payload_json"] = DiscordJson.SerializeObject(pld)
};
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: files).ConfigureAwait(false);
+ var res = await this.ExecuteMultipartRestRequest(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: files).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
foreach (var file in files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
return ret;
}
else
{
pld.Attachments = attachments.ValueOrDefault();
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, 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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
}
///
/// Deletes the message async.
///
/// The channel_id.
/// The message_id.
/// The reason.
internal Task DeleteMessageAsync(ulong channelId, ulong messageId, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
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);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Deletes the messages async.
///
/// The channel_id.
/// The message_ids.
/// The reason.
internal Task DeleteMessagesAsync(ulong channelId, IEnumerable messageIds, string reason)
{
var pld = new RestChannelMessageBulkDeletePayload
{
Messages = messageIds
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}{Endpoints.BULK_DELETE}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Triggers the typing async.
///
/// The channel_id.
internal Task TriggerTypingAsync(ulong channelId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.TYPING}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.POST, route);
}
///
/// Gets the pinned messages async.
///
/// The channel_id.
internal async Task> GetPinnedMessagesAsync(ulong channelId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}";
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 res = await this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var msgsRaw = JArray.Parse(res.Response);
var msgs = new List();
foreach (var xj in msgsRaw)
msgs.Add(this.PrepareMessage(xj));
return new ReadOnlyCollection(new List(msgs));
}
///
/// Pins the message async.
///
/// The channel_id.
/// The message_id.
internal Task PinMessageAsync(ulong channelId, ulong messageId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, 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.PUT, route);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.PUT, route);
}
///
/// Unpins the message async.
///
/// The channel_id.
/// The message_id.
internal Task UnpinMessageAsync(ulong channelId, ulong messageId)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
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);
+ return this.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Reactions/DiscordApiClient.BaseMethods.Reactions.cs b/DisCatSharp/Clients/Rest/BaseMethods/Reactions/DiscordApiClient.BaseMethods.Reactions.cs
new file mode 100644
index 000000000..577723a5b
--- /dev/null
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Reactions/DiscordApiClient.BaseMethods.Reactions.cs
@@ -0,0 +1,174 @@
+// 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.Text;
+using System.Threading.Tasks;
+
+using DisCatSharp.Entities;
+using DisCatSharp.Enums;
+using DisCatSharp.Net.Abstractions;
+using DisCatSharp.Net.Serialization;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace DisCatSharp.Net;
+
+public sealed partial class DiscordApiClient
+{
+ ///
+ /// 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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
+ }
+}
diff --git a/DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs b/DisCatSharp/Clients/Rest/BaseMethods/Stickers/DiscordApiClient.BaseMethods.Stickers.cs
similarity index 63%
copy from DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs
copy to DisCatSharp/Clients/Rest/BaseMethods/Stickers/DiscordApiClient.BaseMethods.Stickers.cs
index 9d129d982..79d81640a 100644
--- a/DisCatSharp/Clients/Rest/GuildMethods/Preview/DiscordApiClient.GuildMethods.Preview.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Stickers/DiscordApiClient.BaseMethods.Stickers.cs
@@ -1,62 +1,76 @@
// 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.Text;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DisCatSharp.Net;
public sealed partial class DiscordApiClient
{
///
- /// Gets the guild preview async.
+ /// Gets a sticker.
///
- /// The guild_id.
-
- internal async Task GetGuildPreviewAsync(ulong guildId)
+ /// The sticker id.
+ internal async Task GetStickerAsync(ulong stickerId)
{
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PREVIEW}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
-
+ 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 = JsonConvert.DeserializeObject(res.Response);
- ret.Discord = this.Discord;
+ var res = await this.ExecuteRestRequest(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.ExecuteRestRequest(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();
+ }
}
diff --git a/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs b/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
index a1988b98c..06cc3aade 100644
--- a/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Users/DiscordApiClient.BaseMethods.Users.cs
@@ -1,165 +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 res = await this.ExecuteRestRequest(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 res = await this.ExecuteRestRequest(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);
+ var res = await this.ExecuteRestRequest(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 res = await this.ExecuteRestRequest(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 res = await this.ExecuteRestRequest(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/BaseMethods/Webhooks/DiscordApiClient.BaseMethods.Webhooks.cs b/DisCatSharp/Clients/Rest/BaseMethods/Webhooks/DiscordApiClient.BaseMethods.Webhooks.cs
new file mode 100644
index 000000000..a65cb48cd
--- /dev/null
+++ b/DisCatSharp/Clients/Rest/BaseMethods/Webhooks/DiscordApiClient.BaseMethods.Webhooks.cs
@@ -0,0 +1,512 @@
+// 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.Text;
+using System.Threading.Tasks;
+
+using DisCatSharp.Entities;
+using DisCatSharp.Enums;
+using DisCatSharp.Net.Abstractions;
+using DisCatSharp.Net.Serialization;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace DisCatSharp.Net;
+
+public sealed partial class DiscordApiClient
+{
+ ///
+ /// 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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteMultipartRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteMultipartRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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.ExecuteRestRequest(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());
+}
diff --git a/DisCatSharp/Clients/Rest/DiscordApiClient.Helpers.cs b/DisCatSharp/Clients/Rest/DiscordApiClient.Helpers.cs
new file mode 100644
index 000000000..124093156
--- /dev/null
+++ b/DisCatSharp/Clients/Rest/DiscordApiClient.Helpers.cs
@@ -0,0 +1,137 @@
+// 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";
+
+ ///
+ /// 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;
+ }
+}
diff --git a/DisCatSharp/Clients/Rest/DiscordApiClient.cs b/DisCatSharp/Clients/Rest/DiscordApiClient.cs
index b89673ead..512ba2646 100644
--- a/DisCatSharp/Clients/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Clients/Rest/DiscordApiClient.cs
@@ -1,2026 +1,172 @@
// 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)
+ internal Task ExecuteRestRequest(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,
+ private Task ExecuteStickerMultipartRestRequest(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,
+ private Task ExecuteMultipartRestRequest(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 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