Skip to content

Commit 2023d98

Browse files
committed
add /command prefix
1 parent 87f1c51 commit 2023d98

File tree

15 files changed

+242
-17
lines changed

15 files changed

+242
-17
lines changed

src/TaylorBot.Net/Core/src/TaylorBot.Net.Commands/PostExecution/InteractionCustomId.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public enum CustomIdNames
7979
MonitorEditedStop = 132,
8080
MonitorDeletedStop = 133,
8181
MonitorMembersStop = 134,
82+
CommandPrefixToggle = 135,
8283
}
8384

8485
public static class CustomIdExtensions

src/TaylorBot.Net/Core/src/TaylorBot.Net.Commands/PostExecution/SlashCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ await CreateInteractionClient().SendFollowupResponseAsync(
192192
}
193193
else
194194
{
195-
logger.LogWarning("Slash command {CommandName} not found", commandName);
195+
logger.LogWarning("Slash command '{CommandName}' not found", commandName);
196196
}
197197
}
198198
else

src/TaylorBot.Net/Core/src/TaylorBot.Net.Commands/Preconditions/NotGuildDisabledPrecondition.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,58 @@ public async Task<bool> IsGuildCommandDisabledAsync(CommandGuild guild, CommandM
3333
}
3434
}
3535

36-
public class NotGuildDisabledPrecondition(DisabledGuildCommandDomainService disabledGuildCommandDomainService, UserHasPermissionOrOwnerPrecondition.Factory userHasPermission, CommandMentioner mention) : ICommandPrecondition
36+
public class NotGuildDisabledPrecondition(
37+
DisabledGuildCommandDomainService disabledGuildCommandDomainService,
38+
UserHasPermissionOrOwnerPrecondition.Factory userHasPermission,
39+
IDisabledGuildCommandRepository disabledGuildCommandRepository,
40+
CommandMentioner mention) : ICommandPrecondition
3741
{
42+
private readonly UserHasPermissionOrOwnerPrecondition userHasManageGuild = userHasPermission.Create(GuildPermission.ManageGuild);
43+
3844
public async ValueTask<ICommandResult> CanRunAsync(Command command, RunContext context)
3945
{
4046
if (context.Guild == null)
47+
{
4148
return new PreconditionPassed();
49+
}
4250

4351
var isDisabled = await disabledGuildCommandDomainService.IsGuildCommandDisabledAsync(context.Guild, command.Metadata, context);
44-
45-
var canRun = await userHasPermission.Create(GuildPermission.ManageGuild).CanRunAsync(command, context);
46-
47-
return isDisabled ?
48-
new PreconditionFailed(
52+
if (isDisabled)
53+
{
54+
return new PreconditionFailed(
4955
PrivateReason: $"{command.Metadata.Name} is disabled in {context.Guild.FormatLog()}",
5056
UserReason: new(
5157
$"""
5258
You can't use {mention.Command(command, context)} because it is disabled in this server 🚫
53-
{(canRun is PreconditionPassed
54-
? $"You can re-enable it by typing </command server-enable:909694280703016991> {command.Metadata.Name} ✅"
59+
{(await userHasManageGuild.CanRunAsync(command, context) is PreconditionPassed
60+
? $"You can re-enable it by typing {mention.SlashCommand("command server-enable")} {command.Metadata.Name} ✅"
5561
: "Ask a moderator to re-enable it 🙏")}
5662
""",
5763
HideInPrefixCommands: true)
58-
) :
59-
new PreconditionPassed();
64+
);
65+
}
66+
67+
if (context.PrefixCommand != null)
68+
{
69+
var result = await disabledGuildCommandRepository.IsGuildCommandDisabledAsync(context.Guild, new("all-prefix"));
70+
var arePrefixCommandsDisabled = result.IsDisabled;
71+
if (arePrefixCommandsDisabled)
72+
{
73+
return new PreconditionFailed(
74+
PrivateReason: $"Prefix commands disabled in {context.Guild.FormatLog()}",
75+
UserReason: new(
76+
$"""
77+
You can't use {mention.Command(command, context)} because prefix commands are disabled in this server 🚫
78+
{(context.PrefixCommand.ReplacementSlashCommands != null && context.PrefixCommand.ReplacementSlashCommands.Count > 1
79+
? $"Use these slash commands instead ⚡\n{string.Join('\n', context.PrefixCommand.ReplacementSlashCommands.Select(c => $"👉 {mention.SlashCommand(c, context)} 👈"))}"
80+
: context.PrefixCommand.ReplacementSlashCommands != null && context.PrefixCommand.ReplacementSlashCommands.Count == 1
81+
? $"Use the slash command 👉 {mention.SlashCommand(context.PrefixCommand.ReplacementSlashCommands[0], context)} 👈 instead ⚡"
82+
: $"Sorry, slash commands starting with **/** are the future of commands on Discord 😕")}
83+
""")
84+
);
85+
}
86+
}
87+
88+
return new PreconditionPassed();
6089
}
6190
}

