diff --git a/DisCatSharp/Entities/Guild/DiscordMember.cs b/DisCatSharp/Entities/Guild/DiscordMember.cs
index c85d54d5c..608c50d0c 100644
--- a/DisCatSharp/Entities/Guild/DiscordMember.cs
+++ b/DisCatSharp/Entities/Guild/DiscordMember.cs
@@ -1,778 +1,778 @@
// 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 DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
namespace DisCatSharp.Entities
{
///
/// Represents a Discord guild member.
///
public class DiscordMember : DiscordUser, IEquatable
{
///
/// Initializes a new instance of the class.
///
internal DiscordMember()
{
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Initializes a new instance of the class.
///
/// The user.
internal DiscordMember(DiscordUser user)
{
this.Discord = user.Discord;
this.Id = user.Id;
this._role_ids = new List();
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Initializes a new instance of the class.
///
/// The mbr.
internal DiscordMember(TransportMember mbr)
{
this.Id = mbr.User.Id;
this.IsDeafened = mbr.IsDeafened;
this.IsMuted = mbr.IsMuted;
this.JoinedAt = mbr.JoinedAt;
this.Nickname = mbr.Nickname;
this.PremiumSince = mbr.PremiumSince;
this.IsPending = mbr.IsPending;
this.GuildAvatarHash = mbr.GuildAvatarHash;
this.GuildBannerHash = mbr.GuildBannerHash;
this.GuildBio = mbr.GuildBio;
this.CommunicationDisabledUntil = mbr.CommunicationDisabledUntil;
this._avatarHash = mbr.AvatarHash;
this._role_ids = mbr.Roles ?? new List();
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Gets the members avatar hash.
///
[JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
public virtual string GuildAvatarHash { get; internal set; }
///
/// Gets the members avatar URL.
///
[JsonIgnore]
public string GuildAvatarUrl
=> string.IsNullOrWhiteSpace(this.GuildAvatarHash) ? this.User.AvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this._guild_id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHash}.{(this.GuildAvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
///
/// Gets this member's banner url.
///
[JsonIgnore]
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
public string BannerUrl => this.User.BannerUrl;
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
///
/// Gets the member's banner hash.
///
[JsonIgnore]
public override string BannerHash
{
get => this.User.BannerHash;
internal set => this.User.BannerHash = value;
}
///
/// Gets the members banner hash.
///
[JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
public virtual string GuildBannerHash { get; internal set; }
///
/// Gets the members banner URL.
///
[JsonIgnore]
public string GuildBannerUrl
=> string.IsNullOrWhiteSpace(this.GuildBannerHash) ? this.User.BannerUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this._guild_id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.GuildBannerHash}.{(this.GuildBannerHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
///
/// The color of this member's banner. Mutually exclusive with .
///
[JsonIgnore]
public override DiscordColor? BannerColor => this.User.BannerColor;
///
/// Gets this member's nickname.
///
[JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
public string Nickname { get; internal set; }
///
/// Gets the members guild bio.
/// This is not available to bots tho.
///
[JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
public string GuildBio { get; internal set; }
[JsonIgnore]
internal string _avatarHash;
///
/// Gets this member's display name.
///
[JsonIgnore]
public string DisplayName
=> this.Nickname ?? this.Username;
///
/// List of role ids
///
[JsonIgnore]
internal IReadOnlyList RoleIds
=> this._role_ids_lazy.Value;
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
internal List _role_ids;
[JsonIgnore]
private readonly Lazy> _role_ids_lazy;
///
/// Gets the list of roles associated with this member.
///
[JsonIgnore]
public IEnumerable Roles
=> this.RoleIds.Select(id => this.Guild.GetRole(id)).Where(x => x != null);
///
/// Gets the color associated with this user's top color-giving role, otherwise 0 (no color).
///
[JsonIgnore]
public DiscordColor Color
{
get
{
var role = this.Roles.OrderByDescending(xr => xr.Position).FirstOrDefault(xr => xr.Color.Value != 0);
return role != null ? role.Color : new DiscordColor();
}
}
///
/// Date the user joined the guild
///
[JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset JoinedAt { get; internal set; }
///
/// Date the user started boosting this server
///
[JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? PremiumSince { get; internal set; }
///
/// Date until the can communicate again.
///
[JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Include)]
public DateTimeOffset? CommunicationDisabledUntil { get; internal set; }
///
/// If the user is deafened
///
[JsonProperty("is_deafened", NullValueHandling = NullValueHandling.Ignore)]
public bool IsDeafened { get; internal set; }
///
/// If the user is muted
///
[JsonProperty("is_muted", NullValueHandling = NullValueHandling.Ignore)]
public bool IsMuted { get; internal set; }
///
/// Whether the user has not passed the guild's Membership Screening requirements yet.
///
[JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsPending { get; internal set; }
///
/// Gets this member's voice state.
///
[JsonIgnore]
public DiscordVoiceState VoiceState
=> this.Discord.Guilds[this._guild_id].VoiceStates.TryGetValue(this.Id, out var voiceState) ? voiceState : null;
[JsonIgnore]
internal ulong _guild_id = 0;
///
/// Gets the guild of which this member is a part of.
///
[JsonIgnore]
public DiscordGuild Guild
=> this.Discord.Guilds[this._guild_id];
///
/// Gets whether this member is the Guild owner.
///
[JsonIgnore]
public bool IsOwner
=> this.Id == this.Guild.OwnerId;
///
/// Gets the member's position in the role hierarchy, which is the member's highest role's position. Returns for the guild's owner.
///
[JsonIgnore]
public int Hierarchy
=> this.IsOwner ? int.MaxValue : this.RoleIds.Count == 0 ? 0 : this.Roles.Max(x => x.Position);
///
/// Gets the permissions for the current member.
///
[JsonIgnore]
public Permissions Permissions => this.GetPermissions();
#region Overridden user properties
///
/// Gets the user.
///
[JsonIgnore]
internal DiscordUser User
=> this.Discord.UserCache[this.Id];
///
/// Gets this member's username.
///
public override string Username
{
get => this.User.Username;
internal set => this.User.Username = value;
}
///
/// Gets the member's 4-digit discriminator.
///
public override string Discriminator
{
get => this.User.Discriminator;
internal set => this.User.Discriminator = value;
}
///
/// Gets the member's avatar hash.
///
[JsonIgnore]
public override string AvatarHash
{
get => this.User.AvatarHash;
internal set => this.User.AvatarHash = value;
}
///
/// Gets whether the member is a bot.
///
public override bool IsBot
{
get => this.User.IsBot;
internal set => this.User.IsBot = value;
}
///
/// Gets the member's email address.
/// This is only present in OAuth.
///
public override string Email
{
get => this.User.Email;
internal set => this.User.Email = value;
}
///
/// Gets whether the member has multi-factor authentication enabled.
///
public override bool? MfaEnabled
{
get => this.User.MfaEnabled;
internal set => this.User.MfaEnabled = value;
}
///
/// Gets whether the member is verified.
/// This is only present in OAuth.
///
public override bool? Verified
{
get => this.User.Verified;
internal set => this.User.Verified = value;
}
///
/// Gets the member's chosen language
///
public override string Locale
{
get => this.User.Locale;
internal set => this.User.Locale = value;
}
///
/// Gets the user's flags.
///
public override UserFlags? OAuthFlags
{
get => this.User.OAuthFlags;
internal set => this.User.OAuthFlags = value;
}
///
/// Gets the member's flags for OAuth.
///
public override UserFlags? Flags
{
get => this.User.Flags;
internal set => this.User.Flags = value;
}
#endregion
///
/// Creates a direct message channel to this member.
///
/// Direct message channel to this member.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// 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 CreateDmChannelAsync()
=> this.Discord.ApiClient.CreateDmAsync(this.Id);
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Content of the message to send.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// 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 SendMessageAsync(string content)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(content).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// 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 SendMessageAsync(DiscordEmbed embed)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(embed).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Content of the message to send.
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// 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 SendMessageAsync(string content, DiscordEmbed embed)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(content, embed).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Builder to with the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// 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 SendMessageAsync(DiscordMessageBuilder message)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(message).ConfigureAwait(false);
}
///
/// Sets this member's voice mute status.
///
/// Whether the member is to be muted.
/// Reason for audit logs.
///
/// 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 SetMuteAsync(bool mute, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this._guild_id, this.Id, default, default, mute, default, default, reason);
///
/// Sets this member's voice deaf status.
///
/// Whether the member is to be deafened.
/// Reason for audit logs.
///
/// 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 SetDeafAsync(bool deaf, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this._guild_id, this.Id, default, default, default, deaf, default, reason);
///
/// Modifies this member.
///
/// Action to perform on this member.
///
/// 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 async Task ModifyAsync(Action action)
{
var mdl = new MemberEditModel();
action(mdl);
if (mdl.VoiceChannel.HasValue && mdl.VoiceChannel.Value != null && mdl.VoiceChannel.Value.Type != ChannelType.Voice && mdl.VoiceChannel.Value.Type != ChannelType.Stage)
throw new ArgumentException("Given channel is not a voice or stage channel.", nameof(mdl.VoiceChannel));
if (mdl.Nickname.HasValue && this.Discord.CurrentUser.Id == this.Id)
{
await this.Discord.ApiClient.ModifyCurrentMemberNicknameAsync(this.Guild.Id, mdl.Nickname.Value,
mdl.AuditLogReason).ConfigureAwait(false);
await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, Optional.FromNoValue(),
mdl.Roles.IfPresent(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
mdl.VoiceChannel.IfPresent(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
}
else
{
await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, mdl.Nickname,
mdl.Roles.IfPresent(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
mdl.VoiceChannel.IfPresent(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
}
}
///
/// Adds a timeout to a member.
///
- /// The datetime offset to time out the user.
+ /// The datetime offset to time out the user. Up to 28 days.
/// Reason for audit logs.
///
/// 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 TimeOutAsync(DateTimeOffset until, string reason = null) => this.Discord.ApiClient.ModifyTimeOutAsync(this.Guild.Id, this.Id, until, reason);
///
/// Adds a timeout to a member.
///
- /// The timespan to time out the user.
+ /// The timespan to time out the user. Up to 28 days.
/// Reason for audit logs.
///
/// 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 TimeOutAsync(TimeSpan until, string reason = null) => this.TimeOutAsync(DateTimeOffset.UtcNow + until, reason);
///
/// Adds a timeout to a member.
///
- /// The datetime to time out the user.
+ /// The datetime to time out the user. Up to 28 days.
/// Reason for audit logs.
///
/// 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 TimeOutAsync(DateTime until, string reason = null) => this.TimeOutAsync(until.ToUniversalTime() - DateTime.UtcNow, reason);
///
/// Removes the timeout from a member.
///
/// Reason for audit logs.
///
/// 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 RemoveTimeOutAsync(string reason = null) => this.Discord.ApiClient.ModifyTimeOutAsync(this.Guild.Id, this.Id, null, reason);
///
/// Grants a role to the member.
///
/// Role to grant.
/// Reason for audit logs.
///
/// 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 GrantRoleAsync(DiscordRole role, string reason = null)
=> this.Discord.ApiClient.AddGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
///
/// Revokes a role from a member.
///
/// Role to revoke.
/// Reason for audit logs.
///
/// 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 RevokeRoleAsync(DiscordRole role, string reason = null)
=> this.Discord.ApiClient.RemoveGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
///
/// Sets the member's roles to ones specified.
///
/// Roles to set.
/// Reason for audit logs.
///
/// 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 ReplaceRolesAsync(IEnumerable roles, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, default,
new Optional>(roles.Select(xr => xr.Id)), default, default, default, reason);
///
/// Bans this member from their guild.
///
/// How many days to remove messages from.
/// Reason for audit logs.
///
/// 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 BanAsync(int delete_message_days = 0, string reason = null)
=> this.Guild.BanMemberAsync(this, delete_message_days, reason);
///
/// Unbans this member from their guild.
///
/// 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 UnbanAsync(string reason = null) => this.Guild.UnbanMemberAsync(this, reason);
///
/// Kicks this member from their guild.
///
/// Reason for audit logs.
///
/// [alias="KickAsync"]
/// 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 RemoveAsync(string reason = null)
=> this.Discord.ApiClient.RemoveGuildMemberAsync(this._guild_id, this.Id, reason);
///
/// Moves this member to the specified voice 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 PlaceInAsync(DiscordChannel channel)
=> channel.PlaceMemberAsync(this);
///
/// Updates the member's suppress state in a stage channel.
///
/// The channel the member is currently in.
/// Toggles the member's suppress state.
/// Thrown when the channel in not a voice channel.
public async Task UpdateVoiceStateAsync(DiscordChannel channel, bool? suppress)
{
if (channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated in a stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, channel.Id, suppress).ConfigureAwait(false);
}
///
/// Makes the user a speaker.
///
/// Thrown when the user is not inside an stage 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 async Task MakeSpeakerAsync()
{
var vs = this.VoiceState;
if (vs == null || vs.Channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, false).ConfigureAwait(false);
}
///
/// Moves the user to audience.
///
/// Thrown when the user is not inside an stage 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 async Task MoveToAudienceAsync()
{
var vs = this.VoiceState;
if (vs == null || vs.Channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, true).ConfigureAwait(false);
}
///
/// Calculates permissions in a given channel for this member.
///
/// Channel to calculate permissions for.
/// Calculated permissions for this member in the channel.
public Permissions PermissionsIn(DiscordChannel channel)
=> channel.PermissionsFor(this);
///
/// Get's the current member's roles based on the sum of the permissions of their given roles.
///
private Permissions GetPermissions()
{
if (this.Guild.OwnerId == this.Id)
return PermissionMethods.FULL_PERMS;
Permissions perms;
// assign @everyone permissions
var everyoneRole = this.Guild.EveryoneRole;
perms = everyoneRole.Permissions;
// assign permissions from member's roles (in order)
perms |= this.Roles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
// Adminstrator grants all permissions and cannot be overridden
return (perms & Permissions.Administrator) == Permissions.Administrator ? PermissionMethods.FULL_PERMS : perms;
}
///
/// Returns a string representation of this member.
///
/// String representation of this member.
public override string ToString() => $"Member {this.Id}; {this.Username}#{this.Discriminator} ({this.DisplayName})";
///
/// 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 DiscordMember);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordMember e) => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this._guild_id == e._guild_id));
///
/// 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._guild_id.GetHashCode();
return hash;
}
///
/// Gets whether the two objects are equal.
///
/// First member to compare.
/// Second member to compare.
/// Whether the two members are equal.
public static bool operator ==(DiscordMember e1, DiscordMember 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._guild_id == e2._guild_id));
}
///
/// Gets whether the two objects are not equal.
///
/// First member to compare.
/// Second member to compare.
/// Whether the two members are not equal.
public static bool operator !=(DiscordMember e1, DiscordMember e2)
=> !(e1 == e2);
}
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
index 337f94c53..e7c331067 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
@@ -1,716 +1,722 @@
// 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 DisCatSharp.Entities;
using Newtonsoft.Json;
namespace DisCatSharp.Net.Abstractions
{
///
/// The reason action.
///
internal interface IReasonAction
{
///
/// Gets or sets the reason.
///
string Reason { get; set; }
//[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
//public string Reason { get; set; }
}
///
/// Represents a guild create payload.
///
internal class RestGuildCreatePayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
///
/// Gets or sets the region id.
///
[JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)]
public string RegionId { get; set; }
///
/// Gets or sets the icon base64.
///
[JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
public Optional IconBase64 { get; set; }
///
/// Gets or sets the verification level.
///
[JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
public VerificationLevel? VerificationLevel { get; set; }
///
/// Gets or sets the default message notifications.
///
[JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
public DefaultMessageNotifications? DefaultMessageNotifications { get; set; }
///
/// Gets or sets the system channel flags.
///
[JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
public SystemChannelFlags? SystemChannelFlags { get; set; }
///
/// Gets or sets the roles.
///
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Roles { get; set; }
///
/// Gets or sets the channels.
///
[JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Channels { get; set; }
}
///
/// Represents a guild create from template payload.
///
internal sealed class RestGuildCreateFromTemplatePayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
///
/// Gets or sets the icon base64.
///
[JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
public Optional IconBase64 { get; set; }
}
///
/// Represents a guild modify payload.
///
internal sealed class RestGuildModifyPayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name")]
public Optional Name { get; set; }
///
/// Gets or sets the icon base64.
///
[JsonProperty("icon")]
public Optional IconBase64 { get; set; }
///
/// Gets or sets the verification level.
///
[JsonProperty("verification_level")]
public Optional VerificationLevel { get; set; }
///
/// Gets or sets the default message notifications.
///
[JsonProperty("default_message_notifications")]
public Optional DefaultMessageNotifications { get; set; }
///
/// Gets or sets the owner id.
///
[JsonProperty("owner_id")]
public Optional OwnerId { get; set; }
///
/// Gets or sets the splash base64.
///
[JsonProperty("splash")]
public Optional SplashBase64 { get; set; }
///
/// Gets or sets the banner base64.
///
[JsonProperty("banner")]
public Optional BannerBase64 { get; set; }
///
/// Gets or sets the discovery splash base64.
///
[JsonProperty("discorvery_splash")]
public Optional DiscoverySplashBase64 { get; set; }
///
/// Gets or sets the afk channel id.
///
[JsonProperty("afk_channel_id")]
public Optional AfkChannelId { get; set; }
///
/// Gets or sets the afk timeout.
///
[JsonProperty("afk_timeout")]
public Optional AfkTimeout { get; set; }
///
/// Gets or sets the mfa level.
///
[JsonProperty("mfa_level")]
public Optional MfaLevel { get; set; }
///
/// Gets or sets the explicit content filter.
///
[JsonProperty("explicit_content_filter")]
public Optional ExplicitContentFilter { get; set; }
///
/// Gets or sets the system channel id.
///
[JsonProperty("system_channel_id", NullValueHandling = NullValueHandling.Include)]
public Optional SystemChannelId { get; set; }
///
/// Gets or sets the system channel flags.
///
[JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
public Optional SystemChannelFlags { get; set; }
///
/// Gets or sets the rules channel id.
///
[JsonProperty("rules_channel_id")]
public Optional RulesChannelId { get; set; }
///
/// Gets or sets the public updates channel id.
///
[JsonProperty("public_updates_channel_id")]
public Optional PublicUpdatesChannelId { get; set; }
///
/// Gets or sets the preferred locale.
///
[JsonProperty("preferred_locale")]
public Optional PreferredLocale { get; set; }
///
/// Gets or sets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
public Optional Description { get; set; }
///
/// Gets or sets whether the premium progress bar should be enabled.
///
[JsonProperty("premium_progress_bar_enabled", NullValueHandling = NullValueHandling.Ignore)]
public Optional PremiumProgressBarEnabled { get; set; }
}
///
/// Represents a guild community modify payload.
///
internal sealed class RestGuildCommunityModifyPayload
{
///
/// Gets or sets the verification level.
///
[JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
public Optional VerificationLevel { get; set; }
///
/// Gets or sets the default message notifications.
///
[JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
public Optional DefaultMessageNotifications { get; set; }
///
/// Gets or sets the explicit content filter.
///
[JsonProperty("explicit_content_filter", NullValueHandling = NullValueHandling.Ignore)]
public Optional ExplicitContentFilter { get; set; }
///
/// Gets or sets the rules channel id.
///
[JsonProperty("rules_channel_id", NullValueHandling = NullValueHandling.Ignore)]
public Optional RulesChannelId { get; set; }
///
/// Gets or sets the public updates channel id.
///
[JsonProperty("public_updates_channel_id", NullValueHandling = NullValueHandling.Ignore)]
public Optional PublicUpdatesChannelId { get; set; }
///
/// Gets or sets the preferred locale.
///
[JsonProperty("preferred_locale")]
public Optional PreferredLocale { get; set; }
///
/// Gets or sets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
public Optional Description { get; set; }
///
/// Gets or sets the features.
///
[JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
public List Features { get; set; }
}
///
/// Represents a guild member add payload.
///
internal sealed class RestGuildMemberAddPayload : 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; }
///
/// Gets or sets the roles.
///
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Roles { get; set; }
///
/// Gets or sets a value indicating whether mute.
///
[JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
public bool? Mute { get; set; }
///
/// Gets or sets a value indicating whether deaf.
///
[JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
public bool? Deaf { get; set; }
}
///
/// Represents a guild channel reorder payload.
///
internal sealed class RestGuildChannelReorderPayload
{
///
/// Gets or sets the channel id.
///
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public ulong ChannelId { get; set; }
///
/// Gets or sets the position.
///
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int Position { get; set; }
}
///
/// Represents a guild channel new parent payload.
///
internal sealed class RestGuildChannelNewParentPayload
{
///
/// Gets or sets the channel id.
///
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public ulong ChannelId { get; set; }
///
/// Gets or sets the position.
///
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int Position { get; set; }
///
/// Gets or sets the parent id.
///
[JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
public Optional ParentId { get; set; }
///
/// Gets or sets a value indicating whether lock permissions.
///
[JsonProperty("lock_permissions", NullValueHandling = NullValueHandling.Ignore)]
public bool? LockPermissions { get; set; }
}
///
/// Represents a guild channel no parent payload.
///
internal sealed class RestGuildChannelNoParentPayload
{
///
/// Gets or sets the channel id.
///
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public ulong ChannelId { get; set; }
///
/// Gets or sets the position.
///
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int Position { get; set; }
///
/// Gets or sets the parent id.
///
[JsonProperty("parent_id", NullValueHandling = NullValueHandling.Include)]
public Optional ParentId { get; set; }
}
///
/// Represents a guild role reorder payload.
///
internal sealed class RestGuildRoleReorderPayload
{
///
/// Gets or sets the role id.
///
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public ulong RoleId { get; set; }
///
/// Gets or sets the position.
///
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int Position { get; set; }
}
///
/// Represents a guild member modify payload.
///
internal sealed class RestGuildMemberModifyPayload
{
///
/// Gets or sets the nickname.
///
[JsonProperty("nick")]
public Optional Nickname { get; set; }
///
/// Gets or sets the role ids.
///
[JsonProperty("roles")]
public Optional> RoleIds { get; set; }
///
/// Gets or sets the mute.
///
[JsonProperty("mute")]
public Optional Mute { get; set; }
///
/// Gets or sets the deafen.
///
[JsonProperty("deaf")]
public Optional Deafen { get; set; }
///
/// Gets or sets the voice channel id.
///
[JsonProperty("channel_id")]
public Optional VoiceChannelId { get; set; }
+ }
+ ///
+ /// Represents a guild member timeout modify payload.
+ ///
+ internal sealed class RestGuildMemberTimeoutModifyPayload
+ {
///
/// Date until the can communicate again.
///
[JsonProperty("communication_disabled_until")]
- public Optional CommunicationDisabledUntil { get; internal set; }
+ public DateTimeOffset? CommunicationDisabledUntil { get; internal set; }
}
- ///
- /// Represents a guild role payload.
- ///
+ ///
+ /// Represents a guild role payload.
+ ///
internal sealed class RestGuildRolePayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
///
/// Gets or sets the permissions.
///
[JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
public Permissions? Permissions { get; set; }
///
/// Gets or sets the color.
///
[JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)]
public int? Color { get; set; }
///
/// Gets or sets a value indicating whether hoist.
///
[JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)]
public bool? Hoist { get; set; }
///
/// Gets or sets a value indicating whether mentionable.
///
[JsonProperty("mentionable", NullValueHandling = NullValueHandling.Ignore)]
public bool? Mentionable { get; set; }
///
/// Gets or sets the icon base64.
///
[JsonProperty("icon")]
public Optional IconBase64 { get; set; }
///
/// Gets or sets the icon base64.
///
[JsonProperty("unicode_emoji")]
public Optional UnicodeEmoji { get; set; }
}
///
/// Represents a guild prune result payload.
///
internal sealed class RestGuildPruneResultPayload
{
///
/// Gets or sets the pruned.
///
[JsonProperty("pruned", NullValueHandling = NullValueHandling.Ignore)]
public int? Pruned { get; set; }
}
///
/// Represents a guild integration attach payload.
///
internal sealed class RestGuildIntegrationAttachPayload
{
///
/// Gets or sets the type.
///
[JsonProperty("type")]
public string Type { get; set; }
///
/// Gets or sets the id.
///
[JsonProperty("id")]
public ulong Id { get; set; }
}
///
/// Represents a guild integration modify payload.
///
internal sealed class RestGuildIntegrationModifyPayload
{
///
/// Gets or sets the expire behavior.
///
[JsonProperty("expire_behavior", NullValueHandling = NullValueHandling.Ignore)]
public int? ExpireBehavior { get; set; }
///
/// Gets or sets the expire grace period.
///
[JsonProperty("expire_grace_period", NullValueHandling = NullValueHandling.Ignore)]
public int? ExpireGracePeriod { get; set; }
///
/// Gets or sets a value indicating whether enable emoticons.
///
[JsonProperty("enable_emoticons", NullValueHandling = NullValueHandling.Ignore)]
public bool? EnableEmoticons { get; set; }
}
///
/// Represents a guild emoji modify payload.
///
internal class RestGuildEmojiModifyPayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name")]
public string Name { get; set; }
///
/// Gets or sets the roles.
///
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
public ulong[] Roles { get; set; }
}
///
/// Represents a guild emoji create payload.
///
internal class RestGuildEmojiCreatePayload : RestGuildEmojiModifyPayload
{
///
/// Gets or sets the image b64.
///
[JsonProperty("image")]
public string ImageB64 { get; set; }
}
///
/// Represents a guild widget settings payload.
///
internal class RestGuildWidgetSettingsPayload
{
///
/// Gets or sets a value indicating whether enabled.
///
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
public bool? Enabled { get; set; }
///
/// Gets or sets the channel id.
///
[JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong? ChannelId { get; set; }
}
///
/// Represents a guild template create or modify payload.
///
internal class RestGuildTemplateCreateOrModifyPayload
{
///
/// Gets or sets the name.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
public string Name { get; set; }
///
/// Gets or sets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
public string Description { get; set; }
}
///
/// Represents a guild membership screening form modify payload.
///
internal class RestGuildMembershipScreeningFormModifyPayload
{
///
/// Gets or sets the enabled.
///
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
public Optional Enabled { get; set; }
///
/// Gets or sets the fields.
///
[JsonProperty("form_fields", NullValueHandling = NullValueHandling.Ignore)]
public Optional Fields { get; set; }
///
/// Gets or sets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public Optional Description { get; set; }
}
///
/// Represents a guild welcome screen modify payload.
///
internal class RestGuildWelcomeScreenModifyPayload
{
///
/// Gets or sets the enabled.
///
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
public Optional Enabled { get; set; }
///
/// Gets or sets the welcome channels.
///
[JsonProperty("welcome_channels", NullValueHandling = NullValueHandling.Ignore)]
public Optional> WelcomeChannels { get; set; }
///
/// Gets or sets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public Optional Description { get; set; }
}
///
/// Represents a guild update current user voice state payload.
///
internal class RestGuildUpdateCurrentUserVoiceStatePayload
{
///
/// Gets or sets the channel id.
///
[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }
///
/// Gets or sets a value indicating whether suppress.
///
[JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
public bool? Suppress { get; set; }
///
/// Gets or sets the request to speak timestamp.
///
[JsonProperty("request_to_speak_timestamp", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? RequestToSpeakTimestamp { get; set; }
}
///
/// Represents a guild update user voice state payload.
///
internal class RestGuildUpdateUserVoiceStatePayload
{
///
/// Gets or sets the channel id.
///
[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }
///
/// Gets or sets a value indicating whether suppress.
///
[JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
public bool? Suppress { get; set; }
}
}
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index af89297f0..75598a18a 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -1,5181 +1,5191 @@
// 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.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
///
/// Creates the guild channel async.
///
/// The guild_id.
/// The name.
/// The type.
/// The parent.
/// The topic.
/// The bitrate.
/// The user_limit.
/// The overwrites.
/// If true, nsfw.
/// The per user rate limit.
/// The quality mode.
/// The reason.
/// A Task.
internal async Task CreateGuildChannelAsync(ulong guild_id, string name, ChannelType type, ulong? parent, Optional topic, int? bitrate, int? user_limit, IEnumerable overwrites, bool? nsfw, Optional perUserRateLimit, VideoQualityMode? qualityMode, string reason)
{
var restoverwrites = new List();
if (overwrites != null)
foreach (var ow in overwrites)
restoverwrites.Add(ow.Build());
var pld = new RestChannelCreatePayload
{
Name = name,
Type = type,
Parent = parent,
Topic = topic,
Bitrate = bitrate,
UserLimit = user_limit,
PermissionOverwrites = restoverwrites,
Nsfw = nsfw,
PerUserRateLimit = perUserRateLimit,
QualityMode = qualityMode
};
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.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)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Discord = this.Discord;
foreach (var xo in ret._permissionOverwrites)
{
xo.Discord = this.Discord;
xo._channel_id = ret.Id;
}
return ret;
}
///
/// Modifies the channel async.
///
/// The channel_id.
/// The name.
/// The position.
/// The topic.
/// If true, nsfw.
/// The parent.
/// The bitrate.
/// The user_limit.
/// The per user rate limit.
/// The rtc region.
/// The quality mode.
/// The default auto archive duration.
/// The type.
/// The permission overwrites.
/// The banner.
/// The reason.
internal Task ModifyChannelAsync(ulong channel_id, string name, int? position, Optional topic, bool? nsfw, Optional parent, int? bitrate, int? user_limit, Optional perUserRateLimit, Optional rtcRegion, VideoQualityMode? qualityMode, ThreadAutoArchiveDuration? autoArchiveDuration, Optional type, IEnumerable permissionOverwrites, Optional bannerb64, string reason)
{
List restoverwrites = null;
if (permissionOverwrites != null)
{
restoverwrites = new List();
foreach (var ow in permissionOverwrites)
restoverwrites.Add(ow.Build());
}
var pld = new RestChannelModifyPayload
{
Name = name,
Position = position,
Topic = topic,
Nsfw = nsfw,
Parent = parent,
Bitrate = bitrate,
UserLimit = user_limit,
PerUserRateLimit = perUserRateLimit,
RtcRegion = rtcRegion,
QualityMode = qualityMode,
DefaultAutoArchiveDuration = autoArchiveDuration,
Type = type,
PermissionOverwrites = restoverwrites,
BannerBase64 = bannerb64
};
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.PATCH, route, new { channel_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 channel async.
///
/// The channel_id.
/// A Task.
internal async Task GetChannelAsync(ulong channel_id)
{
var route = $"{Endpoints.CHANNELS}/:channel_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { channel_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.Discord = this.Discord;
foreach (var xo in ret._permissionOverwrites)
{
xo.Discord = this.Discord;
xo._channel_id = ret.Id;
}
return ret;
}
///
/// Deletes the channel async.
///
/// The channel_id.
/// The reason.
/// A Task.
internal Task DeleteChannelAsync(ulong channel_id, 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 }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Gets the message async.
///
/// The channel_id.
/// The message_id.
/// A Task.
internal async Task GetMessageAsync(ulong channel_id, ulong message_id)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { channel_id, message_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 = 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.
/// A Task.
internal async Task CreateMessageAsync(ulong channel_id, 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 }, 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 = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
///
/// Creates the message async.
///
/// The channel_id.
/// The builder.
/// A Task.
internal async Task CreateMessageAsync(ulong channel_id, 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 }, 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 = this.PrepareMessage(JObject.Parse(res.Response));
return ret;
}
else
{
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 }, 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 ret = this.PrepareMessage(JObject.Parse(res.Response));
foreach (var file in builder._files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
return ret;
}
}
///
/// Gets the guild channels async.
///
/// The guild_id.
/// A Task.
internal async Task> GetGuildChannelsAsync(ulong guild_id)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
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 channels_raw = JsonConvert.DeserializeObject>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
foreach (var ret in channels_raw)
foreach (var xo in ret._permissionOverwrites)
{
xo.Discord = this.Discord;
xo._channel_id = ret.Id;
}
return new ReadOnlyCollection(new List(channels_raw));
}
///
/// Creates the stage instance async.
///
/// The channel_id.
/// The topic.
/// Whether everyone should be notified about the stage.
/// The privacy_level.
/// The reason.
internal async Task CreateStageInstanceAsync(ulong channel_id, string topic, bool send_start_notification, StagePrivacyLevel privacy_level, string reason)
{
var pld = new RestStageInstanceCreatePayload
{
ChannelId = channel_id,
Topic = topic,
PrivacyLevel = privacy_level,
SendStartNotification = send_start_notification
};
var route = $"{Endpoints.STAGE_INSTANCES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
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 stageInstance = JsonConvert.DeserializeObject(res.Response);
return stageInstance;
}
///
/// Gets the stage instance async.
///
/// The channel_id.
internal async Task GetStageInstanceAsync(ulong channel_id)
{
var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { channel_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 stageInstance = JsonConvert.DeserializeObject(res.Response);
return stageInstance;
}
///
/// Modifies the stage instance async.
///
/// The channel_id.
/// The topic.
/// The privacy_level.
/// The reason.
internal Task ModifyStageInstanceAsync(ulong channel_id, Optional topic, Optional privacy_level, string reason)
{
var pld = new RestStageInstanceModifyPayload
{
Topic = topic,
PrivacyLevel = privacy_level
};
var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { channel_id }, out var path);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Deletes the stage instance async.
///
/// The channel_id.
/// The reason.
internal Task DeleteStageInstanceAsync(ulong channel_id, string reason)
{
var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { channel_id }, out var path);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Gets the channel messages async.
///
/// The channel id.
/// The limit.
/// The before.
/// The after.
/// The around.
/// A Task.
internal async Task> GetChannelMessagesAsync(ulong channel_id, 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 }, 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 msgs_raw = JArray.Parse(res.Response);
var msgs = new List();
foreach (var xj in msgs_raw)
msgs.Add(this.PrepareMessage(xj));
return new ReadOnlyCollection(new List(msgs));
}
///
/// Gets the channel message async.
///
/// The channel_id.
/// The message_id.
/// A Task.
internal async Task GetChannelMessageAsync(ulong channel_id, ulong message_id)
{
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { channel_id, message_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 = 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.
/// A Task.
internal async Task EditMessageAsync(ulong channel_id, ulong message_id, Optional content, Optional> embeds, IEnumerable mentions, IReadOnlyList components, Optional suppress_embed, IReadOnlyCollection files)
{
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.HasValue ? (string)content : null,
HasEmbed = embeds.HasValue && (embeds.Value?.Any() ?? false),
Embeds = embeds.HasValue && (embeds.Value?.Any() ?? false) ? embeds.Value : null,
Components = components,
Flags = suppress_embed.HasValue ? (bool)suppress_embed ? MessageFlags.SuppressedEmbeds : null : null
};
pld.Mentions = new DiscordMentions(mentions ?? Mentions.None, false, mentions?.OfType().Any() ?? false);
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, message_id }, 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 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;
}
///