* BaseDiscordClient * ClientHelper * DiscordRestApiClient * DiscordRestClient * DiscordContractResolver * RestGuildUser * RestUser * RestWebhookUser * RestWebhook * RestRole * RoleHelper * RestApplicationCommandOption * InteractionHelper * GuildHelper * RestGuildpull/1923/head
| @@ -10,6 +10,7 @@ namespace Discord.Rest | |||
| { | |||
| public abstract class BaseDiscordClient : IDiscordClient | |||
| { | |||
| #region BaseDiscordClient | |||
| public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } | |||
| internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); | |||
| @@ -155,8 +156,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| public Task<BotGateway> GetBotGatewayAsync(RequestOptions options = null) | |||
| => ClientHelper.GetBotGatewayAsync(this, options); | |||
| #endregion | |||
| //IDiscordClient | |||
| #region IDiscordClient | |||
| /// <inheritdoc /> | |||
| ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; | |||
| /// <inheritdoc /> | |||
| @@ -235,5 +237,6 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| Task IDiscordClient.StopAsync() | |||
| => Task.Delay(0); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -10,7 +10,7 @@ namespace Discord.Rest | |||
| { | |||
| internal static class ClientHelper | |||
| { | |||
| //Applications | |||
| #region Applications | |||
| public static async Task<RestApplication> GetApplicationInfoAsync(BaseDiscordClient client, RequestOptions options) | |||
| { | |||
| var model = await client.ApiClient.GetMyApplicationAsync(options).ConfigureAwait(false); | |||
| @@ -263,5 +263,6 @@ namespace Discord.Rest | |||
| public static Task RemoveRoleAsync(BaseDiscordClient client, ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | |||
| => client.ApiClient.RemoveRoleAsync(guildId, userId, roleId, options); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -24,6 +24,7 @@ namespace Discord.API | |||
| { | |||
| internal class DiscordRestApiClient : IDisposable | |||
| { | |||
| #region DiscordRestApiClient | |||
| private static readonly ConcurrentDictionary<string, Func<BucketIds, BucketId>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, BucketId>>(); | |||
| public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } | |||
| @@ -167,8 +168,9 @@ namespace Discord.API | |||
| internal virtual Task ConnectInternalAsync() => Task.Delay(0); | |||
| internal virtual Task DisconnectInternalAsync(Exception ex = null) => Task.Delay(0); | |||
| #endregion | |||
| //Core | |||
| #region Core | |||
| internal Task SendAsync(string method, Expression<Func<string>> endpointExpr, BucketIds ids, | |||
| ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) | |||
| => SendAsync(method, GetEndpoint(endpointExpr), GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options); | |||
| @@ -271,15 +273,17 @@ namespace Discord.API | |||
| return responseStream; | |||
| } | |||
| #endregion | |||
| //Auth | |||
| #region Auth | |||
| public async Task ValidateTokenAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| await SendAsync("GET", () => "auth/login", new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Gateway | |||
| #region Gateway | |||
| public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| @@ -290,8 +294,9 @@ namespace Discord.API | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await SendAsync<GetBotGatewayResponse>("GET", () => "gateway/bot", new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channels | |||
| #region Channels | |||
| public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| @@ -414,8 +419,9 @@ namespace Discord.API | |||
| break; | |||
| } | |||
| } | |||
| #endregion | |||
| // Threads | |||
| #region Threads | |||
| public async Task<Channel> ModifyThreadAsync(ulong channelId, ModifyThreadParams args, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| @@ -582,8 +588,9 @@ namespace Discord.API | |||
| return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/users/@me/threads/archived/private{query}", bucket, options: options); | |||
| } | |||
| #endregion | |||
| // stage | |||
| #region Stage | |||
| public async Task<StageInstance> CreateStageInstanceAsync(CreateStageInstanceParams args, RequestOptions options = null) | |||
| { | |||
| @@ -658,8 +665,9 @@ namespace Discord.API | |||
| await SendJsonAsync("PATCH", () => $"guilds/{guildId}/voice-states/{userId}", args, bucket, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| // roles | |||
| #region Roles | |||
| public async Task AddRoleAsync(ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -682,8 +690,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| await SendAsync("DELETE", () => $"guilds/{guildId}/members/{userId}/roles/{roleId}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channel Messages | |||
| #region Channel Messages | |||
| public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| @@ -886,8 +895,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| // Stickers | |||
| #region Stickers | |||
| public async Task<Sticker> GetStickerAsync(ulong id, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(id, 0, nameof(id)); | |||
| @@ -1044,8 +1054,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| await SendAsync("POST", () => $"channels/{channelId}/messages/{messageId}/crosspost", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channel Permissions | |||
| #region Channel Permissions | |||
| public async Task ModifyChannelPermissionsAsync(ulong channelId, ulong targetId, ModifyChannelPermissionsParams args, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| @@ -1065,8 +1076,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| await SendAsync("DELETE", () => $"channels/{channelId}/permissions/{targetId}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channel Pins | |||
| #region Channel Pins | |||
| public async Task AddPinAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
| { | |||
| Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | |||
| @@ -1094,8 +1106,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| return await SendAsync<IReadOnlyCollection<Message>>("GET", () => $"channels/{channelId}/pins", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channel Recipients | |||
| #region Channel Recipients | |||
| public async Task AddGroupRecipientAsync(ulong channelId, ulong userId, RequestOptions options = null) | |||
| { | |||
| Preconditions.GreaterThan(channelId, 0, nameof(channelId)); | |||
| @@ -1115,8 +1128,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| await SendAsync("DELETE", () => $"channels/{channelId}/recipients/{userId}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Interactions | |||
| #region Interactions | |||
| public async Task<ApplicationCommand[]> GetGlobalApplicationCommandsAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| @@ -1252,8 +1266,9 @@ namespace Discord.API | |||
| return await TrySendApplicationCommand(SendJsonAsync<ApplicationCommand[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands", commands, bucket, options: options)).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Interaction Responses | |||
| #region Interaction Responses | |||
| public async Task CreateInteractionResponse(InteractionResponse response, ulong interactionId, string interactionToken, RequestOptions options = null) | |||
| { | |||
| if(response.Data.IsSpecified && response.Data.Value.Content.IsSpecified) | |||
| @@ -1322,8 +1337,9 @@ namespace Discord.API | |||
| await SendAsync("DELETE", () => $"webhooks/{CurrentUserId}/{token}/messages/{id}", new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| // Application Command permissions | |||
| #region Application Command permissions | |||
| public async Task<GuildApplicationCommandPermission[]> GetGuildApplicationCommandPermissions(ulong guildId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1362,8 +1378,9 @@ namespace Discord.API | |||
| return await SendJsonAsync<GuildApplicationCommandPermission[]>("PUT", () => $"applications/{this.CurrentUserId}/guilds/{guildId}/commands/permissions", permissions, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guilds | |||
| #region Guilds | |||
| public async Task<Guild> GetGuildAsync(ulong guildId, bool withCounts, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1436,8 +1453,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| return await SendAsync<GetGuildPruneCountResponse>("GET", () => $"guilds/{guildId}/prune?days={args.Days}{endpointRoleIds}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Bans | |||
| #region Guild Bans | |||
| public async Task<IReadOnlyCollection<Ban>> GetGuildBansAsync(ulong guildId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1488,8 +1506,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| await SendAsync("DELETE", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Widget | |||
| #region Guild Widget | |||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | |||
| public async Task<GuildWidget> GetGuildWidgetAsync(ulong guildId, RequestOptions options = null) | |||
| { | |||
| @@ -1514,8 +1533,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| return await SendJsonAsync<GuildWidget>("PATCH", () => $"guilds/{guildId}/widget", args, ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Integrations | |||
| #region Guild Integrations | |||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | |||
| public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null) | |||
| { | |||
| @@ -1567,8 +1587,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| return await SendAsync<Integration>("POST", () => $"guilds/{guildId}/integrations/{integrationId}/sync", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Invites | |||
| #region Guild Invites | |||
| /// <exception cref="ArgumentException"><paramref name="inviteId"/> cannot be blank.</exception> | |||
| /// <exception cref="ArgumentNullException"><paramref name="inviteId"/> must not be <see langword="null"/>.</exception> | |||
| public async Task<InviteMetadata> GetInviteAsync(string inviteId, RequestOptions options = null) | |||
| @@ -1651,8 +1672,9 @@ namespace Discord.API | |||
| return await SendAsync<Invite>("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Members | |||
| #region Guild Members | |||
| public async Task<GuildMember> AddGuildMemberAsync(ulong guildId, ulong userId, AddGuildMemberParams args, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1750,8 +1772,9 @@ namespace Discord.API | |||
| Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members/search?limit={limit}&query={query}"; | |||
| return await SendAsync<IReadOnlyCollection<GuildMember>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild Roles | |||
| #region Guild Roles | |||
| public async Task<IReadOnlyCollection<Role>> GetGuildRolesAsync(ulong guildId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1798,8 +1821,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Guild emoji | |||
| #region Guild emoji | |||
| public async Task<IReadOnlyCollection<Emoji>> GetGuildEmotesAsync(ulong guildId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1851,8 +1875,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| await SendAsync("DELETE", () => $"guilds/{guildId}/emojis/{emoteId}", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Users | |||
| #region Users | |||
| public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | |||
| @@ -1864,8 +1889,9 @@ namespace Discord.API | |||
| } | |||
| catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } | |||
| } | |||
| #endregion | |||
| //Current User/DMs | |||
| #region Current User/DMs | |||
| public async Task<User> GetMyUserAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| @@ -1924,8 +1950,9 @@ namespace Discord.API | |||
| return await SendJsonAsync<Channel>("POST", () => "users/@me/channels", args, new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Voice Regions | |||
| #region Voice Regions | |||
| public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| @@ -1939,8 +1966,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(guildId: guildId); | |||
| return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", () => $"guilds/{guildId}/regions", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Audit logs | |||
| #region Audit logs | |||
| public async Task<AuditLog> GetAuditLogsAsync(ulong guildId, GetAuditLogsParams args, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
| @@ -1969,12 +1997,13 @@ namespace Discord.API | |||
| .Append(args.ActionType.Value); | |||
| } | |||
| // still use string interp for the query w/o params, as this is necessary for CreateBucketId | |||
| // Still use string interp for the query w/o params, as this is necessary for CreateBucketId | |||
| endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}{queryArgs.ToString()}"; | |||
| return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Webhooks | |||
| #region Webhooks | |||
| public async Task<Webhook> CreateWebhookAsync(ulong channelId, CreateWebhookParams args, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| @@ -2037,8 +2066,9 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| return await SendAsync<IReadOnlyCollection<Webhook>>("GET", () => $"channels/{channelId}/webhooks", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Helpers | |||
| #region Helpers | |||
| /// <exception cref="InvalidOperationException">Client is not logged in.</exception> | |||
| protected void CheckState() | |||
| { | |||
| @@ -2248,5 +2278,6 @@ namespace Discord.API | |||
| return (expr as MemberExpression).Member.Name; | |||
| } | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -12,6 +12,7 @@ namespace Discord.Rest | |||
| /// </summary> | |||
| public class DiscordRestClient : BaseDiscordClient, IDiscordClient | |||
| { | |||
| #region DiscordRestClient | |||
| private RestApplication _applicationInfo; | |||
| /// <summary> | |||
| @@ -138,7 +139,9 @@ namespace Discord.Rest | |||
| => MessageHelper.RemoveAllReactionsAsync(channelId, messageId, this, options); | |||
| public Task RemoveAllReactionsForEmoteAsync(ulong channelId, ulong messageId, IEmote emote, RequestOptions options = null) | |||
| => MessageHelper.RemoveAllReactionsForEmoteAsync(channelId, messageId, emote, this, options); | |||
| //IDiscordClient | |||
| #endregion | |||
| #region IDiscordClient | |||
| /// <inheritdoc /> | |||
| async Task<IApplication> IDiscordClient.GetApplicationInfoAsync(RequestOptions options) | |||
| => await GetApplicationInfoAsync(options).ConfigureAwait(false); | |||
| @@ -229,5 +232,6 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| async Task<IApplicationCommand> IDiscordClient.GetGlobalApplicationCommandAsync(ulong id, RequestOptions options) | |||
| => await ClientHelper.GetGlobalApplicationCommand(this, id, options).ConfigureAwait(false); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -14,7 +14,7 @@ namespace Discord.Rest | |||
| { | |||
| internal static class GuildHelper | |||
| { | |||
| //General | |||
| #region General | |||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <c>null</c>.</exception> | |||
| public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | |||
| Action<GuildProperties> func, RequestOptions options) | |||
| @@ -123,8 +123,9 @@ namespace Discord.Rest | |||
| { | |||
| await client.ApiClient.DeleteGuildAsync(guild.Id, options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Bans | |||
| #region Bans | |||
| public static async Task<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild guild, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -148,8 +149,9 @@ namespace Discord.Rest | |||
| { | |||
| await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId, options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| //Channels | |||
| #region Channels | |||
| public static async Task<RestGuildChannel> GetChannelAsync(IGuild guild, BaseDiscordClient client, | |||
| ulong id, RequestOptions options) | |||
| { | |||
| @@ -275,16 +277,18 @@ namespace Discord.Rest | |||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | |||
| return RestCategoryChannel.Create(client, guild, model); | |||
| } | |||
| #endregion | |||
| //Voice Regions | |||
| #region Voice Regions | |||
| public static async Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(IGuild guild, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| var models = await client.ApiClient.GetGuildVoiceRegionsAsync(guild.Id, options).ConfigureAwait(false); | |||
| return models.Select(x => RestVoiceRegion.Create(client, x)).ToImmutableArray(); | |||
| } | |||
| #endregion | |||
| //Integrations | |||
| #region Integrations | |||
| public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -298,8 +302,9 @@ namespace Discord.Rest | |||
| var model = await client.ApiClient.CreateGuildIntegrationAsync(guild.Id, args, options).ConfigureAwait(false); | |||
| return RestGuildIntegration.Create(client, guild, model); | |||
| } | |||
| #endregion | |||
| //Interactions | |||
| #region Interactions | |||
| public static async Task<IReadOnlyCollection<RestGuildCommand>> GetSlashCommandsAsync(IGuild guild, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -312,8 +317,9 @@ namespace Discord.Rest | |||
| var model = await client.ApiClient.GetGuildApplicationCommandAsync(guild.Id, id, options); | |||
| return RestGuildCommand.Create(client, model, guild.Id); | |||
| } | |||
| #endregion | |||
| //Invites | |||
| #region Invites | |||
| public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuild guild, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -329,8 +335,9 @@ namespace Discord.Rest | |||
| inviteModel.Uses = vanityModel.Uses; | |||
| return RestInviteMetadata.Create(client, guild, null, inviteModel); | |||
| } | |||
| #endregion | |||
| //Roles | |||
| #region Roles | |||
| /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> | |||
| public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | |||
| string name, GuildPermissions? permissions, Color? color, bool isHoisted, bool isMentionable, RequestOptions options) | |||
| @@ -350,8 +357,9 @@ namespace Discord.Rest | |||
| return RestRole.Create(client, guild, model); | |||
| } | |||
| #endregion | |||
| //Users | |||
| #region Users | |||
| public static async Task<RestGuildUser> AddGuildUserAsync(IGuild guild, BaseDiscordClient client, ulong userId, string accessToken, | |||
| Action<AddGuildUserProperties> func, RequestOptions options) | |||
| { | |||
| @@ -470,8 +478,9 @@ namespace Discord.Rest | |||
| var models = await client.ApiClient.SearchGuildMembersAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | |||
| return models.Select(x => RestGuildUser.Create(client, guild, x)).ToImmutableArray(); | |||
| } | |||
| #endregion | |||
| // Audit logs | |||
| #region Audit logs | |||
| public static IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client, | |||
| ulong? from, int? limit, RequestOptions options, ulong? userId = null, ActionType? actionType = null) | |||
| { | |||
| @@ -503,8 +512,9 @@ namespace Discord.Rest | |||
| count: limit | |||
| ); | |||
| } | |||
| #endregion | |||
| //Webhooks | |||
| #region Webhooks | |||
| public static async Task<RestWebhook> GetWebhookAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) | |||
| { | |||
| var model = await client.ApiClient.GetWebhookAsync(id, options: options).ConfigureAwait(false); | |||
| @@ -517,8 +527,9 @@ namespace Discord.Rest | |||
| var models = await client.ApiClient.GetGuildWebhooksAsync(guild.Id, options).ConfigureAwait(false); | |||
| return models.Select(x => RestWebhook.Create(client, guild, x)).ToImmutableArray(); | |||
| } | |||
| #endregion | |||
| //Emotes | |||
| #region Emotes | |||
| public static async Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) | |||
| { | |||
| var models = await client.ApiClient.GetGuildEmotesAsync(guild.Id, options).ConfigureAwait(false); | |||
| @@ -637,5 +648,6 @@ namespace Discord.Rest | |||
| public static async Task DeleteStickerAsync(BaseDiscordClient client, ulong guildId, ISticker sticker, RequestOptions options = null) | |||
| => await client.ApiClient.DeleteStickerAsync(guildId, sticker.Id, options).ConfigureAwait(false); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -18,6 +18,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestGuild : RestEntity<ulong>, IGuild, IUpdateable | |||
| { | |||
| #region RestGuild | |||
| private ImmutableDictionary<ulong, RestRole> _roles; | |||
| private ImmutableArray<GuildEmote> _emotes; | |||
| private ImmutableArray<CustomSticker> _stickers; | |||
| @@ -217,8 +218,9 @@ namespace Discord.Rest | |||
| WidgetChannelId = model.ChannelId; | |||
| IsWidgetEnabled = model.Enabled; | |||
| } | |||
| #endregion | |||
| //General | |||
| #region General | |||
| /// <inheritdoc /> | |||
| public async Task UpdateAsync(RequestOptions options = null) | |||
| => Update(await Discord.ApiClient.GetGuildAsync(Id, false, options).ConfigureAwait(false)); | |||
| @@ -277,8 +279,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| public Task LeaveAsync(RequestOptions options = null) | |||
| => GuildHelper.LeaveAsync(this, Discord, options); | |||
| #endregion | |||
| //Interactions | |||
| #region Interactions | |||
| /// <summary> | |||
| /// Deletes all slash commands in the current guild. | |||
| /// </summary> | |||
| @@ -311,8 +314,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public Task<RestGuildCommand> GetSlashCommandAsync(ulong id, RequestOptions options = null) | |||
| => GuildHelper.GetSlashCommandAsync(this, id, Discord, options); | |||
| #endregion | |||
| //Bans | |||
| #region Bans | |||
| /// <summary> | |||
| /// Gets a collection of all users banned in this guild. | |||
| /// </summary> | |||
| @@ -360,8 +364,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| public Task RemoveBanAsync(ulong userId, RequestOptions options = null) | |||
| => GuildHelper.RemoveBanAsync(this, Discord, userId, options); | |||
| #endregion | |||
| //Channels | |||
| #region Channels | |||
| /// <summary> | |||
| /// Gets a collection of all channels in this guild. | |||
| /// </summary> | |||
| @@ -697,14 +702,16 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | |||
| => GuildHelper.GetVoiceRegionsAsync(this, Discord, options); | |||
| #endregion | |||
| //Integrations | |||
| #region Integrations | |||
| public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null) | |||
| => GuildHelper.GetIntegrationsAsync(this, Discord, options); | |||
| public Task<RestGuildIntegration> CreateIntegrationAsync(ulong id, string type, RequestOptions options = null) | |||
| => GuildHelper.CreateIntegrationAsync(this, Discord, id, type, options); | |||
| #endregion | |||
| //Invites | |||
| #region Invites | |||
| /// <summary> | |||
| /// Gets a collection of all invites in this guild. | |||
| /// </summary> | |||
| @@ -724,8 +731,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public Task<RestInviteMetadata> GetVanityInviteAsync(RequestOptions options = null) | |||
| => GuildHelper.GetVanityInviteAsync(this, Discord, options); | |||
| #endregion | |||
| //Roles | |||
| #region Roles | |||
| /// <summary> | |||
| /// Gets a role in this guild. | |||
| /// </summary> | |||
| @@ -765,8 +773,9 @@ namespace Discord.Rest | |||
| _roles = _roles.Add(role.Id, role); | |||
| return role; | |||
| } | |||
| #endregion | |||
| //Users | |||
| #region Users | |||
| /// <summary> | |||
| /// Gets a collection of all users in this guild. | |||
| /// </summary> | |||
| @@ -860,8 +869,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public Task<IReadOnlyCollection<RestGuildUser>> SearchUsersAsync(string query, int limit = DiscordConfig.MaxUsersPerBatch, RequestOptions options = null) | |||
| => GuildHelper.SearchUsersAsync(this, Discord, query, limit, options); | |||
| #endregion | |||
| //Audit logs | |||
| #region Audit logs | |||
| /// <summary> | |||
| /// Gets the specified number of audit log entries for this guild. | |||
| /// </summary> | |||
| @@ -876,8 +886,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public IAsyncEnumerable<IReadOnlyCollection<RestAuditLogEntry>> GetAuditLogsAsync(int limit, RequestOptions options = null, ulong? beforeId = null, ulong? userId = null, ActionType? actionType = null) | |||
| => GuildHelper.GetAuditLogsAsync(this, Discord, beforeId, limit, options, userId: userId, actionType: actionType); | |||
| #endregion | |||
| //Webhooks | |||
| #region Webhooks | |||
| /// <summary> | |||
| /// Gets a webhook found within this guild. | |||
| /// </summary> | |||
| @@ -900,8 +911,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null) | |||
| => GuildHelper.GetWebhooksAsync(this, Discord, options); | |||
| #endregion | |||
| //Interactions | |||
| #region Interactions | |||
| /// <summary> | |||
| /// Gets this guilds slash commands commands | |||
| /// </summary> | |||
| @@ -961,6 +973,7 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public override string ToString() => Name; | |||
| private string DebuggerDisplay => $"{Name} ({Id})"; | |||
| #endregion | |||
| #region Emotes | |||
| /// <inheritdoc /> | |||
| @@ -11,6 +11,7 @@ namespace Discord.Rest | |||
| { | |||
| internal static class InteractionHelper | |||
| { | |||
| #region InteractionHelper | |||
| public static Task DeleteAllGuildCommandsAsync(BaseDiscordClient client, ulong guildId, RequestOptions options = null) | |||
| { | |||
| return client.ApiClient.BulkOverwriteGuildApplicationCommands(guildId, new CreateApplicationCommandParams[0], options); | |||
| @@ -42,8 +43,9 @@ namespace Discord.Rest | |||
| RestFollowupMessage entity = RestFollowupMessage.Create(client, model, token, channel); | |||
| return entity; | |||
| } | |||
| #endregion | |||
| // Global commands | |||
| #region Global commands | |||
| public static async Task<RestGlobalCommand> GetGlobalCommandAsync(BaseDiscordClient client, ulong id, | |||
| RequestOptions options = null) | |||
| { | |||
| @@ -236,8 +238,9 @@ namespace Discord.Rest | |||
| await client.ApiClient.DeleteGlobalApplicationCommandAsync(command.Id, options).ConfigureAwait(false); | |||
| } | |||
| #endregion | |||
| // Guild Commands | |||
| #region Guild Commands | |||
| public static Task<ApplicationCommand> CreateGuildCommand<TArg>(BaseDiscordClient client, ulong guildId, | |||
| Action<TArg> func, RequestOptions options) where TArg : ApplicationCommandProperties | |||
| { | |||
| @@ -324,8 +327,9 @@ namespace Discord.Rest | |||
| return DeleteGlobalCommand(client, command, options); | |||
| } | |||
| } | |||
| #endregion | |||
| // Responses | |||
| #region Responses | |||
| public static async Task<Discord.API.Message> ModifyFollowupMessage(BaseDiscordClient client, RestFollowupMessage message, Action<MessageProperties> func, | |||
| RequestOptions options = null) | |||
| { | |||
| @@ -412,8 +416,9 @@ namespace Discord.Rest | |||
| public static async Task DeletedInteractionResponse(BaseDiscordClient client, RestInteractionMessage message, RequestOptions options = null) | |||
| => await client.ApiClient.DeleteInteractionFollowupMessage(message.Id, message.Token, options); | |||
| #endregion | |||
| // Guild permissions | |||
| #region Guild permissions | |||
| public static async Task<IReadOnlyCollection<GuildApplicationCommandPermission>> GetGuildCommandPermissionsAsync(BaseDiscordClient client, | |||
| ulong guildId, RequestOptions options) | |||
| { | |||
| @@ -506,5 +511,6 @@ namespace Discord.Rest | |||
| x => new GuildApplicationCommandPermission(x.Id, x.ApplicationId, x.GuildId, x.Permissions.Select( | |||
| y => new ApplicationCommandPermission(y.Id, y.Type, y.Permission)).ToArray())).ToArray(); | |||
| } | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -13,6 +13,7 @@ namespace Discord.Rest | |||
| /// </summary> | |||
| public class RestApplicationCommandOption : IApplicationCommandOption | |||
| { | |||
| #region RestApplicationCommandOption | |||
| /// <inheritdoc/> | |||
| public ApplicationCommandOptionType Type { get; private set; } | |||
| @@ -67,11 +68,13 @@ namespace Discord.Rest | |||
| ? model.Choices.Value.Select(x => new RestApplicationCommandChoice(x)).ToImmutableArray() | |||
| : null; | |||
| } | |||
| #endregion | |||
| //IApplicationCommandOption | |||
| #region IApplicationCommandOption | |||
| IReadOnlyCollection<IApplicationCommandOption> IApplicationCommandOption.Options | |||
| => Options; | |||
| IReadOnlyCollection<IApplicationCommandOptionChoice> IApplicationCommandOption.Choices | |||
| => Choices; | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -11,6 +11,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestRole : RestEntity<ulong>, IRole | |||
| { | |||
| #region RestRole | |||
| internal IGuild Guild { get; } | |||
| /// <inheritdoc /> | |||
| public Color Color { get; private set; } | |||
| @@ -64,7 +65,7 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| public async Task ModifyAsync(Action<RoleProperties> func, RequestOptions options = null) | |||
| { | |||
| { | |||
| var model = await RoleHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | |||
| Update(model); | |||
| } | |||
| @@ -83,8 +84,9 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public override string ToString() => Name; | |||
| private string DebuggerDisplay => $"{Name} ({Id})"; | |||
| #endregion | |||
| //IRole | |||
| #region IRole | |||
| /// <inheritdoc /> | |||
| IGuild IRole.Guild | |||
| { | |||
| @@ -7,7 +7,7 @@ namespace Discord.Rest | |||
| { | |||
| internal static class RoleHelper | |||
| { | |||
| //General | |||
| #region General | |||
| public static async Task DeleteAsync(IRole role, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -36,5 +36,6 @@ namespace Discord.Rest | |||
| } | |||
| return model; | |||
| } | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -14,6 +14,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestGuildUser : RestUser, IGuildUser | |||
| { | |||
| #region RestGuildUser | |||
| private long? _premiumSinceTicks; | |||
| private long? _joinedAtTicks; | |||
| private ImmutableArray<ulong> _roleIds; | |||
| @@ -155,8 +156,9 @@ namespace Discord.Rest | |||
| var guildPerms = GuildPermissions; | |||
| return new ChannelPermissions(Permissions.ResolveChannel(Guild, this, channel, guildPerms.RawValue)); | |||
| } | |||
| #endregion | |||
| //IGuildUser | |||
| #region IGuildUser | |||
| /// <inheritdoc /> | |||
| IGuild IGuildUser.Guild | |||
| { | |||
| @@ -167,8 +169,9 @@ namespace Discord.Rest | |||
| throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | |||
| } | |||
| } | |||
| #endregion | |||
| //IVoiceState | |||
| #region IVoiceState | |||
| /// <inheritdoc /> | |||
| bool IVoiceState.IsSelfDeafened => false; | |||
| /// <inheritdoc /> | |||
| @@ -183,5 +186,6 @@ namespace Discord.Rest | |||
| bool IVoiceState.IsStreaming => false; | |||
| /// <inheritdoc /> | |||
| DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -13,6 +13,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestUser : RestEntity<ulong>, IUser, IUpdateable | |||
| { | |||
| #region RestUser | |||
| /// <inheritdoc /> | |||
| public bool IsBot { get; private set; } | |||
| /// <inheritdoc /> | |||
| @@ -116,10 +117,12 @@ namespace Discord.Rest | |||
| /// </returns> | |||
| public override string ToString() => $"{Username}#{Discriminator}"; | |||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; | |||
| #endregion | |||
| //IUser | |||
| #region IUser | |||
| /// <inheritdoc /> | |||
| async Task<IDMChannel> IUser.CreateDMChannelAsync(RequestOptions options) | |||
| => await CreateDMChannelAsync(options).ConfigureAwait(false); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestWebhookUser : RestUser, IWebhookUser | |||
| { | |||
| #region RestWebhookUser | |||
| /// <inheritdoc /> | |||
| public ulong WebhookId { get; } | |||
| internal IGuild Guild { get; } | |||
| @@ -33,8 +34,9 @@ namespace Discord.Rest | |||
| entity.Update(model); | |||
| return entity; | |||
| } | |||
| #endregion | |||
| //IGuildUser | |||
| #region IGuildUser | |||
| /// <inheritdoc /> | |||
| IGuild IGuildUser.Guild | |||
| { | |||
| @@ -91,8 +93,9 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||
| #endregion | |||
| //IVoiceState | |||
| #region IVoiceState | |||
| /// <inheritdoc /> | |||
| bool IVoiceState.IsDeafened => false; | |||
| /// <inheritdoc /> | |||
| @@ -111,5 +114,6 @@ namespace Discord.Rest | |||
| bool IVoiceState.IsStreaming => false; | |||
| /// <inheritdoc /> | |||
| DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ namespace Discord.Rest | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class RestWebhook : RestEntity<ulong>, IWebhook, IUpdateable | |||
| { | |||
| #region RestWebhook | |||
| internal IGuild Guild { get; private set; } | |||
| internal ITextChannel Channel { get; private set; } | |||
| @@ -95,8 +96,9 @@ namespace Discord.Rest | |||
| public override string ToString() => $"Webhook: {Name}:{Id}"; | |||
| private string DebuggerDisplay => $"Webhook: {Name} ({Id})"; | |||
| #endregion | |||
| //IWebhook | |||
| #region IWebhook | |||
| /// <inheritdoc /> | |||
| IGuild IWebhook.Guild | |||
| => Guild ?? throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | |||
| @@ -106,5 +108,6 @@ namespace Discord.Rest | |||
| /// <inheritdoc /> | |||
| Task IWebhook.ModifyAsync(Action<WebhookProperties> func, RequestOptions options) | |||
| => ModifyAsync(func, options); | |||
| #endregion | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ namespace Discord.Net.Converters | |||
| { | |||
| internal class DiscordContractResolver : DefaultContractResolver | |||
| { | |||
| #region DiscordContractResolver | |||
| private static readonly TypeInfo _ienumerable = typeof(IEnumerable<ulong[]>).GetTypeInfo(); | |||
| private static readonly MethodInfo _shouldSerialize = typeof(DiscordContractResolver).GetTypeInfo().GetDeclaredMethod("ShouldSerialize"); | |||
| @@ -57,8 +58,9 @@ namespace Discord.Net.Converters | |||
| else if (genericType == typeof(EntityOrId<>)) | |||
| return MakeGenericConverter(property, propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0], depth); | |||
| } | |||
| #endregion | |||
| //Primitives | |||
| #region Primitives | |||
| bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null; | |||
| if (!hasInt53) | |||
| { | |||
| @@ -107,5 +109,6 @@ namespace Discord.Net.Converters | |||
| var innerConverter = GetConverter(property, propInfo, innerType, depth + 1); | |||
| return genericType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; | |||
| } | |||
| #endregion | |||
| } | |||
| } | |||