diff --git a/DisCatSharp/Entities/Message/DiscordMessage.cs b/DisCatSharp/Entities/Message/DiscordMessage.cs
index 0182e40a7..d90df282f 100644
--- a/DisCatSharp/Entities/Message/DiscordMessage.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessage.cs
@@ -1,888 +1,888 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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 Newtonsoft.Json;
namespace DisCatSharp.Entities
{
///
/// Represents a Discord text message.
///
public class DiscordMessage : SnowflakeObject, IEquatable
{
///
/// Initializes a new instance of the class.
///
internal DiscordMessage()
{
this._attachmentsLazy = new Lazy>(() => new ReadOnlyCollection(this._attachments));
this._embedsLazy = new Lazy>(() => new ReadOnlyCollection(this._embeds));
this._mentionedChannelsLazy = new Lazy>(() => this._mentionedChannels != null
? new ReadOnlyCollection(this._mentionedChannels)
: Array.Empty());
this._mentionedRolesLazy = new Lazy>(() => this._mentionedRoles != null ? new ReadOnlyCollection(this._mentionedRoles) : Array.Empty());
this._mentionedUsersLazy = new Lazy>(() => new ReadOnlyCollection(this._mentionedUsers));
this._reactionsLazy = new Lazy>(() => new ReadOnlyCollection(this._reactions));
this._stickersLazy = new Lazy>(() => new ReadOnlyCollection(this._stickers));
this._jumpLink = new Lazy(() =>
{
var gid = this.Channel != null
? this.Channel is DiscordDmChannel ? "@me" : this.Channel.GuildId.Value.ToString(CultureInfo.InvariantCulture)
: this.InternalThread.GuildId.Value.ToString(CultureInfo.InvariantCulture);
var cid = this.ChannelId.ToString(CultureInfo.InvariantCulture);
var mid = this.Id.ToString(CultureInfo.InvariantCulture);
return new Uri($"https://{(this.Discord.Configuration.UseCanary ? "canary.discord.com" : "discord.com")}/channels/{gid}/{cid}/{mid}");
});
}
///
/// Initializes a new instance of the class.
///
/// The other.
internal DiscordMessage(DiscordMessage other)
: this()
{
this.Discord = other.Discord;
this._attachments = other._attachments; // the attachments cannot change, thus no need to copy and reallocate.
this._embeds = new List(other._embeds);
if (other._mentionedChannels != null)
this._mentionedChannels = new List(other._mentionedChannels);
if (other._mentionedRoles != null)
this._mentionedRoles = new List(other._mentionedRoles);
if (other._mentionedRoleIds != null)
this._mentionedRoleIds = new List(other._mentionedRoleIds);
this._mentionedUsers = new List(other._mentionedUsers);
this._reactions = new List(other._reactions);
this._stickers = new List(other._stickers);
this.Author = other.Author;
this.ChannelId = other.ChannelId;
this.Content = other.Content;
this.EditedTimestampRaw = other.EditedTimestampRaw;
this.Id = other.Id;
this.IsTTS = other.IsTTS;
this.MessageType = other.MessageType;
this.Pinned = other.Pinned;
this.TimestampRaw = other.TimestampRaw;
this.WebhookId = other.WebhookId;
}
///
/// Gets the channel in which the message was sent.
///
[JsonIgnore]
public DiscordChannel Channel
{
get => (this.Discord as DiscordClient)?.InternalGetCachedChannel(this.ChannelId) ?? this._channel;
internal set => this._channel = value;
}
private DiscordChannel _channel;
///
/// Gets the thread in which the message was sent.
///
[JsonIgnore]
private DiscordThreadChannel InternalThread
{
get => (this.Discord as DiscordClient)?.InternalGetCachedThread(this.ChannelId) ?? this._thread;
set => this._thread = value;
}
private DiscordThreadChannel _thread;
///
/// Gets the ID of the channel in which the message was sent.
///
[JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong ChannelId { get; internal set; }
///
/// Gets the components this message was sent with.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyCollection Components { get; internal set; }
///
/// Gets the user or member that sent the message.
///
[JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)]
public DiscordUser Author { get; internal set; }
///
/// Gets the message's content.
///
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; internal set; }
///
/// Gets the message's creation timestamp.
///
[JsonIgnore]
public DateTimeOffset Timestamp
=> !string.IsNullOrWhiteSpace(this.TimestampRaw) && DateTimeOffset.TryParse(this.TimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
dto : this.CreationTimestamp;
///
/// Gets the message's creation timestamp as raw string.
///
[JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)]
internal string TimestampRaw { get; set; }
///
/// Gets the message's edit timestamp. Will be null if the message was not edited.
///
[JsonIgnore]
public DateTimeOffset? EditedTimestamp
=> !string.IsNullOrWhiteSpace(this.EditedTimestampRaw) && DateTimeOffset.TryParse(this.EditedTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
(DateTimeOffset?)dto : null;
///
/// Gets the message's edit timestamp as raw string. Will be null if the message was not edited.
///
[JsonProperty("edited_timestamp", NullValueHandling = NullValueHandling.Ignore)]
internal string EditedTimestampRaw { get; set; }
///
/// Gets whether this message was edited.
///
[JsonIgnore]
public bool IsEdited
=> !string.IsNullOrWhiteSpace(this.EditedTimestampRaw);
///
/// Gets whether the message is a text-to-speech message.
///
[JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
public bool IsTTS { get; internal set; }
///
/// Gets whether the message mentions everyone.
///
[JsonProperty("mention_everyone", NullValueHandling = NullValueHandling.Ignore)]
public bool MentionEveryone { get; internal set; }
///
/// Gets users or members mentioned by this message.
///
[JsonIgnore]
public IReadOnlyList MentionedUsers
=> this._mentionedUsersLazy.Value;
[JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)]
internal List _mentionedUsers;
[JsonIgnore]
internal readonly Lazy> _mentionedUsersLazy;
// TODO this will probably throw an exception in DMs since it tries to wrap around a null List...
// this is probably low priority but need to find out a clean way to solve it...
///
/// Gets roles mentioned by this message.
///
[JsonIgnore]
public IReadOnlyList MentionedRoles
=> this._mentionedRolesLazy.Value;
[JsonIgnore]
internal List _mentionedRoles;
[JsonProperty("mention_roles")]
internal List _mentionedRoleIds;
[JsonIgnore]
private readonly Lazy> _mentionedRolesLazy;
///
/// Gets channels mentioned by this message.
///
[JsonIgnore]
public IReadOnlyList MentionedChannels
=> this._mentionedChannelsLazy.Value;
[JsonIgnore]
internal List _mentionedChannels;
[JsonIgnore]
private readonly Lazy> _mentionedChannelsLazy;
///
/// Gets files attached to this message.
///
[JsonIgnore]
public IReadOnlyList Attachments
=> this._attachmentsLazy.Value;
[JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
internal List _attachments = new();
[JsonIgnore]
private readonly Lazy> _attachmentsLazy;
///
/// Gets embeds attached to this message.
///
[JsonIgnore]
public IReadOnlyList Embeds
=> this._embedsLazy.Value;
[JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
internal List _embeds = new();
[JsonIgnore]
private readonly Lazy> _embedsLazy;
///
/// Gets reactions used on this message.
///
[JsonIgnore]
public IReadOnlyList Reactions
=> this._reactionsLazy.Value;
[JsonProperty("reactions", NullValueHandling = NullValueHandling.Ignore)]
internal List _reactions = new();
[JsonIgnore]
private readonly Lazy> _reactionsLazy;
/*
///
/// Gets the nonce sent with the message, if the message was sent by the client.
///
[JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)]
public ulong? Nonce { get; internal set; }
*/
///
/// Gets whether the message is pinned.
///
[JsonProperty("pinned", NullValueHandling = NullValueHandling.Ignore)]
public bool Pinned { get; internal set; }
///
/// Gets the id of the webhook that generated this message.
///
[JsonProperty("webhook_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong? WebhookId { get; internal set; }
///
/// Gets the type of the message.
///
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public MessageType? MessageType { get; internal set; }
///
/// Gets the message activity in the Rich Presence embed.
///
[JsonProperty("activity", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMessageActivity Activity { get; internal set; }
///
/// Gets the message application in the Rich Presence embed.
///
[JsonProperty("application", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMessageApplication Application { get; internal set; }
///
/// Gets the message application id in the Rich Presence embed.
///
[JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong ApplicationId { get; internal set; }
///
/// Gets the internal reference.
///
[JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
internal InternalDiscordMessageReference? InternalReference { get; set; }
///
/// Gets the original message reference from the crossposted message.
///
[JsonIgnore]
public DiscordMessageReference Reference
=> this.InternalReference.HasValue ? this?.InternalBuildMessageReference() : null;
///
/// Gets the bitwise flags for this message.
///
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public MessageFlags? Flags { get; internal set; }
///
/// Gets whether the message originated from a webhook.
///
[JsonIgnore]
public bool WebhookMessage
=> this.WebhookId != null;
///
/// Gets the jump link to this message.
///
[JsonIgnore]
public Uri JumpLink => this._jumpLink.Value;
private readonly Lazy _jumpLink;
///
/// Gets stickers for this message.
///
[JsonIgnore]
public IReadOnlyList Stickers
=> this._stickersLazy.Value;
[JsonProperty("sticker_items", NullValueHandling = NullValueHandling.Ignore)]
internal List _stickers = new();
[JsonIgnore]
private readonly Lazy> _stickersLazy;
///
/// Gets the guild id.
///
[JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
internal ulong? GuildId { get; set; }
///
/// Gets the message object for the referenced message
///
[JsonProperty("referenced_message", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMessage ReferencedMessage { get; internal set; }
///
/// Gets whether the message is a response to an interaction.
///
[JsonProperty("interaction", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMessageInteraction Interaction { get; internal set; }
///
/// Gets the thread that was started from this message.
///
[JsonProperty("thread", NullValueHandling = NullValueHandling.Ignore)]
public DiscordThreadChannel Thread { get; internal set; }
///
/// Build the message reference.
///
internal DiscordMessageReference InternalBuildMessageReference()
{
var client = this.Discord as DiscordClient;
var guildId = this.InternalReference.Value.GuildId;
var channelId = this.InternalReference.Value.ChannelId;
var messageId = this.InternalReference.Value.MessageId;
var reference = new DiscordMessageReference();
if (guildId.HasValue)
reference.Guild = client._guilds.TryGetValue(guildId.Value, out var g)
? g
: new DiscordGuild
{
Id = guildId.Value,
Discord = client
};
var channel = client.InternalGetCachedChannel(channelId.Value);
if (channel == null)
{
reference.Channel = new DiscordChannel
{
Id = channelId.Value,
Discord = client
};
if (guildId.HasValue)
reference.Channel.GuildId = guildId.Value;
}
else reference.Channel = channel;
if (client.MessageCache != null && client.MessageCache.TryGet(m => m.Id == messageId.Value && m.ChannelId == channelId, out var msg))
reference.Message = msg;
else
{
reference.Message = new DiscordMessage
{
ChannelId = this.ChannelId,
Discord = client
};
if (messageId.HasValue)
reference.Message.Id = messageId.Value;
}
return reference;
}
///
/// Gets the mentions.
///
/// An array of IMentions.
private IMention[] GetMentions()
{
var mentions = new List();
if (this.ReferencedMessage != null && this._mentionedUsers.Contains(this.ReferencedMessage.Author))
mentions.Add(new RepliedUserMention()); // Return null to allow all mentions
if (this._mentionedUsers.Any())
mentions.AddRange(this._mentionedUsers.Select(m => (IMention)new UserMention(m)));
if (this._mentionedRoleIds.Any())
mentions.AddRange(this._mentionedRoleIds.Select(r => (IMention)new RoleMention(r)));
return mentions.ToArray();
}
///
/// Populates the mentions.
///
internal void PopulateMentions()
{
var guild = this.Channel?.Guild;
this._mentionedUsers ??= new List();
this._mentionedRoles ??= new List();
this._mentionedChannels ??= new List();
var mentionedUsers = new HashSet(new DiscordUserComparer());
if (guild != null)
{
foreach (var usr in this._mentionedUsers)
{
usr.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
mentionedUsers.Add(guild._members.TryGetValue(usr.Id, out var member) ? member : usr);
}
}
if (!string.IsNullOrWhiteSpace(this.Content))
{
//mentionedUsers.UnionWith(Utilities.GetUserMentions(this).Select(this.Discord.GetCachedOrEmptyUserInternal));
if (guild != null)
{
//this._mentionedRoles = this._mentionedRoles.Union(Utilities.GetRoleMentions(this).Select(xid => guild.GetRole(xid))).ToList();
this._mentionedRoles = this._mentionedRoles.Union(this._mentionedRoleIds.Select(xid => guild.GetRole(xid))).ToList();
this._mentionedChannels = this._mentionedChannels.Union(Utilities.GetChannelMentions(this).Select(xid => guild.GetChannel(xid))).ToList();
}
}
this._mentionedUsers = mentionedUsers.ToList();
}
///
/// Edits the message.
///
/// New content.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content)
=> this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, default, this.GetMentions(), default, default, Array.Empty(), default);
///
/// Edits the message.
///
/// New embed.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional embed = default)
=> this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, embed.HasValue ? new[] {embed.Value} : Array.Empty(), this.GetMentions(), default, default, Array.Empty(), default);
///
/// Edits the message.
///
/// New content.
/// New embed.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content, Optional embed = default)
=> this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embed.HasValue ? new[] {embed.Value} : Array.Empty(), this.GetMentions(), default, default, Array.Empty(), default);
///
/// Edits the message.
///
/// New content.
/// New embeds.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content, Optional> embeds = default)
=> this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embeds, this.GetMentions(), default, default, Array.Empty(), default);
///
/// Edits the message.
///
/// The builder of the message to edit.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task ModifyAsync(DiscordMessageBuilder builder)
{
builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, new Optional>(builder.Embeds), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? builder._attachments : builder._keepAttachments ? this._attachments : null);
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, new Optional>(builder.Embeds), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? new Optional>(builder.Attachments) : builder._keepAttachments ? new Optional>(this.Attachments) : null);
}
///
/// Edits the message embed suppression.
///
/// Suppress embeds.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifySuppressionAsync(bool suppress = false)
=> this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, this.GetMentions(), default, suppress, default, default);
///
/// Clears all attachments from the message.
///
///
public Task ClearAttachmentsAsync()
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, this.GetMentions(), default, default, default, Optional.FromValue>(null));
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, this.GetMentions(), default, default, default, Array.Empty());
///
/// Edits the message.
///
/// The builder of the message to edit.
///
/// Thrown when the client tried to modify a message not sent by them.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task ModifyAsync(Action action)
{
var builder = new DiscordMessageBuilder();
action(builder);
builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, new Optional>(builder.Embeds), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? builder._attachments : builder._keepAttachments ? this._attachments : null);
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, new Optional>(builder.Embeds), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? new Optional>(builder.Attachments) : builder._keepAttachments ? new Optional>(this.Attachments) : null);
}
///
/// Deletes the message.
///
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task DeleteAsync(string reason = null)
=> this.Discord.ApiClient.DeleteMessageAsync(this.ChannelId, this.Id, reason);
///
/// Creates a thread.
/// Depending on the of the parent channel it's either a or a .
///
/// The name of the thread.
/// till it gets archived. Defaults to
/// The per user ratelimit, aka slowdown.
/// The reason.
///
/// Thrown when the client does not have the or permission.
/// Thrown when the channel does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
/// Thrown when the cannot be modified.
public async Task CreateThreadAsync(string name, ThreadAutoArchiveDuration auto_archive_duration = ThreadAutoArchiveDuration.OneHour, int? rate_limit_per_user = null, string reason = null)
{
return Utilities.CheckThreadAutoArchiveDurationFeature(this.Channel.Guild, auto_archive_duration)
? await this.Discord.ApiClient.CreateThreadWithMessageAsync(this.ChannelId, this.Id, name, auto_archive_duration, rate_limit_per_user, reason)
: throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(auto_archive_duration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
}
///
/// Pins the message in its channel.
///
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task PinAsync()
=> this.Discord.ApiClient.PinMessageAsync(this.ChannelId, this.Id);
///
/// Unpins the message in its channel.
///
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task UnpinAsync()
=> this.Discord.ApiClient.UnpinMessageAsync(this.ChannelId, this.Id);
///
/// Responds to the message. This produces a reply.
///
/// Message content to respond with.
/// The sent message.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RespondAsync(string content)
=> this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
///
/// Responds to the message. This produces a reply.
///
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RespondAsync(DiscordEmbed embed)
=> this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
///
/// Responds to the message. This produces a reply.
///
/// Message content to respond with.
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RespondAsync(string content, DiscordEmbed embed)
=> this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
///
/// Responds to the message. This produces a reply.
///
/// The Discord message builder.
/// The sent message.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RespondAsync(DiscordMessageBuilder builder)
=> this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
///
/// Responds to the message. This produces a reply.
///
/// The Discord message builder.
/// The sent message.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RespondAsync(Action action)
{
var builder = new DiscordMessageBuilder();
action(builder);
return this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
}
///
/// Creates a reaction to this message.
///
/// The emoji you want to react with, either an emoji or name:id
///
/// Thrown when the client does not have the permission.
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task CreateReactionAsync(DiscordEmoji emoji)
=> this.Discord.ApiClient.CreateReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
///
/// Deletes your own reaction
///
/// Emoji for the reaction you want to remove, either an emoji or name:id
///
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task DeleteOwnReactionAsync(DiscordEmoji emoji)
=> this.Discord.ApiClient.DeleteOwnReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
///
/// Deletes another user's reaction.
///
/// Emoji for the reaction you want to remove, either an emoji or name:id.
/// Member you want to remove the reaction for
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task DeleteReactionAsync(DiscordEmoji emoji, DiscordUser user, string reason = null)
=> this.Discord.ApiClient.DeleteUserReactionAsync(this.ChannelId, this.Id, user.Id, emoji.ToReactionString(), reason);
///
/// Gets users that reacted with this emoji.
///
/// Emoji to react with.
/// Limit of users to fetch.
/// Fetch users after this user's id.
///
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task> GetReactionsAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
=> this.GetReactionsInternalAsync(emoji, limit, after);
///
/// Deletes all reactions for this message.
///
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task DeleteAllReactionsAsync(string reason = null)
=> this.Discord.ApiClient.DeleteAllReactionsAsync(this.ChannelId, this.Id, reason);
///
/// Deletes all reactions of a specific reaction for this message.
///
/// The emoji to clear, either an emoji or name:id.
///
/// Thrown when the client does not have the permission.
/// Thrown when the emoji does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task DeleteReactionsEmojiAsync(DiscordEmoji emoji)
=> this.Discord.ApiClient.DeleteReactionsEmojiAsync(this.ChannelId, this.Id, emoji.ToReactionString());
///
/// Gets the reactions.
///
/// The emoji to search for.
/// The limit of results.
/// Get the reasctions after snowflake.
private async Task> GetReactionsInternalAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
{
if (limit < 0)
throw new ArgumentException("Cannot get a negative number of reactions' users.");
if (limit == 0)
return Array.Empty();
var users = new List(limit);
var remaining = limit;
var last = after;
int lastCount;
do
{
var fetchSize = remaining > 100 ? 100 : remaining;
var fetch = await this.Discord.ApiClient.GetReactionsAsync(this.Channel.Id, this.Id, emoji.ToReactionString(), last, fetchSize).ConfigureAwait(false);
lastCount = fetch.Count;
remaining -= lastCount;
users.AddRange(fetch);
last = fetch.LastOrDefault()?.Id;
} while (remaining > 0 && lastCount > 0);
return new ReadOnlyCollection(users);
}
///
/// Returns a string representation of this message.
///
/// String representation of this message.
public override string ToString() => $"Message {this.Id}; Attachment count: {this._attachments.Count}; Embed count: {this._embeds.Count}; Contents: {this.Content}";
///
/// Checks whether this is equal to another object.
///
/// Object to compare to.
/// Whether the object is equal to this .
public override bool Equals(object obj) => this.Equals(obj as DiscordMessage);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordMessage e) => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.ChannelId == e.ChannelId));
///
/// Gets the hash code for this .
///
/// The hash code for this .
public override int GetHashCode()
{
var hash = 13;
hash = (hash * 7) + this.Id.GetHashCode();
hash = (hash * 7) + this.ChannelId.GetHashCode();
return hash;
}
///
/// Gets whether the two objects are equal.
///
/// First message to compare.
/// Second message to compare.
/// Whether the two messages are equal.
public static bool operator ==(DiscordMessage e1, DiscordMessage e2)
{
var o1 = e1 as object;
var o2 = e2 as object;
return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.ChannelId == e2.ChannelId));
}
///
/// Gets whether the two objects are not equal.
///
/// First message to compare.
/// Second message to compare.
/// Whether the two messages are not equal.
public static bool operator !=(DiscordMessage e1, DiscordMessage e2)
=> !(e1 == e2);
}
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
index a4b87af6c..b767b05f1 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
@@ -1,503 +1,503 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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 DisCatSharp.Entities;
using Newtonsoft.Json;
namespace DisCatSharp.Net.Abstractions
{
///
/// Represents a channel create payload.
///
internal sealed class RestChannelCreatePayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name")]
public string Name { get; set; }
///
/// Gets or sets the type.
///
[JsonProperty("type")]
public ChannelType Type { get; set; }
///
/// Gets or sets the parent.
///
[JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong? Parent { get; set; }
///
/// Gets or sets the topic.
///
[JsonProperty("topic")]
public Optional Topic { get; set; }
///
/// Gets or sets the bitrate.
///
[JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
public int? Bitrate { get; set; }
///
/// Gets or sets the user limit.
///
[JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
public int? UserLimit { get; set; }
///
/// Gets or sets the permission overwrites.
///
[JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable PermissionOverwrites { get; set; }
///
/// Gets or sets a value indicating whether nsfw.
///
[JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
public bool? Nsfw { get; set; }
///
/// Gets or sets the per user rate limit.
///
[JsonProperty("rate_limit_per_user")]
public Optional PerUserRateLimit { get; set; }
///
/// Gets or sets the quality mode.
///
[JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
public VideoQualityMode? QualityMode { get; set; }
///
/// Gets or sets the default auto archive duration.
///
[JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
}
///
/// Represents a channel modify payload.
///
internal sealed class RestChannelModifyPayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
///
/// Gets or sets the type.
///
[JsonProperty("type")]
public Optional Type { get; set; }
///
/// Gets or sets the position.
///
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int? Position { get; set; }
///
/// Gets or sets the topic.
///
[JsonProperty("topic")]
public Optional Topic { get; set; }
///
/// Gets or sets a value indicating whether nsfw.
///
[JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
public bool? Nsfw { get; set; }
///
/// Gets or sets the parent.
///
[JsonProperty("parent_id")]
public Optional Parent { get; set; }
///
/// Gets or sets the bitrate.
///
[JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
public int? Bitrate { get; set; }
///
/// Gets or sets the user limit.
///
[JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
public int? UserLimit { get; set; }
///
/// Gets or sets the per user rate limit.
///
[JsonProperty("rate_limit_per_user")]
public Optional PerUserRateLimit { get; set; }
///
/// Gets or sets the rtc region.
///
[JsonProperty("rtc_region")]
public Optional RtcRegion { get; set; }
///
/// Gets or sets the quality mode.
///
[JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
public VideoQualityMode? QualityMode { get; set; }
///
/// Gets or sets the default auto archive duration.
///
[JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
///
/// Gets or sets the permission overwrites.
///
[JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable PermissionOverwrites { get; set; }
///
/// Gets or sets the banner base64.
///
[JsonProperty("banner")]
public Optional BannerBase64 { get; set; }
}
///
/// Represents a channel message edit payload.
///
internal class RestChannelMessageEditPayload
{
///
/// Gets or sets the content.
///
[JsonProperty("content", NullValueHandling = NullValueHandling.Include)]
public string Content { get; set; }
///
/// Gets or sets a value indicating whether has content.
///
[JsonIgnore]
public bool HasContent { get; set; }
///
/// Gets or sets the embeds.
///
[JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Embeds { get; set; }
///
/// Gets or sets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMentions Mentions { get; set; }
///
/// Gets or sets the attachments.
///
[JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public List Attachments { get; set; }
+ public IEnumerable Attachments { get; set; }
///
/// Gets or sets the flags.
///
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public MessageFlags? Flags{ get; set; }
///
/// Gets or sets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyCollection Components { get; set; }
///
/// Gets or sets a value indicating whether has embed.
///
[JsonIgnore]
public bool HasEmbed { get; set; }
///
/// Should serialize the content.
///
public bool ShouldSerializeContent()
=> this.HasContent;
///
/// Should serialize the embed.
///
public bool ShouldSerializeEmbed()
=> this.HasEmbed;
}
///
/// Represents a channel message create payload.
///
internal sealed class RestChannelMessageCreatePayload : RestChannelMessageEditPayload
{
///
/// Gets or sets a value indicating whether t t is s.
///
[JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsTTS { get; set; }
///
/// Gets or sets the stickers ids.
///
[JsonProperty("sticker_ids", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable StickersIds { get; set; }
///
/// Gets or sets the message reference.
///
[JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
public InternalDiscordMessageReference? MessageReference { get; set; }
}
///
/// Represents a channel message create multipart payload.
///
internal sealed class RestChannelMessageCreateMultipartPayload
{
///
/// Gets or sets the content.
///
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
///
/// Gets or sets a value indicating whether t t is s.
///
[JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsTTS { get; set; }
///
/// Gets or sets the embeds.
///
[JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Embeds { get; set; }
///
/// Gets or sets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMentions Mentions { get; set; }
///
/// Gets or sets the message reference.
///
[JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
public InternalDiscordMessageReference? MessageReference { get; set; }
}
///
/// Represents a channel message bulk delete payload.
///
internal sealed class RestChannelMessageBulkDeletePayload
{
///
/// Gets or sets the messages.
///
[JsonProperty("messages", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Messages { get; set; }
}
///
/// Represents a channel invite create payload.
///
internal sealed class RestChannelInviteCreatePayload
{
///
/// Gets or sets the max age.
///
[JsonProperty("max_age", NullValueHandling = NullValueHandling.Ignore)]
public int MaxAge { get; set; }
///
/// Gets or sets the max uses.
///
[JsonProperty("max_uses", NullValueHandling = NullValueHandling.Ignore)]
public int MaxUses { get; set; }
///
/// Gets or sets the target type.
///
[JsonProperty("target_type", NullValueHandling = NullValueHandling.Ignore)]
public TargetType? TargetType{ get; set; }
///
/// Gets or sets the target application.
///
[JsonProperty("target_application_id", NullValueHandling = NullValueHandling.Ignore)]
public TargetActivity? TargetApplication { get; set; }
///
/// Gets or sets the target user id.
///
[JsonProperty("target_user_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong? TargetUserId { get; set; }
///
/// Gets or sets a value indicating whether temporary.
///
[JsonProperty("temporary", NullValueHandling = NullValueHandling.Ignore)]
public bool Temporary { get; set; }
///
/// Gets or sets a value indicating whether unique.
///
[JsonProperty("unique", NullValueHandling = NullValueHandling.Ignore)]
public bool Unique { get; set; }
}
///
/// Represents a channel permission edit payload.
///
internal sealed class RestChannelPermissionEditPayload
{
///
/// Gets or sets the allow.
///
[JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
public Permissions Allow { get; set; }
///
/// Gets or sets the deny.
///
[JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
public Permissions Deny { get; set; }
///
/// Gets or sets the type.
///
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public string Type { get; set; }
}
///
/// Represents a channel group dm recipient add payload.
///
internal sealed class RestChannelGroupDmRecipientAddPayload : IOAuth2Payload
{
///
/// Gets or sets the access token.
///
[JsonProperty("access_token")]
public string AccessToken { get; set; }
///
/// Gets or sets the nickname.
///
[JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
public string Nickname { get; set; }
}
///
/// The acknowledge payload.
///
internal sealed class AcknowledgePayload
{
///
/// Gets or sets the token.
///
[JsonProperty("token", NullValueHandling = NullValueHandling.Include)]
public string Token { get; set; }
}
///
/// Represents a thread channel create payload.
///
internal sealed class RestThreadChannelCreatePayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name")]
public string Name { get; set; }
///
/// Gets or sets the auto archive duration.
///
[JsonProperty("auto_archive_duration")]
public ThreadAutoArchiveDuration AutoArchiveDuration { get; set; }
///
/// Gets or sets the rate limit per user.
///
[JsonProperty("rate_limit_per_user")]
public int? PerUserRateLimit { get; set; }
///
/// Gets or sets the thread type.
///
[JsonProperty("type")]
public ChannelType Type { get; set; }
}
///
/// Represents a thread channel modify payload.
///
internal sealed class RestThreadChannelModifyPayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
///
/// Gets or sets the archived.
///
[JsonProperty("archived", NullValueHandling = NullValueHandling.Ignore)]
public Optional Archived { get; set; }
///
/// Gets or sets the auto archive duration.
///
[JsonProperty("auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
public Optional AutoArchiveDuration { get; set; }
///
/// Gets or sets the locked.
///
[JsonProperty("locked", NullValueHandling = NullValueHandling.Ignore)]
public Optional Locked { get; set; }
///
/// Gets or sets the per user rate limit.
///
[JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
public Optional PerUserRateLimit { get; set; }
///
/// Gets or sets the threads's invitable state.
///
[JsonProperty("invitable", NullValueHandling = NullValueHandling.Ignore)]
public Optional Invitable { internal get; set; }
}
}
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index a5beced69..52f62a199 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -1,5377 +1,5377 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Exceptions;
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 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.
/// If true, post.
/// A string.
private static string BuildQueryString(IDictionary values, bool post = false)
{
if (values == null || values.Count == 0)
return string.Empty;
var vals_collection = values.Select(xkvp =>
$"{WebUtility.UrlEncode(xkvp.Key)}={WebUtility.UrlEncode(xkvp.Value)}");
var vals = string.Join("&", vals_collection);
return !post ? $"?{vals}" : vals;
}
///
/// Prepares the message.
///
/// The msg_raw.
/// A DiscordMessage.
private DiscordMessage PrepareMessage(JToken msg_raw)
{
var author = msg_raw["author"].ToObject();
var ret = msg_raw.ToDiscordObject();
ret.Discord = this.Discord;
this.PopulateMessage(author, ret);
var referencedMsg = msg_raw["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 ret.
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, _guild_id = guild.Id };
ret.Author = mbr;
}
else
{
ret.Author = usr;
}
}
ret.PopulateMentions();
if (ret._reactions == null)
ret._reactions = new List();
foreach (var xr in ret._reactions)
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.
/// A Task.
internal Task DoRequestAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, string payload = null, double? ratelimitWaitOverride = null)
{
var req = new RestRequest(client, bucket, url, method, route, headers, payload, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart rest request for stickers.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The file.
/// The sticker name.
/// The sticker tag.
/// The sticker description.
/// The ratelimit wait override.
/// A Task.
private Task DoStickerMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null,
DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
{
var req = new MultipartStickerWebRequest(client, bucket, url, method, route, headers, file, name, tags, description, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart request.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The values.
/// The files.
/// The ratelimit wait override.
/// A Task.
private Task DoMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, IReadOnlyDictionary values = null,
IReadOnlyCollection files = null, double? ratelimitWaitOverride = null)
{
var req = new MultipartWebRequest(client, bucket, url, method, route, headers, values, files, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
#region Guild
///
/// Searches the members async.
///
/// The guild_id.
/// The name.
/// The limit.
/// A Task.
internal async Task> SearchMembersAsync(ulong guild_id, string name, int? limit)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.SEARCH}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var querydict = new Dictionary
{
["query"] = name,
["limit"] = limit.ToString()
};
var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JArray.Parse(res.Response);
var tms = json.ToObject>();
var mbrs = new List();
foreach (var xtm in tms)
{
var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discord = usr.Discord;
old.AvatarHash = usr.AvatarHash;
return old;
});
mbrs.Add(new DiscordMember(xtm) { Discord = this.Discord, _guild_id = guild_id });
}
return mbrs;
}
///
/// Gets the guild ban async.
///
/// The guild_id.
/// The user_id.
/// A Task.
internal async Task GetGuildBanAsync(ulong guild_id, ulong user_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id, user_id}, out var path);
var uri = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, uri, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var ban = json.ToObject();
return ban;
}
///
/// Creates the guild async.
///
/// The name.
/// The region_id.
/// The iconb64.
/// The verification_level.
/// The default_message_notifications.
/// The system_channel_flags.
internal async Task CreateGuildAsync(string name, string region_id, Optional iconb64, VerificationLevel? verification_level,
DefaultMessageNotifications? default_message_notifications, SystemChannelFlags? system_channel_flags)
{
var pld = new RestGuildCreatePayload
{
Name = name,
RegionId = region_id,
DefaultMessageNotifications = default_message_notifications,
VerificationLevel = verification_level,
IconBase64 = iconb64,
SystemChannelFlags = system_channel_flags
};
var route = $"{Endpoints.GUILDS}";
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 json = JObject.Parse(res.Response);
var raw_members = (JArray)json["members"];
var guild = json.ToDiscordObject();
if (this.Discord is DiscordClient dc)
await dc.OnGuildCreateEventAsync(guild, raw_members, null).ConfigureAwait(false);
return guild;
}
///
/// Creates the guild from template async.
///
/// The template_code.
/// The name.
/// The iconb64.
internal async Task CreateGuildFromTemplateAsync(string template_code, string name, Optional iconb64)
{
var pld = new RestGuildCreateFromTemplatePayload
{
Name = name,
IconBase64 = iconb64
};
var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { template_code }, 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 json = JObject.Parse(res.Response);
var raw_members = (JArray)json["members"];
var guild = json.ToDiscordObject();
if (this.Discord is DiscordClient dc)
await dc.OnGuildCreateEventAsync(guild, raw_members, null).ConfigureAwait(false);
return guild;
}
///
/// Deletes the guild async.
///
/// The guild_id.
internal async Task DeleteGuildAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
if (this.Discord is DiscordClient dc)
{
var gld = dc._guilds[guild_id];
await dc.OnGuildDeleteEventAsync(gld).ConfigureAwait(false);
}
}
///
/// Modifies the guild.
///
/// The guild id.
/// The name.
/// The verification level.
/// The default message notifications.
/// The mfa level.
/// The explicit content filter.
/// The afk channel id.
/// The afk timeout.
/// The iconb64.
/// The owner id.
/// The splashb64.
/// The system channel id.
/// The system channel flags.
/// The public updates channel id.
/// The rules channel id.
/// The description.
/// The banner base64.
/// The discovery base64.
/// The preferred locale.
/// Whether the premium progress bar should be enabled.
/// The reason.
internal async Task ModifyGuildAsync(ulong guildId, Optional name, Optional verificationLevel,
Optional defaultMessageNotifications, Optional mfaLevel,
Optional explicitContentFilter, Optional afkChannelId,
Optional afkTimeout, Optional iconb64, Optional ownerId, Optional splashb64,
Optional systemChannelId, Optional systemChannelFlags,
Optional publicUpdatesChannelId, Optional rulesChannelId, Optional description,
Optional bannerb64, Optional discorverySplashb64, Optional preferredLocale, Optional premiumProgressBarEnabled, string reason)
{
var pld = new RestGuildModifyPayload
{
Name = name,
VerificationLevel = verificationLevel,
DefaultMessageNotifications = defaultMessageNotifications,
MfaLevel = mfaLevel,
ExplicitContentFilter = explicitContentFilter,
AfkChannelId = afkChannelId,
AfkTimeout = afkTimeout,
IconBase64 = iconb64,
SplashBase64 = splashb64,
BannerBase64 = bannerb64,
DiscoverySplashBase64 = discorverySplashb64,
OwnerId = ownerId,
SystemChannelId = systemChannelId,
SystemChannelFlags = systemChannelFlags,
RulesChannelId = rulesChannelId,
PublicUpdatesChannelId = publicUpdatesChannelId,
PreferredLocale = preferredLocale,
Description = description,
PremiumProgressBarEnabled = premiumProgressBarEnabled
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, 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.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
foreach (var r in guild._roles.Values)
r._guild_id = guild.Id;
if (this.Discord is DiscordClient dc)
await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
return guild;
}
///
/// Modifies the guild community settings.
///
/// The guild id.
/// The guild features.
/// The rules channel id.
/// The public updates channel id.
/// The preferred locale.
/// The description.
/// The default message notifications.
/// The explicit content filter.
/// The verification level.
/// The reason.
internal async Task ModifyGuildCommunitySettingsAsync(ulong guildId, List features, Optional rulesChannelId, Optional publicUpdatesChannelId, string preferredLocale, string description, DefaultMessageNotifications defaultMessageNotifications, ExplicitContentFilter explicitContentFilter, VerificationLevel verificationLevel, string reason)
{
var pld = new RestGuildCommunityModifyPayload
{
VerificationLevel = verificationLevel,
DefaultMessageNotifications = defaultMessageNotifications,
ExplicitContentFilter = explicitContentFilter,
RulesChannelId = rulesChannelId,
PublicUpdatesChannelId = publicUpdatesChannelId,
PreferredLocale = preferredLocale,
Description = description ?? Optional.FromNoValue(),
Features = features
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, 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.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
foreach (var r in guild._roles.Values)
r._guild_id = guild.Id;
if (this.Discord is DiscordClient dc)
await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
return guild;
}
///
/// Gets the guild bans async.
///
/// The guild_id.
/// A Task.
internal async Task> GetGuildBansAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var bans_raw = JsonConvert.DeserializeObject>(res.Response).Select(xb =>
{
if (!this.Discord.TryGetCachedUserInternal(xb.RawUser.Id, out var usr))
{
usr = new DiscordUser(xb.RawUser) { Discord = this.Discord };
usr = this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
}
xb.User = usr;
return xb;
});
var bans = new ReadOnlyCollection(new List(bans_raw));
return bans;
}
///
/// Creates the guild ban async.
///
/// The guild_id.
/// The user_id.
/// The delete_message_days.
/// The reason.
/// A Task.
internal Task CreateGuildBanAsync(ulong guild_id, ulong user_id, int delete_message_days, string reason)
{
if (delete_message_days < 0 || delete_message_days > 7)
throw new ArgumentException("Delete message days must be a number between 0 and 7.", nameof(delete_message_days));
var urlparams = new Dictionary
{
["delete_message_days"] = delete_message_days.ToString(CultureInfo.InvariantCulture)
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new { guild_id, user_id }, out var path);
var url = Utilities.GetApiUriFor(path, BuildQueryString(urlparams), this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
}
///
/// Removes the guild ban async.
///
/// The guild_id.
/// The user_id.
/// The reason.
/// A Task.
internal Task RemoveGuildBanAsync(ulong guild_id, ulong user_id, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id, user_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Leaves the guild async.
///
/// The guild_id.
/// A Task.
internal Task LeaveGuildAsync(ulong guild_id)
{
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
///
/// Adds the guild member async.
///
/// The guild_id.
/// The user_id.
/// The access_token.
/// The nick.
/// The roles.
/// If true, muted.
/// If true, deafened.
/// A Task.
internal async Task AddGuildMemberAsync(ulong guild_id, ulong user_id, string access_token, string nick, IEnumerable roles, bool muted, bool deafened)
{
var pld = new RestGuildMemberAddPayload
{
AccessToken = access_token,
Nickname = nick ?? "",
Roles = roles ?? new List(),
Deaf = deafened,
Mute = muted
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new { guild_id, user_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var tm = JsonConvert.DeserializeObject(res.Response);
return new DiscordMember(tm) { Discord = this.Discord, _guild_id = guild_id };
}
///
/// Lists the guild members async.
///
/// The guild_id.
/// The limit.
/// The after.
/// A Task.
internal async Task> ListGuildMembersAsync(ulong guild_id, int? limit, ulong? after)
{
var urlparams = new Dictionary();
if (limit != null && limit > 0)
urlparams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
if (after != null)
urlparams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, 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 members_raw = JsonConvert.DeserializeObject>(res.Response);
return new ReadOnlyCollection(members_raw);
}
///
/// Adds the guild member role async.
///
/// The guild_id.
/// The user_id.
/// The role_id.
/// The reason.
/// A Task.
internal Task AddGuildMemberRoleAsync(ulong guild_id, ulong user_id, ulong role_id, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new { guild_id, user_id, role_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
}
///
/// Removes the guild member role async.
///
/// The guild_id.
/// The user_id.
/// The role_id.
/// The reason.
/// A Task.
internal Task RemoveGuildMemberRoleAsync(ulong guild_id, ulong user_id, ulong role_id, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id, user_id, role_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Modifies the guild channel position async.
///
/// The guild_id.
/// The pld.
/// The reason.
/// A Task.
internal Task ModifyGuildChannelPositionAsync(ulong guild_id, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Modifies the guild channel parent async.
///
/// The guild_id.
/// The pld.
/// The reason.
/// A Task.
internal Task ModifyGuildChannelParentAsync(ulong guild_id, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Detaches the guild channel parent async.
///
/// The guild_id.
/// The pld.
/// The reason.
/// A Task.
internal Task DetachGuildChannelParentAsync(ulong guild_id, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Modifies the guild role position async.
///
/// The guild_id.
/// The pld.
/// The reason.
/// A Task.
internal Task ModifyGuildRolePositionAsync(ulong guild_id, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Gets the audit logs async.
///
/// The guild_id.
/// The limit.
/// The after.
/// The before.
/// The responsible.
/// The action_type.
/// A Task.
internal async Task GetAuditLogsAsync(ulong guild_id, int limit, ulong? after, ulong? before, ulong? responsible, int? action_type)
{
var urlparams = new Dictionary
{
["limit"] = limit.ToString(CultureInfo.InvariantCulture)
};
if (after != null)
urlparams["after"] = after?.ToString(CultureInfo.InvariantCulture);
if (before != null)
urlparams["before"] = before?.ToString(CultureInfo.InvariantCulture);
if (responsible != null)
urlparams["user_id"] = responsible?.ToString(CultureInfo.InvariantCulture);
if (action_type != null)
urlparams["action_type"] = action_type?.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.AUDIT_LOGS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, 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 audit_log_data_raw = JsonConvert.DeserializeObject(res.Response);
return audit_log_data_raw;
}
///
/// Gets the guild vanity url async.
///
/// The guild_id.
/// A Task.
internal async Task GetGuildVanityUrlAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VANITY_URL}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var invite = JsonConvert.DeserializeObject(res.Response);
return invite;
}
///
/// Gets the guild widget async.
///
/// The guild_id.
/// A Task.
internal async Task GetGuildWidgetAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET_JSON}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawChannels = (JArray)json["channels"];
var ret = json.ToDiscordObject();
ret.Discord = this.Discord;
ret.Guild = this.Discord.Guilds[guild_id];
ret.Channels = ret.Guild == null
? rawChannels.Select(r => new DiscordChannel
{
Id = (ulong)r["id"],
Name = r["name"].ToString(),
Position = (int)r["position"]
}).ToList()
: rawChannels.Select(r =>
{
var c = ret.Guild.GetChannel((ulong)r["id"]);
c.Position = (int)r["position"];
return c;
}).ToList();
return ret;
}
///
/// Gets the guild widget settings async.
///
/// The guild_id.
/// A Task.
internal async Task GetGuildWidgetSettingsAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Guild = this.Discord.Guilds[guild_id];
return ret;
}
///
/// Modifies the guild widget settings async.
///
/// The guild_id.
/// If true, is enabled.
/// The channel id.
/// The reason.
/// A Task.
internal async Task ModifyGuildWidgetSettingsAsync(ulong guild_id, bool? isEnabled, ulong? channelId, string reason)
{
var pld = new RestGuildWidgetSettingsPayload
{
Enabled = isEnabled,
ChannelId = channelId
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, 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.Guild = this.Discord.Guilds[guild_id];
return ret;
}
///
/// Gets the guild templates async.
///
/// The guild_id.
/// A Task.
internal async Task> GetGuildTemplatesAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var templates_raw = JsonConvert.DeserializeObject>(res.Response);
return new ReadOnlyCollection(new List(templates_raw));
}
///
/// Creates the guild template async.
///
/// The guild_id.
/// The name.
/// The description.
/// A Task.
internal async Task CreateGuildTemplateAsync(ulong guild_id, string name, string description)
{
var pld = new RestGuildTemplateCreateOrModifyPayload
{
Name = name,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { guild_id }, 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 ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Syncs the guild template async.
///
/// The guild_id.
/// The template_code.
/// A Task.
internal async Task SyncGuildTemplateAsync(ulong guild_id, string template_code)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new { guild_id, template_code }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route).ConfigureAwait(false);
var template_raw = JsonConvert.DeserializeObject(res.Response);
return template_raw;
}
///
/// Modifies the guild template async.
///
/// The guild_id.
/// The template_code.
/// The name.
/// The description.
/// A Task.
internal async Task ModifyGuildTemplateAsync(ulong guild_id, string template_code, string name, string description)
{
var pld = new RestGuildTemplateCreateOrModifyPayload
{
Name = name,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id, template_code }, 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 template_raw = JsonConvert.DeserializeObject(res.Response);
return template_raw;
}
///
/// Deletes the guild template async.
///
/// The guild_id.
/// The template_code.
/// A Task.
internal async Task DeleteGuildTemplateAsync(ulong guild_id, string template_code)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id, template_code }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
var template_raw = JsonConvert.DeserializeObject(res.Response);
return template_raw;
}
///
/// Gets the guild membership screening form async.
///
/// The guild_id.
/// A Task.
internal async Task GetGuildMembershipScreeningFormAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var screening_raw = JsonConvert.DeserializeObject(res.Response);
return screening_raw;
}
///
/// Modifies the guild membership screening form async.
///
/// The guild_id.
/// The enabled.
/// The fields.
/// The description.
/// A Task.
internal async Task ModifyGuildMembershipScreeningFormAsync(ulong guild_id, Optional enabled, Optional fields, Optional description)
{
var pld = new RestGuildMembershipScreeningFormModifyPayload
{
Enabled = enabled,
Description = description,
Fields = fields
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, 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 screening_raw = JsonConvert.DeserializeObject(res.Response);
return screening_raw;
}
///
/// Gets the guild welcome screen async.
///
/// The guild_id.
/// A Task.
internal async Task GetGuildWelcomeScreenAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Modifies the guild welcome screen async.
///
/// The guild_id.
/// The enabled.
/// The welcome channels.
/// The description.
/// A Task.
internal async Task ModifyGuildWelcomeScreenAsync(ulong guild_id, Optional enabled, Optional> welcomeChannels, Optional description)
{
var pld = new RestGuildWelcomeScreenModifyPayload
{
Enabled = enabled,
WelcomeChannels = welcomeChannels,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Updates the current user voice state async.
///
/// The guild_id.
/// The channel id.
/// If true, suppress.
/// The request to speak timestamp.
/// A Task.
internal async Task UpdateCurrentUserVoiceStateAsync(ulong guild_id, ulong channelId, bool? suppress, DateTimeOffset? requestToSpeakTimestamp)
{
var pld = new RestGuildUpdateCurrentUserVoiceStatePayload
{
ChannelId = channelId,
Suppress = suppress,
RequestToSpeakTimestamp = requestToSpeakTimestamp
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/@me";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
}
///
/// Updates the user voice state async.
///
/// The guild_id.
/// The user_id.
/// The channel id.
/// If true, suppress.
/// A Task.
internal async Task UpdateUserVoiceStateAsync(ulong guild_id, ulong user_id, ulong channelId, bool? suppress)
{
var pld = new RestGuildUpdateUserVoiceStatePayload
{
ChannelId = channelId,
Suppress = suppress
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id, user_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
}
#endregion
#region Guild Scheduled Events
///
/// Creates a scheduled event.
///
internal async Task CreateGuildScheduledEventAsync(ulong guild_id, ulong? channel_id, DiscordScheduledEventEntityMetadata metadata, string name, DateTimeOffset scheduled_start_time, DateTimeOffset? scheduled_end_time, string description, ScheduledEventEntityType type, string reason = null)
{
var pld = new RestGuildScheduledEventCreatePayload
{
ChannelId = channel_id,
EntityMetadata = metadata,
Name = name,
ScheduledStartTime = scheduled_start_time,
ScheduledEndTime = scheduled_end_time,
Description = description,
EntityType = type
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { guild_id }, 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));
var scheduled_event = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guild_id];
scheduled_event.Discord = this.Discord;
if (scheduled_event.Creator != null)
scheduled_event.Creator.Discord = this.Discord;
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventCreateEventAsync(scheduled_event, guild);
return scheduled_event;
}
///
/// Modifies a scheduled event.
///
internal async Task ModifyGuildScheduledEventAsync(ulong guild_id, ulong scheduled_event_id, Optional channel_id, Optional metadata, Optional name, Optional scheduled_start_time, Optional scheduled_end_time, Optional description, Optional type, Optional status, string reason = null)
{
var pld = new RestGuildSheduledEventModifyPayload
{
ChannelId = channel_id,
EntityMetadata = metadata,
Name = name,
ScheduledStartTime = scheduled_start_time,
ScheduledEndTime = scheduled_end_time,
Description = description,
EntityType = type,
Status = status
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id, scheduled_event_id }, 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));
var scheduled_event = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guild_id];
scheduled_event.Discord = this.Discord;
if (scheduled_event.Creator != null)
{
scheduled_event.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(scheduled_event.Creator.Id, scheduled_event.Creator, (id, old) =>
{
old.Username = scheduled_event.Creator.Username;
old.Discriminator = scheduled_event.Creator.Discriminator;
old.AvatarHash = scheduled_event.Creator.AvatarHash;
old.Flags = scheduled_event.Creator.Flags;
return old;
});
}
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventUpdateEventAsync(scheduled_event, guild);
return scheduled_event;
}
///
/// Modifies a scheduled event.
///
internal async Task ModifyGuildScheduledEventStatusAsync(ulong guild_id, ulong scheduled_event_id, ScheduledEventStatus status, string reason = null)
{
var pld = new RestGuildSheduledEventModifyPayload
{
Status = status
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id, scheduled_event_id }, 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));
var scheduled_event = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guild_id];
scheduled_event.Discord = this.Discord;
if (scheduled_event.Creator != null)
{
scheduled_event.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(scheduled_event.Creator.Id, scheduled_event.Creator, (id, old) =>
{
old.Username = scheduled_event.Creator.Username;
old.Discriminator = scheduled_event.Creator.Discriminator;
old.AvatarHash = scheduled_event.Creator.AvatarHash;
old.Flags = scheduled_event.Creator.Flags;
return old;
});
}
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventUpdateEventAsync(scheduled_event, guild);
return scheduled_event;
}
///
/// Gets a scheduled event.
///
/// The guild_id.
/// The event id.
/// Whether to include user count.
internal async Task GetGuildScheduledEventAsync(ulong guild_id, ulong scheduled_event_id, bool? with_user_count)
{
var urlparams = new Dictionary();
if (with_user_count.HasValue)
urlparams["with_user_count"] = with_user_count?.ToString();
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id, scheduled_event_id }, 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);
var scheduled_event = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guild_id];
scheduled_event.Discord = this.Discord;
if (scheduled_event.Creator != null)
{
scheduled_event.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(scheduled_event.Creator.Id, scheduled_event.Creator, (id, old) =>
{
old.Username = scheduled_event.Creator.Username;
old.Discriminator = scheduled_event.Creator.Discriminator;
old.AvatarHash = scheduled_event.Creator.AvatarHash;
old.Flags = scheduled_event.Creator.Flags;
return old;
});
}
return scheduled_event;
}
///
/// Gets the guilds scheduled events.
///
/// The guild_id.
/// Whether to include the count of users subscribed to the scheduled event.
internal async Task> ListGuildScheduledEventsAsync(ulong guild_id, bool? with_user_count)
{
var urlparams = new Dictionary();
if (with_user_count.HasValue)
urlparams["with_user_count"] = with_user_count?.ToString();
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id }, 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);
var events = new Dictionary();
var events_raw = JsonConvert.DeserializeObject>(res.Response);
var guild = this.Discord.Guilds[guild_id];
foreach (var ev in events_raw)
{
ev.Discord = this.Discord;
if(ev.Creator != null)
{
ev.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(ev.Creator.Id, ev.Creator, (id, old) =>
{
old.Username = ev.Creator.Username;
old.Discriminator = ev.Creator.Discriminator;
old.AvatarHash = ev.Creator.AvatarHash;
old.Flags = ev.Creator.Flags;
return old;
});
}
events.Add(ev.Id, ev);
}
return new ReadOnlyDictionary(new Dictionary(events));
}
///
/// Deletes a guild sheduled event.
///
/// The guild_id.
/// The sheduled event id.
/// The reason.
internal Task DeleteGuildScheduledEventAsync(ulong guild_id, ulong scheduled_event_id, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id, scheduled_event_id }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Gets the users who RSVP'd to a sheduled event.
/// Optional with member objects.
/// This endpoint is paginated.
///
/// The guild_id.
/// The sheduled event id.
/// The limit how many users to receive from the event.
/// Get results before the given id.
/// Get results after the given id.
/// Wether to include guild member data. attaches guild_member property to the user object.
internal async Task> GetGuildScheduledEventRSPVUsersAsync(ulong guild_id, ulong scheduled_event_id, int? limit, ulong? before, ulong? after, bool? with_member)
{
var urlparams = new Dictionary();
if (limit != null && limit > 0)
urlparams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
if (before != null)
urlparams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
if (after != null)
urlparams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
if (with_member != null)
urlparams["with_member"] = with_member.Value.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id{Endpoints.USERS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id, scheduled_event_id }, 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 rspv_users = JsonConvert.DeserializeObject>(res.Response);
Dictionary rspv = new();
foreach (var rspv_user in rspv_users)
{
rspv_user.Discord = this.Discord;
rspv_user.GuildId = guild_id;
rspv_user.User.Discord = this.Discord;
rspv_user.User = this.Discord.UserCache.AddOrUpdate(rspv_user.User.Id, rspv_user.User, (id, old) =>
{
old.Username = rspv_user.User.Username;
old.Discriminator = rspv_user.User.Discriminator;
old.AvatarHash = rspv_user.User.AvatarHash;
old.BannerHash = rspv_user.User.BannerHash;
old._bannerColor = rspv_user.User._bannerColor;
return old;
});
/*if (with_member.HasValue && with_member.Value && rspv_user.Member != null)
{
rspv_user.Member.Discord = this.Discord;
}*/
rspv.Add(rspv_user.User.Id, rspv_user);
}
return new ReadOnlyDictionary(new Dictionary(rspv));
}
#endregion
#region Channel
///