Skip to content

[Feature] Guild Onboarding support #2616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 14, 2023
8 changes: 8 additions & 0 deletions src/Discord.Net.Core/Entities/Guilds/IGuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,5 +1307,13 @@ Task<IReadOnlyCollection<IApplicationCommand>> BulkOverwriteApplicationCommandsA
/// A task that represents the asynchronous creation operation. The task result contains the created <see cref="IAutoModRule"/>.
/// </returns>
Task<IAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null);

/// <summary>
/// Gets the onboarding object configured for the guild.
/// </summary>
/// <returns>
/// A task that represents the asynchronous creation operation. The task result contains the created <see cref="IGuildOnboarding"/>.
/// </returns>
Task<IGuildOnboarding> GetOnboardingAsync(RequestOptions options = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Discord;

/// <summary>
/// Represents the guild onboarding option type.
/// </summary>
public enum GuildOnboardingPromptType
{
/// <summary>
/// The prompt accepts multiple choices.
/// </summary>
MultipleChoice = 0,

/// <summary>
/// The prompt uses a dropdown menu.
/// </summary>
Dropdown = 1,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections.Generic;

namespace Discord;

/// <summary>
/// Represents the guild onboarding flow.
/// </summary>
public interface IGuildOnboarding
{
/// <summary>
/// Gets the ID of the guild this onboarding is part of.
/// </summary>
ulong GuildId { get; }

/// <summary>
/// Gets the guild this onboarding is part of.
/// </summary>
IGuild Guild { get; }

/// <summary>
/// Gets prompts shown during onboarding and in customize community.
/// </summary>
IReadOnlyCollection<IGuildOnboardingPrompt> Prompts { get; }

/// <summary>
/// Gets IDs of channels that members get opted into automatically.
/// </summary>
IReadOnlyCollection<ulong> DefaultChannelIds { get; }

/// <summary>
/// Gets whether onboarding is enabled in the guild.
/// </summary>
bool IsEnabled { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;

namespace Discord;

/// <summary>
/// Represents the guild onboarding prompt.
/// </summary>
public interface IGuildOnboardingPrompt : ISnowflakeEntity
{
/// <summary>
/// Gets options available within the prompt.
/// </summary>
IReadOnlyCollection<IGuildOnboardingPromptOption> Options { get; }

/// <summary>
/// Gets the title of the prompt.
/// </summary>
string Title { get; }

/// <summary>
/// Indicates whether users are limited to selecting one option for the prompt.
/// </summary>
bool IsSingleSelect { get; }

/// <summary>
/// Indicates whether the prompt is required before a user completes the onboarding flow.
/// </summary>
bool IsRequired { get; }

/// <summary>
/// Indicates whether the prompt is present in the onboarding flow.
/// If <see langword="false"/>, the prompt will only appear in the Channels and Roles tab.
/// </summary>
bool IsInOnboarding { get; }

/// <summary>
/// Gets the type of the prompt.
/// </summary>
GuildOnboardingPromptType Type { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections.Generic;

namespace Discord;

/// <summary>
/// Represents the guild onboarding prompt option.
/// </summary>
public interface IGuildOnboardingPromptOption : ISnowflakeEntity
{
/// <summary>
/// Gets IDs of channels a member is added to when the option is selected.
/// </summary>
IReadOnlyCollection<ulong> ChannelIds { get; }

/// <summary>
/// Gets IDs of roles assigned to a member when the option is selected.
/// </summary>
IReadOnlyCollection<ulong> RoleIds { get; }

/// <summary>
/// Gets the emoji of the option. <see langword="null"/> if none is set.
/// </summary>
IEmote Emoji { get; }

/// <summary>
/// Gets the title of the option.
/// </summary>
string Title { get; }

/// <summary>
/// Gets the description of the option. <see langword="null"/> if none is set.
/// </summary>
string Description { get; }
}
18 changes: 18 additions & 0 deletions src/Discord.Net.Rest/API/Common/GuildOnboarding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Newtonsoft.Json;

namespace Discord.API;

internal class GuildOnboarding
{
[JsonProperty("guild_id")]
public ulong GuildId { get; set; }

[JsonProperty("prompts")]
public GuildOnboardingPrompt[] Prompts { get; set; }

[JsonProperty("default_channel_ids")]
public ulong[] DefaultChannelIds { get; set; }

[JsonProperty("enabled")]
public bool Enabled { get; set; }
}
27 changes: 27 additions & 0 deletions src/Discord.Net.Rest/API/Common/GuildOnboardingPrompt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Newtonsoft.Json;

namespace Discord.API;

internal class GuildOnboardingPrompt
{
[JsonProperty("id")]
public ulong Id { get; set; }

[JsonProperty("options")]
public GuildOnboardingPromptOption[] Options { get; set; }

[JsonProperty("title")]
public string Title { get; set; }

[JsonProperty("single_select")]
public bool IsSingleSelect { get; set; }

[JsonProperty("required")]
public bool IsRequired { get; set; }

[JsonProperty("in_onboarding")]
public bool IsInOnboarding { get; set; }

[JsonProperty("type")]
public GuildOnboardingPromptType Type { get; set; }
}
24 changes: 24 additions & 0 deletions src/Discord.Net.Rest/API/Common/GuildOnboardingPromptOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json;

namespace Discord.API;

internal class GuildOnboardingPromptOption
{
[JsonProperty("id")]
public ulong Id { get; set; }

[JsonProperty("channel_ids")]
public ulong[] ChannelIds { get; set; }

[JsonProperty("role_ids")]
public ulong[] RoleIds { get; set; }

[JsonProperty("emoji")]
public Emoji Emoji { get; set; }

[JsonProperty("title")]
public string Title { get; set; }

[JsonProperty("description")]
public Optional<string> Description { get; set; }
}
13 changes: 13 additions & 0 deletions src/Discord.Net.Rest/DiscordRestApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2239,6 +2239,19 @@ public async Task<WelcomeScreen> ModifyGuildWelcomeScreenAsync(ModifyGuildWelcom

#endregion

#region Guild Onboarding

public async Task<GuildOnboarding> GetGuildOnboardingAsync(ulong guildId, RequestOptions options)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));

options = RequestOptions.CreateOrClone(options);

return await SendAsync<GuildOnboarding>("GET", () => $"guilds/{guildId}/onboarding", new BucketIds(guildId: guildId), options: options);
}

#endregion

#region Users
public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null)
{
Expand Down
13 changes: 10 additions & 3 deletions src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public static IAsyncEnumerable<IReadOnlyCollection<RestBan>> GetBansAsync(IGuild
},
start: fromUserId,
count: limit
);
);
}

public static async Task<RestBan> GetBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options)
Expand Down Expand Up @@ -427,7 +427,7 @@ public static async Task<IReadOnlyCollection<RestIntegration>> GetIntegrationsAs
}
public static async Task DeleteIntegrationAsync(IGuild guild, BaseDiscordClient client, ulong id,
RequestOptions options) =>
await client.ApiClient.DeleteIntegrationAsync(guild.Id, id, options).ConfigureAwait(false);
await client.ApiClient.DeleteIntegrationAsync(guild.Id, id, options).ConfigureAwait(false);
#endregion

