Skip to content
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

[Feature] user apps #2883

Merged
merged 37 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
33575e1
omg it kinda works somehow
Misha-133 Nov 30, 2023
5a16d9c
more things added
Misha-133 Nov 30, 2023
fc33935
a bit of xmldocs
Misha-133 Nov 30, 2023
e215551
added interaction framework support
Misha-133 Nov 30, 2023
0e2cd58
working? IF
Misha-133 Nov 30, 2023
d2f9e2c
more builder stuff
Misha-133 Nov 30, 2023
547b7fc
space
Misha-133 Dec 3, 2023
35634a0
rename attribute to prevent conflict with `ContextType` enum
Misha-133 Dec 3, 2023
4b5a1de
Merge branch 'dev' into feature/user-apps
Misha-133 Jan 19, 2024
c17afd5
context type
Misha-133 Jan 19, 2024
c3c1c78
moar features
Misha-133 Jan 24, 2024
9470124
remove integration types
Misha-133 Jan 24, 2024
cb31565
trigger workflow
Misha-133 Jan 25, 2024
9efe227
modelzzzz
Misha-133 Feb 16, 2024
a18235b
`InteractionContextType`
Misha-133 Feb 22, 2024
e82358b
allow setting custom status with `SetGameAsync`
Misha-133 Feb 22, 2024
76067db
bugzzz
Misha-133 Feb 22, 2024
5455356
app permissions
Misha-133 Feb 22, 2024
8b05d23
message interaction context
Misha-133 Feb 22, 2024
b0147e4
Merge branch 'dev' into feature/user-apps
Misha-133 Feb 22, 2024
3a27dd5
hm
Misha-133 Feb 22, 2024
b9c7212
push for cd
Misha-133 Feb 22, 2024
2d2b85e
Merge branch 'dev' into feature/user-apps
Misha-133 Feb 28, 2024
55c67ed
structs lets goooo
Misha-133 Feb 28, 2024
064873a
whoops forgot to change types
Misha-133 Feb 28, 2024
c5606c7
whoops x2
Misha-133 Feb 28, 2024
80a1007
Merge branch 'discord-net:dev' into feature/user-apps
Misha-133 Mar 12, 2024
ea348cc
tweak some things
Misha-133 Mar 14, 2024
9261033
xmldocs + missing prop + fix enabled in dm
Misha-133 Mar 14, 2024
513dfd3
Merge branch 'dev' into feature/user-apps
Misha-133 Mar 14, 2024
66b3f80
moar validations
Misha-133 Mar 17, 2024
3d47ccc
deprecate a bunch of stuffz
Misha-133 Mar 18, 2024
a1ea5ba
disable moar obsolete warnings
Misha-133 Mar 18, 2024
9cd1c41
Merge branch 'discord-net:dev' into feature/user-apps
Misha-133 Mar 18, 2024
8077927
add IF sample
Misha-133 Mar 18, 2024
311ed32
Apply suggestions from code review
Misha-133 Mar 18, 2024
10dbbb5
Update src/Discord.Net.Rest/Entities/RestApplication.cs
Misha-133 Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/guides/int_framework/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ Interaction service complex parameter constructors are prioritized in the follow
3. Type's only public constuctor.

#### DM Permissions
> [!WARNING]
> [EnabledInDmAttribute] is being deprecated in favor of [CommandContextTypes] attribute.

You can use the [EnabledInDmAttribute] to configure whether a globally-scoped top level command should be enabled in Dms or not. Only works on top level commands.

