Skip to content

Commit

Permalink
[Feature] Guild Onboarding support (#2616)
Browse files Browse the repository at this point in the history
* api models

* moar models

* complete models

* modelsss

* forgot to push

* oh lol forgot this too

* api & rest guild method

* revert VS being VS & formatting to file scoped namespace

* socket entities

* yup

* fix xml doc

* changes
  • Loading branch information
Misha-133 authored Apr 14, 2023
1 parent 84431de commit 3a8f76c
Show file tree
Hide file tree
Showing 18 changed files with 574 additions and 3 deletions.
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

0 comments on commit 3a8f76c

Please sign in to comment.