#region Interactions
Expand Down Expand Up @@ -810,7 +810,7 @@ public static async Task<IReadOnlyCollection<RestUser>> GetEventUsersAsync(BaseD
}

public static IAsyncEnumerable<IReadOnlyCollection<RestUser>> GetEventUsersAsync(BaseDiscordClient client, IGuildScheduledEvent guildEvent,
ulong? fromUserId, int? limit, RequestOptions options)
ulong? fromUserId, int? limit, RequestOptions options)
{
return new PagedAsyncEnumerable<RestUser>(
DiscordConfig.MaxGuildEventUsersPerBatch,
Expand Down Expand Up @@ -1254,5 +1254,12 @@ public static Task<AutoModerationRule> ModifyRuleAsync(BaseDiscordClient client,
public static Task DeleteRuleAsync(BaseDiscordClient client, IAutoModRule rule, RequestOptions options)
=> client.ApiClient.DeleteGuildAutoModRuleAsync(rule.GuildId, rule.Id, options);
#endregion

#region Onboarding

public static async Task<GuildOnboarding> GetGuildOnboardingAsync(IGuild guild, BaseDiscordClient client, RequestOptions options)
=> await client.ApiClient.GetGuildOnboardingAsync(guild.Id, options);

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Model = Discord.API.GuildOnboarding;

namespace Discord.Rest;

/// <inheritdoc />
public class RestGuildOnboarding : IGuildOnboarding
{
/// <inheritdoc />
public ulong GuildId { get; private set; }

/// <inheritdoc cref="IGuildOnboarding.Guild" />
public RestGuild Guild { get; private set; }

/// <inheritdoc />
public IReadOnlyCollection<ulong> DefaultChannelIds { get; private set; }

/// <inheritdoc />
public bool IsEnabled { get; private set; }

/// <inheritdoc cref="IGuildOnboarding.Prompts"/>
public IReadOnlyCollection<RestGuildOnboardingPrompt> Prompts { get; private set; }

internal RestGuildOnboarding(BaseDiscordClient discord, Model model, RestGuild guild = null)
{
GuildId = model.GuildId;
DefaultChannelIds = model.DefaultChannelIds.ToImmutableArray();
IsEnabled = model.Enabled;

Guild = guild;
Prompts = model.Prompts.Select(prompt => new RestGuildOnboardingPrompt(discord, prompt.Id, prompt)).ToImmutableArray();
}

#region IGuildOnboarding

/// <inheritdoc />
IReadOnlyCollection<IGuildOnboardingPrompt> IGuildOnboarding.Prompts => Prompts;

/// <inheritdoc />
IGuild IGuildOnboarding.Guild => Guild;

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Model = Discord.API.GuildOnboardingPrompt;

namespace Discord.Rest;

/// <inheritdoc cref="IGuildOnboardingPrompt"/>
public class RestGuildOnboardingPrompt : RestEntity<ulong>, IGuildOnboardingPrompt
{
/// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);

/// <inheritdoc cref="IGuildOnboardingPrompt.Options"/>
public IReadOnlyCollection<RestGuildOnboardingPromptOption> Options { get; private set; }

/// <inheritdoc />
public string Title { get; private set; }

/// <inheritdoc />
public bool IsSingleSelect { get; private set; }

/// <inheritdoc />
public bool IsRequired { get; private set; }

/// <inheritdoc />
public bool IsInOnboarding { get; private set; }

/// <inheritdoc />
public GuildOnboardingPromptType Type { get; private set; }

internal RestGuildOnboardingPrompt(BaseDiscordClient discord, ulong id, Model model) : base(discord, id)
{
Title = model.Title;
IsSingleSelect = model.IsSingleSelect;
IsInOnboarding = model.IsInOnboarding;
IsRequired = model.IsRequired;
Type = model.Type;

Options = model.Options.Select(option => new RestGuildOnboardingPromptOption(discord, option.Id, option)).ToImmutableArray();
}

#region IGuildOnboardingPrompt

/// <inheritdoc />
IReadOnlyCollection<IGuildOnboardingPromptOption> IGuildOnboardingPrompt.Options => Options;

#endregion
}
Loading