diff --git a/ApplicationCommands/BirthdayModule.cs b/ApplicationCommands/BirthdayModule.cs index 86d7906..6c7c267 100644 --- a/ApplicationCommands/BirthdayModule.cs +++ b/ApplicationCommands/BirthdayModule.cs @@ -3,8 +3,8 @@ using Discord.Interactions; using System.Text; namespace BirthdayBot.ApplicationCommands; -[RequireGuildContext] [Group("birthday", HelpCmdBirthday)] +[EnabledInDm(false)] public class BirthdayModule : BotModuleBase { public const string HelpCmdBirthday = "Commands relating to birthdays."; public const string HelpCmdSetDate = "Sets or updates your birthday."; @@ -191,7 +191,7 @@ public class BirthdayModule : BotModuleBase { await doOutput(output.ToString()).ConfigureAwait(false); } - [RequireBotModerator] + [DefaultMemberPermissions(GuildPermission.ManageGuild)] [SlashCommand("export", HelpPfxModOnly + HelpCmdExport)] public async Task CmdExport([Summary(description: "Specify whether to export the list in CSV format.")] bool asCsv = false) { if (!await HasMemberCacheAsync(Context.Guild)) { diff --git a/ApplicationCommands/BirthdayOverrideModule.cs b/ApplicationCommands/BirthdayOverrideModule.cs index 2a6de5a..b6a059b 100644 --- a/ApplicationCommands/BirthdayOverrideModule.cs +++ b/ApplicationCommands/BirthdayOverrideModule.cs @@ -3,8 +3,9 @@ using Discord.Interactions; using static BirthdayBot.Common; namespace BirthdayBot.ApplicationCommands; -[RequireBotModerator] [Group("override", HelpCmdOverride)] +[DefaultMemberPermissions(GuildPermission.ManageGuild)] +[EnabledInDm(false)] public class BirthdayOverrideModule : BotModuleBase { public const string HelpCmdOverride = "Commands to set options for other users."; const string HelpOptOvTarget = "The user whose data to modify."; diff --git a/ApplicationCommands/ConfigModule.cs b/ApplicationCommands/ConfigModule.cs index e64acf3..0818dd2 100644 --- a/ApplicationCommands/ConfigModule.cs +++ b/ApplicationCommands/ConfigModule.cs @@ -3,13 +3,13 @@ using Discord.Interactions; using System.Text; namespace BirthdayBot.ApplicationCommands; -[RequireBotModerator] [Group("config", HelpCmdConfig)] +[DefaultMemberPermissions(GuildPermission.ManageGuild)] +[EnabledInDm(false)] public class ConfigModule : BotModuleBase { public const string HelpCmdConfig = "Configure basic settings for the bot."; public const string HelpCmdAnnounce = "Settings regarding birthday announcements."; - public const string HelpCmdBlocking = "Settings for limiting user access."; - public const string HelpCmdRole = "Settings for roles used by this bot."; + public const string HelpCmdBirthdayRole = "Set the role given to users having a birthday."; public const string HelpCmdCheck = "Test the bot's current configuration and show the results."; const string HelpPofxBlankUnset = " Leave blank to unset."; @@ -118,28 +118,14 @@ public class ConfigModule : BotModuleBase { } } - [Group("role", HelpPfxModOnly + HelpCmdRole)] - public class SubCmdsConfigRole : BotModuleBase { - [SlashCommand("set-birthday-role", HelpPfxModOnly + "Set the role given to users having a birthday.")] - public async Task CmdSetBRole([Summary(description: HelpOptRole)]SocketRole role) { - if (role.IsEveryone || role.IsManaged) { - await RespondAsync(":x: This role cannot be used for this setting.", ephemeral: true); - return; - } - await DoDatabaseUpdate(Context, s => s.BirthdayRole = role.Id); - await RespondAsync($":white_check_mark: The birthday role has been set to **{role.Name}**.").ConfigureAwait(false); - } - - [SlashCommand("set-moderator-role", HelpPfxModOnly + "Designate a role whose members can configure the bot." + HelpPofxBlankUnset)] - public async Task CmdSetModRole([Summary(description: HelpOptRole)]SocketRole? role = null) { - if (role != null && (role.IsEveryone || role.IsManaged)) { - await RespondAsync(":x: This role cannot be used for this setting.", ephemeral: true); - return; - } - await DoDatabaseUpdate(Context, s => s.ModeratorRole = role?.Id); - await RespondAsync(":white_check_mark: The moderator role has been " + - (role == null ? "unset." : $"set to **{role.Name}**.")); + [SlashCommand("birthday-role", HelpPfxModOnly + HelpCmdBirthdayRole)] + public async Task CmdSetBRole([Summary(description: HelpOptRole)] SocketRole role) { + if (role.IsEveryone || role.IsManaged) { + await RespondAsync(":x: This role cannot be used for this setting.", ephemeral: true); + return; } + await DoDatabaseUpdate(Context, s => s.BirthdayRole = role.Id); + await RespondAsync($":white_check_mark: The birthday role has been set to **{role.Name}**.").ConfigureAwait(false); } [SlashCommand("check", HelpPfxModOnly + HelpCmdCheck)] diff --git a/ApplicationCommands/HelpModule.cs b/ApplicationCommands/HelpModule.cs index 19bf614..fbd7a3b 100644 --- a/ApplicationCommands/HelpModule.cs +++ b/ApplicationCommands/HelpModule.cs @@ -1,6 +1,7 @@ using Discord.Interactions; namespace BirthdayBot.ApplicationCommands; +[EnabledInDm(true)] public class HelpModule : BotModuleBase { private const string TopMessage = "Thank you for using Birthday Bot!\n" + @@ -20,10 +21,7 @@ public class HelpModule : BotModuleBase { $"` ⤷check` - {ConfigModule.HelpCmdCheck}\n" + $"` ⤷announce` - {ConfigModule.HelpCmdAnnounce}\n" + $"` ⤷` See also: `/config announce help`.\n" + - $"` ⤷block` - {ConfigModule.HelpCmdBlocking}\n" + - $"` ⤷add-block`, `⤷remove-block`, `⤷set-moderated`\n" + - $"` ⤷role` - {ConfigModule.HelpCmdRole}\n" + - $"` ⤷set-birthday-role`, `⤷set-moderator-role`\n" + + $"` ⤷birthday-role` - {ConfigModule.HelpCmdBirthdayRole}\n" + $"`/override` - {BirthdayOverrideModule.HelpCmdOverride}\n" + $"` ⤷set-birthday`, `⤷set-timezone`, `⤷remove`\n" + "**Caution:** Skipping optional parameters __removes__ their configuration."; diff --git a/ApplicationCommands/Preconditions/RequireBotModerator.cs b/ApplicationCommands/Preconditions/RequireBotModerator.cs deleted file mode 100644 index a6a6589..0000000 --- a/ApplicationCommands/Preconditions/RequireBotModerator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BirthdayBot.Data; -using Discord.Interactions; - -namespace BirthdayBot.ApplicationCommands; -/// -/// Precondition requiring the executing user be recognized as a bot moderator.
-/// A bot moderator has either the Manage Server permission or is a member of the designated bot moderator role. -///
-[Obsolete("Replace with appropriate DefaultMemberPermissionsAttribute")] -class RequireBotModeratorAttribute : PreconditionAttribute { - public const string Error = "User did not pass the mod check."; - public const string Reply = ":x: You must be a moderator to use this command."; - - public override string ErrorMessage => Error; - - public override Task CheckRequirementsAsync( - IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services) { - // A bot moderator can only exist in a guild context, so we must do this check. - // This check causes this precondition to become a functional equivalent to RequireGuildContextAttribute... - if (context.User is not SocketGuildUser user) - return Task.FromResult(PreconditionResult.FromError(RequireGuildContextAttribute.Error)); - - if (user.GuildPermissions.ManageGuild) return Task.FromResult(PreconditionResult.FromSuccess()); - using var db = new BotDatabaseContext(); - var checkRole = (ulong?)db.GuildConfigurations - .Where(g => g.GuildId == ((SocketGuild)context.Guild).Id) - .Select(g => g.ModeratorRole).FirstOrDefault(); - if (checkRole.HasValue && user.Roles.Any(r => r.Id == checkRole.Value)) - return Task.FromResult(PreconditionResult.FromSuccess()); - - return Task.FromResult(PreconditionResult.FromError(Error)); - } -} \ No newline at end of file diff --git a/ApplicationCommands/Preconditions/RequireGuildContext.cs b/ApplicationCommands/Preconditions/RequireGuildContext.cs deleted file mode 100644 index d4c8ab0..0000000 --- a/ApplicationCommands/Preconditions/RequireGuildContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Discord.Interactions; - -namespace BirthdayBot.ApplicationCommands; -/// -/// Implements the included precondition from Discord.Net, requiring a guild context while using our custom error message.

-/// Combining this with is redundant. If possible, only use the latter instead. -///
-[Obsolete("Replace with EnableInDmAttribute")] -class RequireGuildContextAttribute : RequireContextAttribute { - public const string Error = "Command not received within a guild context."; - public const string Reply = ":x: This command is only available within a server."; - - public override string ErrorMessage => Error; - - public RequireGuildContextAttribute() : base(ContextType.Guild) { } -} \ No newline at end of file diff --git a/Data/GuildConfig.cs b/Data/GuildConfig.cs index e3d747d..a4cfb1d 100644 --- a/Data/GuildConfig.cs +++ b/Data/GuildConfig.cs @@ -16,11 +16,6 @@ public class GuildConfig { [Column("time_zone")] public string? GuildTimeZone { get; set; } - public bool Moderated { get; set; } - - [Obsolete("To be removed when RequireBotModeratorAttribute is also removed")] - public ulong? ModeratorRole { get; set; } - public string? AnnounceMessage { get; set; } public string? AnnounceMessagePl { get; set; } diff --git a/Data/Migrations/20230204063321_RemoveModRole.Designer.cs b/Data/Migrations/20230204063321_RemoveModRole.Designer.cs new file mode 100644 index 0000000..e40e799 --- /dev/null +++ b/Data/Migrations/20230204063321_RemoveModRole.Designer.cs @@ -0,0 +1,123 @@ +// +using System; +using BirthdayBot.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace BirthdayBot.Data.Migrations +{ + [DbContext(typeof(BotDatabaseContext))] + [Migration("20230204063321_RemoveModRole")] + partial class RemoveModRole + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b => + { + b.Property("GuildId") + .HasColumnType("numeric(20,0)") + .HasColumnName("guild_id"); + + b.Property("AnnounceMessage") + .HasColumnType("text") + .HasColumnName("announce_message"); + + b.Property("AnnounceMessagePl") + .HasColumnType("text") + .HasColumnName("announce_message_pl"); + + b.Property("AnnouncePing") + .HasColumnType("boolean") + .HasColumnName("announce_ping"); + + b.Property("AnnouncementChannel") + .HasColumnType("numeric(20,0)") + .HasColumnName("channel_announce_id"); + + b.Property("BirthdayRole") + .HasColumnType("numeric(20,0)") + .HasColumnName("role_id"); + + b.Property("GuildTimeZone") + .HasColumnType("text") + .HasColumnName("time_zone"); + + b.Property("LastSeen") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen") + .HasDefaultValueSql("now()"); + + b.HasKey("GuildId") + .HasName("settings_pkey"); + + b.ToTable("settings", (string)null); + }); + + modelBuilder.Entity("BirthdayBot.Data.UserEntry", b => + { + b.Property("GuildId") + .HasColumnType("numeric(20,0)") + .HasColumnName("guild_id"); + + b.Property("UserId") + .HasColumnType("numeric(20,0)") + .HasColumnName("user_id"); + + b.Property("BirthDay") + .HasColumnType("integer") + .HasColumnName("birth_day"); + + b.Property("BirthMonth") + .HasColumnType("integer") + .HasColumnName("birth_month"); + + b.Property("LastSeen") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen") + .HasDefaultValueSql("now()"); + + b.Property("TimeZone") + .HasColumnType("text") + .HasColumnName("time_zone"); + + b.HasKey("GuildId", "UserId") + .HasName("user_birthdays_pkey"); + + b.ToTable("user_birthdays", (string)null); + }); + + modelBuilder.Entity("BirthdayBot.Data.UserEntry", b => + { + b.HasOne("BirthdayBot.Data.GuildConfig", "Guild") + .WithMany("UserEntries") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("user_birthdays_guild_id_fkey"); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b => + { + b.Navigation("UserEntries"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20230204063321_RemoveModRole.cs b/Data/Migrations/20230204063321_RemoveModRole.cs new file mode 100644 index 0000000..f8845d4 --- /dev/null +++ b/Data/Migrations/20230204063321_RemoveModRole.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BirthdayBot.Data.Migrations +{ + /// + public partial class RemoveModRole : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "moderated", + table: "settings"); + + migrationBuilder.DropColumn( + name: "moderator_role", + table: "settings"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "moderated", + table: "settings", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "moderator_role", + table: "settings", + type: "numeric(20,0)", + nullable: true); + } + } +} diff --git a/Data/Migrations/BotDatabaseContextModelSnapshot.cs b/Data/Migrations/BotDatabaseContextModelSnapshot.cs index c82301e..7c5bee9 100644 --- a/Data/Migrations/BotDatabaseContextModelSnapshot.cs +++ b/Data/Migrations/BotDatabaseContextModelSnapshot.cs @@ -58,14 +58,6 @@ namespace BirthdayBot.Data.Migrations .HasColumnName("last_seen") .HasDefaultValueSql("now()"); - b.Property("Moderated") - .HasColumnType("boolean") - .HasColumnName("moderated"); - - b.Property("ModeratorRole") - .HasColumnType("numeric(20,0)") - .HasColumnName("moderator_role"); - b.HasKey("GuildId") .HasName("settings_pkey"); diff --git a/ShardInstance.cs b/ShardInstance.cs index 82ffa5e..cd51665 100644 --- a/ShardInstance.cs +++ b/ShardInstance.cs @@ -133,35 +133,18 @@ public sealed class ShardInstance : IDisposable { } // Slash command logging and failed execution handling - private async Task InteractionService_SlashCommandExecuted(SlashCommandInfo info, IInteractionContext context, IResult result) { + private Task InteractionService_SlashCommandExecuted(SlashCommandInfo info, IInteractionContext context, IResult result) { string sender; - if (context.Guild != null) { - sender = $"{context.Guild}!{context.User}"; - } else { - sender = $"{context.User} in non-guild context"; - } + if (context.Guild != null) sender = $"{context.Guild}!{context.User}"; + else sender = $"{context.User} in non-guild context"; var logresult = $"{(result.IsSuccess ? "Success" : "Fail")}: `/{info}` by {sender}."; if (result.Error != null) { // Additional log information with error detail logresult += " " + Enum.GetName(typeof(InteractionCommandError), result.Error) + ": " + result.ErrorReason; - - // Specific responses to errors, if necessary - if (result.Error == InteractionCommandError.UnmetPrecondition) { - var errReply = result.ErrorReason switch { - RequireBotModeratorAttribute.Error => RequireBotModeratorAttribute.Reply, - RequireGuildContextAttribute.Error => RequireGuildContextAttribute.Reply, - _ => result.ErrorReason - }; - await context.Interaction.RespondAsync(errReply, ephemeral: true).ConfigureAwait(false); - } else { - // Generic error response - var ia = context.Interaction; - if (ia.HasResponded) await ia.ModifyOriginalResponseAsync(p => p.Content = InternalError).ConfigureAwait(false); - else await ia.RespondAsync(InternalError).ConfigureAwait(false); - } } Log("Command", logresult); + return Task.CompletedTask; } }