* added models * working getter for welcome screen * <see langword="null"/> * more changes * modify welcome screen support * fix some typos & remove `using` added by VS * Working-ish state * Resolve some reviews * change access modifier * forgot to add docs * revert to InviteGuild & extend it * resolve some reviews * Apply suggestions from code review Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com> Co-authored-by: Cenk Ergen <57065323+Cenngo@users.noreply.github.com> Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>pull/2548/head
| @@ -1251,5 +1251,21 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task<IReadOnlyCollection<IApplicationCommand>> BulkOverwriteApplicationCommandsAsync(ApplicationCommandProperties[] properties, | Task<IReadOnlyCollection<IApplicationCommand>> BulkOverwriteApplicationCommandsAsync(ApplicationCommandProperties[] properties, | ||||
| RequestOptions options = null); | RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Gets the welcome screen of the guild. Returns <see langword="null"/> if the welcome channel is not set. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous creation operation. The task result contains a <see cref="WelcomeScreen"/>. | |||||
| /// </returns> | |||||
| Task<WelcomeScreen> GetWelcomeScreenAsync(RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Modifies the welcome screen of the guild. Returns <see langword="null"/> if welcome screen is removed. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous creation operation. The task result contains a <see cref="WelcomeScreen"/>. | |||||
| /// </returns> | |||||
| Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,24 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| namespace Discord; | |||||
| public class WelcomeScreen | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the server description shown in the welcome screen. <see langword="null"/> if not set. | |||||
| /// </summary> | |||||
| public string Description { get; } | |||||
| /// <summary> | |||||
| /// Gets the channels shown in the welcome screen, up to 5 channels. | |||||
| /// </summary> | |||||
| public IReadOnlyCollection<WelcomeScreenChannel> Channels { get; } | |||||
| internal WelcomeScreen(string description, IReadOnlyCollection<WelcomeScreenChannel> channels) | |||||
| { | |||||
| Description = description; | |||||
| Channels = channels.ToImmutableArray(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,41 @@ | |||||
| using System; | |||||
| namespace Discord; | |||||
| public class WelcomeScreenChannel : ISnowflakeEntity | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the channel's id. | |||||
| /// </summary> | |||||
| public ulong Id { get; } | |||||
| /// <summary> | |||||
| /// Gets the description shown for the channel. | |||||
| /// </summary> | |||||
| public string Description { get; } | |||||
| /// <summary> | |||||
| /// Gets the emoji for this channel. <see cref="Emoji"/> if it is unicode emoji, <see cref="Emote"/> if it is a custom one and <see langword="null"/> if none is set. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// If the emoji is <see cref="Emote"/> only the <see cref="Emote.Id"/> will be populated. | |||||
| /// Use <see cref="IGuild.GetEmoteAsync"/> to get the emoji. | |||||
| /// </remarks> | |||||
| public IEmote Emoji { get; } | |||||
| /// <inheritdoc/> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | |||||
| internal WelcomeScreenChannel(ulong id, string description, string emojiName = null, ulong? emoteId = null) | |||||
| { | |||||
| Id = id; | |||||
| Description = description; | |||||
| if (emoteId.HasValue && emoteId.Value != 0) | |||||
| Emoji = new Emote(emoteId.Value, emojiName, false); | |||||
| else if (emojiName != null) | |||||
| Emoji = new Emoji(emojiName); | |||||
| else | |||||
| Emoji = null; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,54 @@ | |||||
| using System; | |||||
| using System.Xml.Linq; | |||||
| namespace Discord; | |||||
| public class WelcomeScreenChannelProperties : ISnowflakeEntity | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets or sets the channel's id. | |||||
| /// </summary> | |||||
| public ulong Id { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the description shown for the channel. | |||||
| /// </summary> | |||||
| public string Description { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the emoji for this channel. <see cref="Emoji"/> if it is unicode emoji, <see cref="Emote"/> if it is a custom one and <see langword="null"/> if none is set. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// If the emoji is <see cref="Emote"/> only the <see cref="Emote.Id"/> will be populated. | |||||
| /// Use <see cref="IGuild.GetEmoteAsync"/> to get the emoji. | |||||
| /// </remarks> | |||||
| public IEmote Emoji { get; set; } | |||||
| /// <inheritdoc/> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | |||||
| /// <summary> | |||||
| /// Initializes a new instance of <see cref="WelcomeScreenChannelProperties"/>. | |||||
| /// </summary> | |||||
| /// <param name="id">Id if a channel.</param> | |||||
| /// <param name="description">Description for the channel in the welcome screen.</param> | |||||
| /// <param name="emoji">The emoji for the channel in the welcome screen.</param> | |||||
| public WelcomeScreenChannelProperties(ulong id, string description, IEmote emoji = null) | |||||
| { | |||||
| Id = id; | |||||
| Description = description; | |||||
| Emoji = emoji; | |||||
| } | |||||
| /// <summary> | |||||
| /// Initializes a new instance of <see cref="WelcomeScreenChannelProperties"/>. | |||||
| /// </summary> | |||||
| public WelcomeScreenChannelProperties() { } | |||||
| /// <summary> | |||||
| /// Initializes a new instance of <see cref="WelcomeScreenChannelProperties"/>. | |||||
| /// </summary> | |||||
| /// <param name="channel">A welcome screen channel to modify.</param> | |||||
| /// <returns>A new instance of <see cref="WelcomeScreenChannelProperties"/>.</returns> | |||||
| public static WelcomeScreenChannelProperties FromWelcomeScreenChannel(WelcomeScreenChannel channel) | |||||
| => new (channel.Id, channel.Description, channel.Emoji); | |||||
| } | |||||
| @@ -60,6 +60,7 @@ namespace Discord | |||||
| /// A guild object representing the guild that the invite points to. | /// A guild object representing the guild that the invite points to. | ||||
| /// </returns> | /// </returns> | ||||
| IGuild Guild { get; } | IGuild Guild { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the ID of the guild this invite is linked to. | /// Gets the ID of the guild this invite is linked to. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -0,0 +1,156 @@ | |||||
| using System; | |||||
| namespace Discord; | |||||
| public class InviteGuild : ISnowflakeEntity | |||||
| { | |||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | |||||
| /// <inheritdoc/> | |||||
| public ulong Id { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the name of this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A string containing the name of this guild. | |||||
| /// </returns> | |||||
| public string Name { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the description for the guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The description for the guild; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string Description { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the ID of this guild's splash image. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// An identifier for the splash image; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string SplashId { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the URL of this guild's splash image. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A URL pointing to the guild's splash image; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); | |||||
| /// <summary> | |||||
| /// Gets the identifier for this guilds banner image. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// An identifier for the banner image; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string BannerId { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the URL of this guild's banner image. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A URL pointing to the guild's banner image; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string BannerUrl => CDN.GetGuildBannerUrl(Id, BannerId, ImageFormat.Auto); | |||||
| /// <summary> | |||||
| /// Gets the features for this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A flags enum containing all the features for the guild. | |||||
| /// </returns> | |||||
| public GuildFeatures Features { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the ID of this guild's icon. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// An identifier for the splash image; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string IconId { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the URL of this guild's icon. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A URL pointing to the guild's icon; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); | |||||
| /// <summary> | |||||
| /// | |||||
| /// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The level of requirements. | |||||
| /// </returns> | |||||
| public VerificationLevel VerificationLevel { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the code for this guild's vanity invite URL. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A string containing the vanity invite code for this guild; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public string VanityURLCode { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the number of premium subscribers of this guild. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This is the number of users who have boosted this guild. | |||||
| /// </remarks> | |||||
| /// <returns> | |||||
| /// The number of premium subscribers of this guild; | |||||
| /// </returns> | |||||
| public int PremiumSubscriptionCount { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the NSFW level of this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The NSFW level of this guild. | |||||
| /// </returns> | |||||
| public NsfwLevel NsfwLevel { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the Welcome Screen of this guild | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The welcome screen of this guild. <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| public WelcomeScreen WelcomeScreen { get; private set; } | |||||
| internal InviteGuild( | |||||
| ulong id, | |||||
| string name, | |||||
| string description, | |||||
| string splashId, | |||||
| string bannerId, | |||||
| GuildFeatures features, | |||||
| string iconId, | |||||
| VerificationLevel verificationLevel, | |||||
| string vanityURLCode, | |||||
| int premiumSubscriptionCount, | |||||
| NsfwLevel nsfwLevel, | |||||
| WelcomeScreen welcomeScreen) | |||||
| { | |||||
| Id = id; | |||||
| Name = name; | |||||
| Description = description; | |||||
| SplashId = splashId; | |||||
| BannerId = bannerId; | |||||
| Features = features; | |||||
| IconId = iconId; | |||||
| VerificationLevel = verificationLevel; | |||||
| VanityURLCode = vanityURLCode; | |||||
| PremiumSubscriptionCount = premiumSubscriptionCount; | |||||
| NsfwLevel = nsfwLevel; | |||||
| WelcomeScreen = welcomeScreen; | |||||
| } | |||||
| } | |||||
| @@ -83,5 +83,8 @@ namespace Discord.API | |||||
| public Sticker[] Stickers { get; set; } | public Sticker[] Stickers { get; set; } | ||||
| [JsonProperty("premium_progress_bar_enabled")] | [JsonProperty("premium_progress_bar_enabled")] | ||||
| public Optional<bool> IsBoostProgressBarEnabled { get; set; } | public Optional<bool> IsBoostProgressBarEnabled { get; set; } | ||||
| [JsonProperty("welcome_screen")] | |||||
| public Optional<WelcomeScreen> WelcomeScreen { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,9 +6,41 @@ namespace Discord.API | |||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("splash_hash")] | |||||
| public string SplashHash { get; set; } | |||||
| [JsonProperty("splash")] | |||||
| public Optional<string> Splash { get; set; } | |||||
| [JsonProperty("banner")] | |||||
| public Optional<string> BannerHash { get; set; } | |||||
| [JsonProperty("description")] | |||||
| public Optional<string> Description { get; set; } | |||||
| [JsonProperty("icon")] | |||||
| public Optional<string> IconHash { get; set; } | |||||
| [JsonProperty("features")] | |||||
| public GuildFeatures Features { get; set; } | |||||
| [JsonProperty("verification_level")] | |||||
| public VerificationLevel VerificationLevel { get; set; } | |||||
| [JsonProperty("vanity_url_code")] | |||||
| public Optional<string> VanityUrlCode { get; set; } | |||||
| [JsonProperty("premium_subscription_count")] | |||||
| public Optional<int> PremiumSubscriptionCount { get; set; } | |||||
| [JsonProperty("nsfw")] | |||||
| public Optional<bool?> Nsfw { get; set; } | |||||
| [JsonProperty("nsfw_level")] | |||||
| public NsfwLevel NsfwLevel { get; set; } | |||||
| [JsonProperty("welcome_screen")] | |||||
| public Optional<WelcomeScreen> WelcomeScreen { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,12 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API; | |||||
| internal class WelcomeScreen | |||||
| { | |||||
| [JsonProperty("description")] | |||||
| public Optional<string> Description { get; set; } | |||||
| [JsonProperty("welcome_channels")] | |||||
| public WelcomeScreenChannel[] WelcomeChannels { get; set; } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API; | |||||
| internal class WelcomeScreenChannel | |||||
| { | |||||
| [JsonProperty("channel_id")] | |||||
| public ulong ChannelId { get; set; } | |||||
| [JsonProperty("description")] | |||||
| public string Description { get; set; } | |||||
| [JsonProperty("emoji_id")] | |||||
| public Optional<ulong?> EmojiId { get; set; } | |||||
| [JsonProperty("emoji_name")] | |||||
| public Optional<string> EmojiName{ get; set; } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rest; | |||||
| internal class ModifyGuildWelcomeScreenParams | |||||
| { | |||||
| [JsonProperty("enabled")] | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| [JsonProperty("welcome_channels")] | |||||
| public Optional<WelcomeScreenChannel[]> WelcomeChannels { get; set; } | |||||
| [JsonProperty("description")] | |||||
| public Optional<string> Description { get; set; } | |||||
| } | |||||
| @@ -2097,6 +2097,35 @@ namespace Discord.API | |||||
| #endregion | #endregion | ||||
| #region Guild Welcome Screen | |||||
| public async Task<WelcomeScreen> GetGuildWelcomeScreenAsync(ulong guildId, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | |||||
| { | |||||
| var ids = new BucketIds(guildId: guildId); | |||||
| return await SendAsync<WelcomeScreen>("GET", () => $"guilds/{guildId}/welcome-screen", ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } | |||||
| } | |||||
| public async Task<WelcomeScreen> ModifyGuildWelcomeScreenAsync(ModifyGuildWelcomeScreenParams args, ulong guildId, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| Preconditions.NotNull(args, nameof(args)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(guildId: guildId); | |||||
| return await SendJsonAsync<WelcomeScreen>("PATCH", () => $"guilds/{guildId}/welcome-screen", args, ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| #endregion | |||||
| #region Users | #region Users | ||||
| public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -20,7 +20,8 @@ namespace Discord.Rest | |||||
| public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | ||||
| Action<GuildProperties> func, RequestOptions options) | Action<GuildProperties> func, RequestOptions options) | ||||
| { | { | ||||
| if (func == null) throw new ArgumentNullException(nameof(func)); | |||||
| if (func == null) | |||||
| throw new ArgumentNullException(nameof(func)); | |||||
| var args = new GuildProperties(); | var args = new GuildProperties(); | ||||
| func(args); | func(args); | ||||
| @@ -141,7 +142,7 @@ namespace Discord.Rest | |||||
| }; | }; | ||||
| var mebibyte = Math.Pow(2, 20); | var mebibyte = Math.Pow(2, 20); | ||||
| return (ulong) (tierFactor * mebibyte); | |||||
| return (ulong)(tierFactor * mebibyte); | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| @@ -232,7 +233,8 @@ namespace Discord.Rest | |||||
| public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options, Action<TextChannelProperties> func = null) | string name, RequestOptions options, Action<TextChannelProperties> func = null) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(paramName: nameof(name)); | |||||
| if (name == null) | |||||
| throw new ArgumentNullException(paramName: nameof(name)); | |||||
| var props = new TextChannelProperties(); | var props = new TextChannelProperties(); | ||||
| func?.Invoke(props); | func?.Invoke(props); | ||||
| @@ -262,7 +264,8 @@ namespace Discord.Rest | |||||
| public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options, Action<VoiceChannelProperties> func = null) | string name, RequestOptions options, Action<VoiceChannelProperties> func = null) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(paramName: nameof(name)); | |||||
| if (name == null) | |||||
| throw new ArgumentNullException(paramName: nameof(name)); | |||||
| var props = new VoiceChannelProperties(); | var props = new VoiceChannelProperties(); | ||||
| func?.Invoke(props); | func?.Invoke(props); | ||||
| @@ -318,7 +321,8 @@ namespace Discord.Rest | |||||
| public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options, Action<GuildChannelProperties> func = null) | string name, RequestOptions options, Action<GuildChannelProperties> func = null) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(paramName: nameof(name)); | |||||
| if (name == null) | |||||
| throw new ArgumentNullException(paramName: nameof(name)); | |||||
| var props = new GuildChannelProperties(); | var props = new GuildChannelProperties(); | ||||
| func?.Invoke(props); | func?.Invoke(props); | ||||
| @@ -448,11 +452,13 @@ namespace Discord.Rest | |||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| var vanityModel = await client.ApiClient.GetVanityInviteAsync(guild.Id, options).ConfigureAwait(false); | var vanityModel = await client.ApiClient.GetVanityInviteAsync(guild.Id, options).ConfigureAwait(false); | ||||
| if (vanityModel == null) throw new InvalidOperationException("This guild does not have a vanity URL."); | |||||
| if (vanityModel == null) | |||||
| throw new InvalidOperationException("This guild does not have a vanity URL."); | |||||
| var inviteModel = await client.ApiClient.GetInviteAsync(vanityModel.Code, options).ConfigureAwait(false); | var inviteModel = await client.ApiClient.GetInviteAsync(vanityModel.Code, options).ConfigureAwait(false); | ||||
| inviteModel.Uses = vanityModel.Uses; | inviteModel.Uses = vanityModel.Uses; | ||||
| return RestInviteMetadata.Create(client, guild, null, inviteModel); | return RestInviteMetadata.Create(client, guild, null, inviteModel); | ||||
| } | } | ||||
| #endregion | #endregion | ||||
| #region Roles | #region Roles | ||||
| @@ -460,7 +466,8 @@ namespace Discord.Rest | |||||
| public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, GuildPermissions? permissions, Color? color, bool isHoisted, bool isMentionable, RequestOptions options) | string name, GuildPermissions? permissions, Color? color, bool isHoisted, bool isMentionable, RequestOptions options) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(paramName: nameof(name)); | |||||
| if (name == null) | |||||
| throw new ArgumentNullException(paramName: nameof(name)); | |||||
| var createGuildRoleParams = new API.Rest.ModifyGuildRoleParams | var createGuildRoleParams = new API.Rest.ModifyGuildRoleParams | ||||
| { | { | ||||
| @@ -676,7 +683,8 @@ namespace Discord.Rest | |||||
| public static async Task<GuildEmote> ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action<EmoteProperties> func, | public static async Task<GuildEmote> ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action<EmoteProperties> func, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| if (func == null) throw new ArgumentNullException(paramName: nameof(func)); | |||||
| if (func == null) | |||||
| throw new ArgumentNullException(paramName: nameof(func)); | |||||
| var props = new EmoteProperties(); | var props = new EmoteProperties(); | ||||
| func(props); | func(props); | ||||
| @@ -867,7 +875,7 @@ namespace Discord.Rest | |||||
| { | { | ||||
| switch (args.Status.Value) | switch (args.Status.Value) | ||||
| { | { | ||||
| case GuildScheduledEventStatus.Active when guildEvent.Status != GuildScheduledEventStatus.Scheduled: | |||||
| case GuildScheduledEventStatus.Active when guildEvent.Status != GuildScheduledEventStatus.Scheduled: | |||||
| case GuildScheduledEventStatus.Completed when guildEvent.Status != GuildScheduledEventStatus.Active: | case GuildScheduledEventStatus.Completed when guildEvent.Status != GuildScheduledEventStatus.Active: | ||||
| case GuildScheduledEventStatus.Cancelled when guildEvent.Status != GuildScheduledEventStatus.Scheduled: | case GuildScheduledEventStatus.Cancelled when guildEvent.Status != GuildScheduledEventStatus.Scheduled: | ||||
| throw new ArgumentException($"Cannot set event to {args.Status.Value} when events status is {guildEvent.Status}"); | throw new ArgumentException($"Cannot set event to {args.Status.Value} when events status is {guildEvent.Status}"); | ||||
| @@ -909,7 +917,7 @@ namespace Discord.Rest | |||||
| : Optional<ImageModel?>.Unspecified | : Optional<ImageModel?>.Unspecified | ||||
| }; | }; | ||||
| if(args.Location.IsSpecified) | |||||
| if (args.Location.IsSpecified) | |||||
| { | { | ||||
| apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata() | apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata() | ||||
| { | { | ||||
| @@ -949,7 +957,7 @@ namespace Discord.Rest | |||||
| Image? bannerImage = null, | Image? bannerImage = null, | ||||
| RequestOptions options = null) | RequestOptions options = null) | ||||
| { | { | ||||
| if(location != null) | |||||
| if (location != null) | |||||
| { | { | ||||
| Preconditions.AtMost(location.Length, 100, nameof(location)); | Preconditions.AtMost(location.Length, 100, nameof(location)); | ||||
| } | } | ||||
| @@ -985,7 +993,7 @@ namespace Discord.Rest | |||||
| Image = bannerImage.HasValue ? bannerImage.Value.ToModel() : Optional<ImageModel>.Unspecified | Image = bannerImage.HasValue ? bannerImage.Value.ToModel() : Optional<ImageModel>.Unspecified | ||||
| }; | }; | ||||
| if(location != null) | |||||
| if (location != null) | |||||
| { | { | ||||
| apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata() | apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata() | ||||
| { | { | ||||
| @@ -1004,5 +1012,53 @@ namespace Discord.Rest | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| #region Welcome Screen | |||||
| public static async Task<WelcomeScreen> GetWelcomeScreenAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| var model = await client.ApiClient.GetGuildWelcomeScreenAsync(guild.Id, options); | |||||
| if (model.WelcomeChannels.Length == 0) | |||||
| return null; | |||||
| return new WelcomeScreen(model.Description.GetValueOrDefault(null), model.WelcomeChannels.Select( | |||||
| x => new WelcomeScreenChannel( | |||||
| x.ChannelId, x.Description, | |||||
| x.EmojiName.GetValueOrDefault(null), | |||||
| x.EmojiId.GetValueOrDefault(0))).ToList()); | |||||
| } | |||||
| public static async Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, string description, WelcomeScreenChannelProperties[] channels, IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| if (!guild.Features.HasFeature(GuildFeature.Community)) | |||||
| throw new InvalidOperationException("Cannot update welcome screen in a non-community guild."); | |||||
| var args = new ModifyGuildWelcomeScreenParams | |||||
| { | |||||
| Enabled = enabled, | |||||
| Description = description, | |||||
| WelcomeChannels = channels?.Select(ch => new API.WelcomeScreenChannel | |||||
| { | |||||
| ChannelId = ch.Id, | |||||
| Description = ch.Description, | |||||
| EmojiName = ch.Emoji is Emoji emoj ? emoj.Name : Optional<string>.Unspecified, | |||||
| EmojiId = ch.Emoji is Emote emote ? emote.Id : Optional<ulong?>.Unspecified | |||||
| }).ToArray() | |||||
| }; | |||||
| var model = await client.ApiClient.ModifyGuildWelcomeScreenAsync(args, guild.Id, options); | |||||
| if(model.WelcomeChannels.Length == 0) | |||||
| return null; | |||||
| return new WelcomeScreen(model.Description.GetValueOrDefault(null), model.WelcomeChannels.Select( | |||||
| x => new WelcomeScreenChannel( | |||||
| x.ChannelId, x.Description, | |||||
| x.EmojiName.GetValueOrDefault(null), | |||||
| x.EmojiId.GetValueOrDefault(0))).ToList()); | |||||
| } | |||||
| #endregion | |||||
| } | } | ||||
| } | } | ||||
| @@ -1534,6 +1534,15 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc/> | |||||
| public Task<WelcomeScreen> GetWelcomeScreenAsync(RequestOptions options = null) | |||||
| => GuildHelper.GetWelcomeScreenAsync(this, Discord, options); | |||||
| /// <inheritdoc/> | |||||
| public Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null) | |||||
| => GuildHelper.ModifyWelcomeScreenAsync(enabled, description, channels, this, Discord, options); | |||||
| #endregion | #endregion | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,5 +1,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Immutable; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Invite; | using Model = Discord.API.Invite; | ||||
| @@ -27,7 +29,17 @@ namespace Discord.Rest | |||||
| public IUser TargetUser { get; private set; } | public IUser TargetUser { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public TargetUserType TargetUserType { get; private set; } | public TargetUserType TargetUserType { get; private set; } | ||||
| /// <summary> | |||||
| /// Gets the guild this invite is linked to. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A partial guild object representing the guild that the invite points to. | |||||
| /// </returns> | |||||
| public InviteGuild InviteGuild { get; private set; } | |||||
| internal IChannel Channel { get; } | internal IChannel Channel { get; } | ||||
| internal IGuild Guild { get; } | internal IGuild Guild { get; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -59,6 +71,32 @@ namespace Discord.Rest | |||||
| Inviter = model.Inviter.IsSpecified ? RestUser.Create(Discord, model.Inviter.Value) : null; | Inviter = model.Inviter.IsSpecified ? RestUser.Create(Discord, model.Inviter.Value) : null; | ||||
| TargetUser = model.TargetUser.IsSpecified ? RestUser.Create(Discord, model.TargetUser.Value) : null; | TargetUser = model.TargetUser.IsSpecified ? RestUser.Create(Discord, model.TargetUser.Value) : null; | ||||
| TargetUserType = model.TargetUserType.IsSpecified ? model.TargetUserType.Value : TargetUserType.Undefined; | TargetUserType = model.TargetUserType.IsSpecified ? model.TargetUserType.Value : TargetUserType.Undefined; | ||||
| if (model.Guild.IsSpecified) | |||||
| { | |||||
| InviteGuild = new InviteGuild | |||||
| (model.Guild.Value.Id, | |||||
| model.Guild.Value.Name, | |||||
| model.Guild.Value.Description.IsSpecified ? model.Guild.Value.Description.Value : null, | |||||
| model.Guild.Value.Splash.IsSpecified ? model.Guild.Value.Splash.Value : null, | |||||
| model.Guild.Value.BannerHash.IsSpecified ? model.Guild.Value.BannerHash.Value : null, | |||||
| model.Guild.Value.Features, | |||||
| model.Guild.Value.IconHash.IsSpecified ? model.Guild.Value.IconHash.Value : null, | |||||
| model.Guild.Value.VerificationLevel, | |||||
| model.Guild.Value.VanityUrlCode.IsSpecified ? model.Guild.Value.VanityUrlCode.Value : null, | |||||
| model.Guild.Value.PremiumSubscriptionCount.GetValueOrDefault(0), | |||||
| model.Guild.Value.NsfwLevel, | |||||
| model.Guild.Value.WelcomeScreen.IsSpecified | |||||
| ? new WelcomeScreen( | |||||
| model.Guild.Value.WelcomeScreen.Value.Description.IsSpecified ? model.Guild.Value.WelcomeScreen.Value.Description.Value : null, | |||||
| model.Guild.Value.WelcomeScreen.Value.WelcomeChannels.Select(ch => | |||||
| new WelcomeScreenChannel( | |||||
| ch.ChannelId, | |||||
| ch.Description, | |||||
| ch.EmojiName.IsSpecified ? ch.EmojiName.Value : null, | |||||
| ch.EmojiId.IsSpecified ? ch.EmojiId.Value : null)).ToImmutableArray()) | |||||
| : null); | |||||
| } | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -2039,6 +2039,14 @@ namespace Discord.WebSocket | |||||
| RequestOptions options) | RequestOptions options) | ||||
| => await BulkOverwriteApplicationCommandAsync(properties, options); | => await BulkOverwriteApplicationCommandAsync(properties, options); | ||||
| /// <inheritdoc/> | |||||
| public Task<WelcomeScreen> GetWelcomeScreenAsync(RequestOptions options = null) | |||||
| => GuildHelper.GetWelcomeScreenAsync(this, Discord, options); | |||||
| /// <inheritdoc/> | |||||
| public Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null) | |||||
| => GuildHelper.ModifyWelcomeScreenAsync(enabled, description, channels, this, Discord, options); | |||||
| void IDisposable.Dispose() | void IDisposable.Dispose() | ||||
| { | { | ||||
| DisconnectAudioAsync().GetAwaiter().GetResult(); | DisconnectAudioAsync().GetAwaiter().GetResult(); | ||||