src/TaylorBot.Net/Core/src/TaylorBot.Net.Core.Infrastructure/TaylorBot.Net.Core.Infrastructure.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.3" />
1111
<PackageReference Include="Npgsql.OpenTelemetry" Version="9.0.3" />
1212
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
13-
<PackageReference Include="StackExchange.Redis" Version="2.8.41" />
13+
<PackageReference Include="StackExchange.Redis" Version="2.8.47" />
1414
</ItemGroup>
1515

1616
<ItemGroup>

src/TaylorBot.Net/Core/src/TaylorBot.Net.Core/TaylorBot.Net.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<ItemGroup>
88
<PackageReference Include="CSharp.OperationResult" Version="0.1.6" />
9-
<PackageReference Include="Discord.Net" Version="3.18.0-beta.3" />
9+
<PackageReference Include="Discord.Net" Version="3.18.0" />
1010
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
1111
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
1212
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" />

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/DiscordCommandsProgram.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ public static void ConfigureServices(HostBuilderContext hostBuilderContext, ISer
265265
.AddSlashCommand<CommandServerEnableSlashCommand>()
266266
.AddSlashCommand<CommandChannelDisableSlashCommand>()
267267
.AddSlashCommand<CommandChannelEnableSlashCommand>()
268+
.AddSlashCommand<CommandServerPrefixSlashCommand>()
269+
.AddButtonHandler<CommandPrefixToggleHandler>()
268270
.AddTransient<LastFmEmbedFactory>()
269271
.AddSlashCommand<LastFmCurrentSlashCommand>()
270272
.AddSlashCommand<LastFmSetSlashCommand>()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
using Discord;
2+
using TaylorBot.Net.Commands.Parsers;
3+
using TaylorBot.Net.Commands.PostExecution;
4+
using TaylorBot.Net.Commands.Preconditions;
5+
using TaylorBot.Net.Core.Embed;
6+
7+
namespace TaylorBot.Net.Commands.Discord.Program.Modules.Commands.Commands;
8+
9+
public class CommandServerPrefixSlashCommand(
10+
UserHasPermissionOrOwnerPrecondition.Factory userHasPermission,
11+
CommandMentioner mention,
12+
InGuildPrecondition.Factory inGuild,
13+
ICommandPrefixRepository commandPrefixRepository,
14+
IDisabledGuildCommandRepository disabledGuildCommandRepository) : ISlashCommand<NoOptions>
15+
{
16+
public static string CommandName => "command prefix";
17+
18+
public ISlashCommandInfo Info => new MessageCommandInfo(CommandName);
19+
20+
public IList<ICommandPrecondition> BuildPreconditions() => [
21+
inGuild.Create(botMustBeInGuild: true),
22+
userHasPermission.Create(GuildPermission.ManageGuild)
23+
];
24+
25+
public ValueTask<Command> GetCommandAsync(RunContext context, NoOptions _)
26+
{
27+
return new(new Command(
28+
new(Info.Name),
29+
async () =>
30+
{
31+
var guild = context.Guild;
32+
ArgumentNullException.ThrowIfNull(guild);
33+
ArgumentNullException.ThrowIfNull(guild.Fetched);
34+
35+
var prefixResult = await commandPrefixRepository.GetOrInsertGuildPrefixAsync(guild.Fetched);
36+
var currentPrefix = prefixResult.Prefix;
37+
38+
var prefixDisabledResult = await disabledGuildCommandRepository.IsGuildCommandDisabledAsync(guild, new("all-prefix"));
39+
var arePrefixCommandsDisabled = prefixDisabledResult.IsDisabled;
40+
41+
var description =
42+
$"""
43+
## Prefix Commands 👴
44+
Prefix commands are commands that are used in text channels by typing a prefix followed by the command name 💬
45+
The default prefix for TaylorBot is `!`, allowing you to use commands by typing `!help` for example ❗
46+
## This Server's Prefix 🖌️
47+
- **Current server prefix:** `{currentPrefix}`
48+
One issue with prefix commands is that multiple bots can use the same prefix 🪢
49+
This means typing `!help` could trigger multiple bots at once, which can be confusing/spammy 😵
50+
To solve this, TaylorBot allows you to change the prefix for your server with "<@119572982178906114> setprefix" command ⚙️
51+
## Disabling Prefix Commands 🚫
52+
53+
""";
54+
55+
InteractionComponent button;
56+
if (arePrefixCommandsDisabled)
57+
{
58+
description +=
59+
$"""
60+
Prefix commands are currently **disabled** in this server ⛔
61+
Users must use slash commands (e.g. {mention.SlashCommand("help")}) instead of prefix commands like `{currentPrefix}help` ✅
62+
You can re-enable prefix commands using the button below ⬇️
63+
""";
64+
button = CreateEnablePrefixButton();
65+
}
66+
else
67+
{
68+
description +=
69+
$"""
70+
While prefix commands used to be the only way to interact with bots on Discord, they have many downsides 🥲
71+
Slash commands are now the preferred modern way to interact with bots, with many improvements such as buttons, modals, etc. 💪
72+
For example, you can use {mention.SlashCommand("help")} instead of typing `{currentPrefix}help` ⚡
73+
74+
Some servers may want to disable prefix commands entirely to avoid confusion and encourage users to use slash commands instead 🐣
75+
Use the button below to disable prefix commands in this server ⬇️
76+
""";
77+
button = CreateDisablePrefixButton();
78+
}
79+
80+
var embed = EmbedFactory.CreateSuccess(description);
81+
return new MessageResult(new(new(embed), [button]));
82+
},
83+
Preconditions: BuildPreconditions()
84+
));
85+
}
86+
87+
public static InteractionComponent CreateDisablePrefixButton()
88+
{
89+
return InteractionComponent.CreateActionRow(InteractionComponent.CreateButton(
90+
style: InteractionButtonStyle.Danger,
91+
custom_id: InteractionCustomId.Create(CustomIdNames.CommandPrefixToggle, [new("enable", bool.FalseString)]).RawId,
92+
label: "Disable prefix commands"));
93+
}
94+
95+
public static InteractionComponent CreateEnablePrefixButton()
96+
{
97+
return InteractionComponent.CreateActionRow(InteractionComponent.CreateButton(
98+
style: InteractionButtonStyle.Success,
99+
custom_id: InteractionCustomId.Create(CustomIdNames.CommandPrefixToggle, [new("enable", bool.TrueString)]).RawId,
100+
label: "Enable prefix commands"));
101+
}
102+
}
103+
104+
public class CommandPrefixToggleHandler(
105+
IInteractionResponseClient responseClient,
106+
CommandServerPrefixSlashCommand command,
107+
IDisabledGuildCommandRepository disabledGuildCommandRepository,
108+
CommandMentioner mention,
109+
ICommandPrefixRepository commandPrefixRepository
110+
) : IButtonHandler
111+
{
112+
public static CustomIdNames CustomIdName => CustomIdNames.CommandPrefixToggle;
113+
114+
public IComponentHandlerInfo Info => new MessageHandlerInfo(
115+
CustomIdName.ToText(),
116+
Preconditions: command.BuildPreconditions(),
117+
RequireOriginalUser: true);
118+
119+
public async Task HandleAsync(DiscordButtonComponent button, RunContext context)
120+
{
121+
var guild = context.Guild;
122+
ArgumentNullException.ThrowIfNull(guild);
123+
ArgumentNullException.ThrowIfNull(guild.Fetched);
124+
125+
var enable = button.CustomId.ParsedData.TryGetValue("enable", out var enableStr)
126+
&& bool.TryParse(enableStr, out var parsed) && parsed == true;
127+
128+
if (enable)
129+
{
130+
await disabledGuildCommandRepository.EnableInAsync(guild.Fetched, "all-prefix");
131+
132+
var prefixResult = await commandPrefixRepository.GetOrInsertGuildPrefixAsync(guild.Fetched);
133+
var currentPrefix = prefixResult.Prefix;
134+
135+
var embed = EmbedFactory.CreateSuccessEmbed(
136+
$"""
137+
Prefix commands are now enabled in this server ✅
138+
Users can use prefix commands like `{currentPrefix}help` again 👴
139+
You can disable prefix commands at any time using {mention.SlashCommand("command prefix")} ↩️
140+
""");
141+
await responseClient.EditOriginalResponseAsync(button.Interaction, embed);
142+
}
143+
else
144+
{
145+
await disabledGuildCommandRepository.DisableInAsync(guild.Fetched, "all-prefix");
146+
147+
var embed = EmbedFactory.CreateSuccessEmbed(
148+
$"""
149+
Prefix commands are now disabled in this server ✅
150+
Users will need to use slash commands instead 💪
151+
You can re-enable prefix commands at any time using {mention.SlashCommand("command prefix")} ↩️
152+
""");
153+
await responseClient.EditOriginalResponseAsync(button.Interaction, embed);
154+
}
155+
}
156+
}

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Help/Commands/HelpSlashCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task<MessageResponse> GetHelpResponseAsync(RunContext context)
4545
{applicationInfo.Description}
4646
{(context.SlashCommand != null
4747
? "### Pick a command category below to learn more 👇"
48-
: $"### Use {mention.SlashCommand("help")} to learn more about commands! ")}
48+
: $"### Use {mention.SlashCommand("help")} to learn more about commands! 💫")}
4949
""")
5050
.Build();
5151

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/TaylorBot.Net.Commands.Discord.Program.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
<PackageReference Include="Azure.Identity" Version="1.14.2" />
2323
<PackageReference Include="Azure.Storage.Blobs" Version="12.25.0" />
2424
<PackageReference Include="Google.Apis.CustomSearchAPI.v1" Version="1.68.0.3520" />
25-
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.69.0.3764" />
26-
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
25+
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.70.0.3847" />
26+
<PackageReference Include="HtmlAgilityPack" Version="1.12.2" />
2727
<PackageReference Include="Inflatable.Lastfm" Version="1.2.0" />
2828
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
2929
</ItemGroup>

src/TaylorBot.Net/Program.UserNotifier/src/TaylorBot.Net.YoutubeNotifier.Domain/TaylorBot.Net.YoutubeNotifier.Domain.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.69.0.3764" />
8+
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.70.0.3847" />
99
</ItemGroup>
1010

1111
<ItemGroup>

0 commit comments

Comments
 (0)