Expand Down Expand Up @@ -419,6 +421,12 @@ Discord Slash Commands support name/description localization. Localization is av
}
```

## User Apps

User apps are the kind of Discord applications that are installed onto a user instead of a guild, thus making commands usable anywhere on Discord. Note that only users who have installed the application will see the commands. This sample shows you how to create a simple user install command.

[!code-csharp[Registering Commands Example](samples/intro/userapps.cs)]

[AutocompleteHandlers]: xref:Guides.IntFw.AutoCompletion
[DependencyInjection]: xref:Guides.DI.Intro

Expand Down Expand Up @@ -447,6 +455,8 @@ Discord Slash Commands support name/description localization. Localization is av
[ChannelTypesAttribute]: xref:Discord.Interactions.ChannelTypesAttribute
[MaxValueAttribute]: xref:Discord.Interactions.MaxValueAttribute
[MinValueAttribute]: xref:Discord.Interactions.MinValueAttribute
[EnabledInDmAttribute]: xref:Discord.Interactions.EnabledInDmAttribute
[CommandContextTypes]: xref:Discord.Interactions.CommandContextTypesAttribute

[IChannel]: xref:Discord.IChannel
[IRole]: xref:Discord.IRole
Expand Down
19 changes: 19 additions & 0 deletions docs/guides/int_framework/samples/intro/userapps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

// This parameteres can be configured on the module level
// Set supported command context types to Bot DMs and Private Channels (regular DM & GDM)
[CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel)]
// Set supported integration installation type to User Install
[IntegrationType(ApplicationIntegrationType.UserInstall)]
public class CommandModule() : InteractionModuleBase<SocketInteractionContext>
{
[SlashCommand("test", "Just a test command")]
public async Task TestCommand()
=> await RespondAsync("Hello There");

// But can also be overridden on the command level
[CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel, InteractionContextType.Guild)]
[IntegrationType(ApplicationIntegrationType.GuildInstall)]
[SlashCommand("echo", "Echo the input")]
public async Task EchoCommand(string input)
=> await RespondAsync($"You said: {input}");
}
6 changes: 3 additions & 3 deletions docs/toc.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
- name: Home
href: index.md
- name: Documentation
href: api/
topicUid: API.Docs
- name: Guides
href: guides/
topicUid: Guides.Introduction
- name: API Reference
href: api/
topicUid: API.Docs
- name: FAQ
href: faq/
topicUid: FAQ.Basics.GetStarted
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Discord;

public enum ApplicationIntegrationType
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// The application can be installed to a guild.
/// </summary>
GuildInstall = 0,

/// <summary>
/// The application can be installed to a user.
/// </summary>
UserInstall = 1,
}
5 changes: 5 additions & 0 deletions src/Discord.Net.Core/Entities/Applications/IApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,10 @@ public interface IApplication : ISnowflakeEntity
/// Gets the application's verification state.
/// </summary>
ApplicationVerificationState VerificationState { get; }

/// <summary>
/// Gets application install params configured for integration install types.
/// </summary>
IReadOnlyDictionary<ApplicationIntegrationType, ApplicationInstallParams> IntegrationTypesConfig { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;

namespace Discord;

/// <summary>
Expand Down Expand Up @@ -54,4 +56,9 @@ public class ModifyApplicationProperties
/// </remarks>
public Optional<ApplicationFlags> Flags { get; set; }

/// <summary>
/// Gets or sets application install params configured for integration install types.
/// </summary>
public Optional<Dictionary<ApplicationIntegrationType, ApplicationInstallParams>> IntegrationTypesConfig { get; set; }

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ public IReadOnlyDictionary<string, string> DescriptionLocalizations
/// </summary>
public Optional<GuildPermission> DefaultMemberPermissions { get; set; }

/// <summary>
/// Gets or sets the install method for this command.
/// </summary>
public Optional<HashSet<ApplicationIntegrationType>> IntegrationTypes { get; set; }

/// <summary>
/// Gets or sets context types this command can be executed in.
/// </summary>
public Optional<HashSet<InteractionContextType>> ContextTypes { get; set; }

internal ApplicationCommandProperties() { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public interface IApplicationCommand : ISnowflakeEntity, IDeletable
/// <remarks>
/// Only for globally-scoped commands.
/// </remarks>
[Obsolete("This property will be deprecated soon. Use ContextTypes instead.")]
bool IsEnabledInDm { get; }

/// <summary>
Expand Down Expand Up @@ -83,6 +84,16 @@ public interface IApplicationCommand : ISnowflakeEntity, IDeletable
/// </remarks>
string DescriptionLocalized { get; }

/// <summary>
/// Gets context types the command can be used in. <see langword="null" /> if not specified.
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
IReadOnlyCollection<InteractionContextType> ContextTypes { get; }

/// <summary>
/// Gets the install method for the command. <see langword="null" /> if not specified.
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
IReadOnlyCollection<ApplicationIntegrationType> IntegrationTypes { get; }

/// <summary>
/// Modifies the current application command.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Discord;

public enum InteractionContextType
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// The command can be used in guilds.
/// </summary>
Guild = 0,

/// <summary>
/// The command can be used in DM channel with the bot.
/// </summary>
BotDm = 1,

/// <summary>
/// The command can be used in private channels.
/// </summary>
PrivateChannel = 2
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public string Name
/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
[Obsolete("This property will be deprecated soon. Configure with ContextTypes instead.")]
public bool IsDMEnabled { get; set; } = true;

/// <summary>
Expand All @@ -56,6 +57,16 @@ public string Name
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; }

/// <summary>
/// Gets the install method for this command.
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public HashSet<ApplicationIntegrationType> IntegrationTypes { get; set; } = null;

/// <summary>
/// Gets the context types this command can be executed in.
Misha-133 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public HashSet<InteractionContextType> ContextTypes { get; set; } = null;

private string _name;
private Dictionary<string, string> _nameLocalizations;

Expand All @@ -71,10 +82,14 @@ public MessageCommandProperties Build()
{
Name = Name,
IsDefaultPermission = IsDefaultPermission,
#pragma warning disable CS0618 // Type or member is obsolete
IsDMEnabled = IsDMEnabled,
#pragma warning restore CS0618 // Type or member is obsolete
DefaultMemberPermissions = DefaultMemberPermissions ?? Optional<GuildPermission>.Unspecified,
NameLocalizations = NameLocalizations,
IsNsfw = IsNsfw,
IntegrationTypes = IntegrationTypes,
ContextTypes = ContextTypes
};

return props;
Expand Down Expand Up @@ -133,6 +148,7 @@ public MessageCommandBuilder WithNameLocalizations(IDictionary<string, string> n
/// </summary>
/// <param name="permission"><see langword="true"/> if the command is available in dms, otherwise <see langword="false"/>.</param>
/// <returns>The current builder.</returns>
[Obsolete("This method will be deprecated soon. Configure with WithContextTypes instead.")]
public MessageCommandBuilder WithDMPermission(bool permission)
{
IsDMEnabled = permission;
Expand Down Expand Up @@ -187,5 +203,31 @@ public MessageCommandBuilder WithDefaultMemberPermissions(GuildPermission? permi
DefaultMemberPermissions = permissions;
return this;
}

/// <summary>
/// Sets the install method for this command.
/// </summary>
/// <param name="integrationTypes">Install types for this command.</param>
/// <returns>The builder instance.</returns>
public MessageCommandBuilder WithIntegrationTypes(params ApplicationIntegrationType[] integrationTypes)
{
IntegrationTypes = integrationTypes is not null
? new HashSet<ApplicationIntegrationType>(integrationTypes)
: null;
return this;
}

/// <summary>
/// Sets context types this command can be executed in.
/// </summary>
/// <param name="contextTypes">Context types the command can be executed in.</param>
/// <returns>The builder instance.</returns>
public MessageCommandBuilder WithContextTypes(params InteractionContextType[] contextTypes)
{
ContextTypes = contextTypes is not null
? new HashSet<InteractionContextType>(contextTypes)
: null;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public string Name
/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
[Obsolete("This property will be deprecated soon. Configure with ContextTypes instead.")]
public bool IsDMEnabled { get; set; } = true;

/// <summary>
Expand All @@ -56,6 +57,16 @@ public string Name
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; }

/// <summary>
/// Gets the installation method for this command. <see langword="null"/> if not set.
/// </summary>
public HashSet<ApplicationIntegrationType> IntegrationTypes { get; set; }

/// <summary>
/// Gets the context types this command can be executed in. <see langword="null"/> if not set.
/// </summary>
public HashSet<InteractionContextType> ContextTypes { get; set; }

private string _name;
private Dictionary<string, string> _nameLocalizations;

Expand All @@ -69,10 +80,14 @@ public UserCommandProperties Build()
{
Name = Name,
IsDefaultPermission = IsDefaultPermission,
#pragma warning disable CS0618 // Type or member is obsolete
IsDMEnabled = IsDMEnabled,
#pragma warning restore CS0618 // Type or member is obsolete
DefaultMemberPermissions = DefaultMemberPermissions ?? Optional<GuildPermission>.Unspecified,
NameLocalizations = NameLocalizations,
IsNsfw = IsNsfw,
ContextTypes = ContextTypes,
IntegrationTypes = IntegrationTypes
};

return props;
Expand Down Expand Up @@ -131,6 +146,7 @@ public UserCommandBuilder WithNameLocalizations(IDictionary<string, string> name
/// </summary>
/// <param name="permission"><see langword="true"/> if the command is available in dms, otherwise <see langword="false"/>.</param>
/// <returns>The current builder.</returns>
[Obsolete("This method will be deprecated soon. Configure with WithContextTypes instead.")]
public UserCommandBuilder WithDMPermission(bool permission)
{
IsDMEnabled = permission;
Expand Down Expand Up @@ -185,5 +201,31 @@ public UserCommandBuilder WithDefaultMemberPermissions(GuildPermission? permissi
DefaultMemberPermissions = permissions;
return this;
}

/// <summary>
/// Sets the installation method for this command.
/// </summary>
/// <param name="integrationTypes">Installation types for this command.</param>
/// <returns>The builder instance.</returns>
public UserCommandBuilder WithIntegrationTypes(params ApplicationIntegrationType[] integrationTypes)
{
IntegrationTypes = integrationTypes is not null
? new HashSet<ApplicationIntegrationType>(integrationTypes)
: null;
return this;
}

/// <summary>
/// Sets context types this command can be executed in.
/// </summary>
/// <param name="contextTypes">Context types the command can be executed in.</param>
/// <returns>The builder instance.</returns>
public UserCommandBuilder WithContextTypes(params InteractionContextType[] contextTypes)
{
ContextTypes = contextTypes is not null
? new HashSet<InteractionContextType>(contextTypes)
: null;
return this;
}
}
}
15 changes: 15 additions & 0 deletions src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ public interface IDiscordInteraction : ISnowflakeEntity
/// </summary>
IReadOnlyCollection<IEntitlement> Entitlements { get; }

/// <summary>
/// Gets which integrations authorized the interaction.
/// </summary>
IReadOnlyDictionary<ApplicationIntegrationType, ulong> IntegrationOwners { get; }

/// <summary>
/// Gets the context this interaction was created in. <see langword="null"/> if context type is unknown.
/// </summary>
InteractionContextType? ContextType { get; }

/// <summary>
/// Gets the permissions the app or bot has within the channel the interaction was sent from.
/// </summary>
GuildPermissions Permissions { get; }

/// <summary>
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;

namespace Discord;

/// <summary>
/// Represents the metadata of an application command interaction.
/// </summary>
public readonly struct ApplicationCommandInteractionMetadata : IMessageInteractionMetadata
{
/// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);

/// <inheritdoc />
public ulong Id { get; }

/// <inheritdoc />
public InteractionType Type { get; }

/// <inheritdoc />
public ulong UserId { get; }

/// <inheritdoc />
public IReadOnlyDictionary<ApplicationIntegrationType, ulong> IntegrationOwners { get; }

/// <inheritdoc />
public ulong? OriginalResponseMessageId { get; }

/// <summary>
/// Gets the name of the command.
/// </summary>
public string Name { get; }

internal ApplicationCommandInteractionMetadata(ulong id, InteractionType type, ulong userId, IReadOnlyDictionary<ApplicationIntegrationType, ulong> integrationOwners,
ulong? originalResponseMessageId, string name)
{
Id = id;
Type = type;
UserId = userId;
IntegrationOwners = integrationOwners;
OriginalResponseMessageId = originalResponseMessageId;
Name = name;
}
}
Loading
Loading