[Feature] add missing invite guild properties & welcome screen support (#2510)

* 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>
This commit is contained in:
Misha133
2022-12-25 17:40:05 +03:00
committed by GitHub
parent 48fb1b5df4
commit 7c535b952a
16 changed files with 526 additions and 14 deletions

View File

@@ -83,5 +83,8 @@ namespace Discord.API
public Sticker[] Stickers { get; set; }
[JsonProperty("premium_progress_bar_enabled")]
public Optional<bool> IsBoostProgressBarEnabled { get; set; }
[JsonProperty("welcome_screen")]
public Optional<WelcomeScreen> WelcomeScreen { get; set; }
}
}

View File

@@ -6,9 +6,41 @@ namespace Discord.API
{
[JsonProperty("id")]
public ulong Id { get; set; }
[JsonProperty("name")]
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; }
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -2097,6 +2097,35 @@ namespace Discord.API
#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
public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null)
{

View File

@@ -20,7 +20,8 @@ namespace Discord.Rest
public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client,
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();
func(args);
@@ -141,7 +142,7 @@ namespace Discord.Rest
};
var mebibyte = Math.Pow(2, 20);
return (ulong) (tierFactor * mebibyte);
return (ulong)(tierFactor * mebibyte);
}
#endregion
@@ -232,7 +233,8 @@ namespace Discord.Rest
public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client,
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();
func?.Invoke(props);
@@ -262,7 +264,8 @@ namespace Discord.Rest
public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client,
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();
func?.Invoke(props);
@@ -318,7 +321,8 @@ namespace Discord.Rest
public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client,
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();
func?.Invoke(props);
@@ -448,11 +452,13 @@ namespace Discord.Rest
RequestOptions options)
{
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);
inviteModel.Uses = vanityModel.Uses;
return RestInviteMetadata.Create(client, guild, null, inviteModel);
}
#endregion
#region Roles
@@ -460,7 +466,8 @@ namespace Discord.Rest
public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client,
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
{
@@ -676,7 +683,8 @@ namespace Discord.Rest
public static async Task<GuildEmote> ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action<EmoteProperties> func,
RequestOptions options)
{
if (func == null) throw new ArgumentNullException(paramName: nameof(func));
if (func == null)
throw new ArgumentNullException(paramName: nameof(func));
var props = new EmoteProperties();
func(props);
@@ -867,7 +875,7 @@ namespace Discord.Rest
{
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.Cancelled when guildEvent.Status != GuildScheduledEventStatus.Scheduled:
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
};
if(args.Location.IsSpecified)
if (args.Location.IsSpecified)
{
apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata()
{
@@ -949,7 +957,7 @@ namespace Discord.Rest
Image? bannerImage = null,
RequestOptions options = null)
{
if(location != null)
if (location != null)
{
Preconditions.AtMost(location.Length, 100, nameof(location));
}
@@ -985,7 +993,7 @@ namespace Discord.Rest
Image = bannerImage.HasValue ? bannerImage.Value.ToModel() : Optional<ImageModel>.Unspecified
};
if(location != null)
if (location != null)
{
apiArgs.EntityMetadata = new API.GuildScheduledEventEntityMetadata()
{
@@ -1004,5 +1012,53 @@ namespace Discord.Rest
}
#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
}
}

View File

@@ -1534,6 +1534,15 @@ namespace Discord.Rest
else
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
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Model = Discord.API.Invite;
@@ -27,7 +29,17 @@ namespace Discord.Rest
public IUser TargetUser { get; private set; }
/// <inheritdoc />
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 IGuild Guild { get; }
/// <inheritdoc />
@@ -59,6 +71,32 @@ namespace Discord.Rest
Inviter = model.Inviter.IsSpecified ? RestUser.Create(Discord, model.Inviter.Value) : null;
TargetUser = model.TargetUser.IsSpecified ? RestUser.Create(Discord, model.TargetUser.Value) : null;
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 />