Merge pull request #41 from NoiTheCat/remove-access-limits

Remove all blocking features
This commit is contained in:
Noi 2023-01-21 21:55:13 -08:00 committed by GitHub
commit 7b374833e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 176 additions and 157 deletions

2
.vscode/launch.json vendored
View file

@ -11,7 +11,7 @@
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net6.0/BirthdayBot.dll", "program": "${workspaceFolder}/bin/Debug/net6.0/BirthdayBot.dll",
"args": [], "args": [ "-c", "${workspaceFolder}/bin/Debug/net6.0/settings.json" ],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole", "console": "internalConsole",

View file

@ -9,7 +9,6 @@ namespace BirthdayBot.ApplicationCommands;
/// <summary> /// <summary>
/// Base class for our interaction module classes. Contains common data for use in implementing classes. /// Base class for our interaction module classes. Contains common data for use in implementing classes.
/// </summary> /// </summary>
[EnforceBlocking]
public abstract class BotModuleBase : InteractionModuleBase<SocketInteractionContext> { public abstract class BotModuleBase : InteractionModuleBase<SocketInteractionContext> {
protected const string HelpPfxModOnly = "Bot moderators only: "; protected const string HelpPfxModOnly = "Bot moderators only: ";
protected const string ErrGuildOnly = ":x: This command can only be run within a server."; protected const string ErrGuildOnly = ":x: This command can only be run within a server.";

View file

@ -142,50 +142,6 @@ public class ConfigModule : BotModuleBase {
} }
} }
[Group("block", HelpCmdBlocking)]
public class SubCmdsConfigBlocking : BotModuleBase {
[SlashCommand("add-block", HelpPfxModOnly + "Add a user to the block list.")]
public Task CmdAddBlock([Summary(description: "The user to block.")] SocketGuildUser user) => UpdateBlockAsync(user, true);
[SlashCommand("remove-block", HelpPfxModOnly + "Remove a user from the block list.")]
public Task CmdDelBlock([Summary(description: "The user to unblock.")] SocketGuildUser user) => UpdateBlockAsync(user, false);
private async Task UpdateBlockAsync(SocketGuildUser user, bool setting) {
// setting: true to add (set), false to remove (unset)
using var db = new BotDatabaseContext();
var existing = db.BlocklistEntries
.Where(bl => bl.GuildId == user.Guild.Id && bl.UserId == user.Id).FirstOrDefault();
var already = (existing != null) == setting;
if (already) {
await RespondAsync($":white_check_mark: User is already {(setting ? "" : "not ")}blocked.").ConfigureAwait(false);
return;
}
if (setting) db.BlocklistEntries.Add(new BlocklistEntry() { GuildId = user.Guild.Id, UserId = user.Id });
else db.Remove(existing!);
await db.SaveChangesAsync();
await RespondAsync($":white_check_mark: {Common.FormatName(user, false)} has been {(setting ? "" : "un")}blocked.");
}
[SlashCommand("set-moderated", HelpPfxModOnly + "Set moderated mode on the server.")]
public async Task CmdSetModerated([Summary(name: "enable", description: "The moderated mode setting.")] bool setting) {
var current = false;
await DoDatabaseUpdate(Context, s => {
current = s.Moderated;
s.Moderated = setting;
});
var already = setting == current;
if (already) {
await RespondAsync($":white_check_mark: Moderated mode is already **{(setting ? "en" : "dis")}abled**.");
} else {
await RespondAsync($":white_check_mark: Moderated mode is now **{(setting ? "en" : "dis")}abled**.").ConfigureAwait(false);
}
}
}
[SlashCommand("check", HelpPfxModOnly + HelpCmdCheck)] [SlashCommand("check", HelpPfxModOnly + HelpCmdCheck)]
public async Task CmdCheck() { public async Task CmdCheck() {
static string DoTestFor(string label, Func<bool> test) static string DoTestFor(string label, Func<bool> test)

View file

@ -1,42 +0,0 @@
using BirthdayBot.Data;
using Discord.Interactions;
namespace BirthdayBot.ApplicationCommands;
/// <summary>
/// Only users not on the blocklist or affected by moderator mode may use the command.<br/>
/// This is used in the <see cref="BotModuleBase"/> base class. Manually using it anywhere else is unnecessary.
/// </summary>
class EnforceBlockingAttribute : PreconditionAttribute {
public const string FailModerated = "Guild has moderator mode enabled.";
public const string FailBlocked = "User is in the guild's block list.";
public const string ReplyModerated = ":x: This bot is in moderated mode, preventing you from using any bot commands in this server.";
public const string ReplyBlocked = ":x: You have been blocked from using bot commands in this server.";
public override Task<PreconditionResult> CheckRequirementsAsync(
IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services) {
// Not in guild context, unaffected by blocking
if (context.Guild is not SocketGuild guild) return Task.FromResult(PreconditionResult.FromSuccess());
// Manage Guild permission overrides any blocks
var user = (SocketGuildUser)context.User;
if (user.GuildPermissions.ManageGuild) return Task.FromResult(PreconditionResult.FromSuccess());
using var db = new BotDatabaseContext();
var settings = (from row in db.GuildConfigurations
where row.GuildId == guild.Id
select new { ModRole = row.ModeratorRole, ModMode = row.Moderated }).FirstOrDefault();
if (settings != null) {
// Bot moderators override all blocking measures in place
if (user.Roles.Any(r => r.Id == settings.ModRole)) return Task.FromResult(PreconditionResult.FromSuccess());
// Check for moderated mode
if (settings.ModMode) return Task.FromResult(PreconditionResult.FromError(FailModerated));
// Check if user exists in blocklist
if (db.BlocklistEntries.Where(row => row.GuildId == guild.Id && row.UserId == user.Id).Any())
return Task.FromResult(PreconditionResult.FromError(FailBlocked));
}
return Task.FromResult(PreconditionResult.FromSuccess());
}
}

View file

@ -6,6 +6,7 @@ namespace BirthdayBot.ApplicationCommands;
/// Precondition requiring the executing user be recognized as a bot moderator.<br/> /// Precondition requiring the executing user be recognized as a bot moderator.<br/>
/// A bot moderator has either the Manage Server permission or is a member of the designated bot moderator role. /// A bot moderator has either the Manage Server permission or is a member of the designated bot moderator role.
/// </summary> /// </summary>
[Obsolete("Replace with appropriate DefaultMemberPermissionsAttribute")]
class RequireBotModeratorAttribute : PreconditionAttribute { class RequireBotModeratorAttribute : PreconditionAttribute {
public const string Error = "User did not pass the mod check."; 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 const string Reply = ":x: You must be a moderator to use this command.";

View file

@ -5,6 +5,7 @@ namespace BirthdayBot.ApplicationCommands;
/// Implements the included precondition from Discord.Net, requiring a guild context while using our custom error message.<br/><br/> /// Implements the included precondition from Discord.Net, requiring a guild context while using our custom error message.<br/><br/>
/// Combining this with <see cref="RequireBotModeratorAttribute"/> is redundant. If possible, only use the latter instead. /// Combining this with <see cref="RequireBotModeratorAttribute"/> is redundant. If possible, only use the latter instead.
/// </summary> /// </summary>
[Obsolete("Replace with EnableInDmAttribute")]
class RequireGuildContextAttribute : RequireContextAttribute { class RequireGuildContextAttribute : RequireContextAttribute {
public const string Error = "Command not received within a guild context."; 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 const string Reply = ":x: This command is only available within a server.";

View file

@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>3.4.6</Version> <Version>3.4.7</Version>
<Authors>NoiTheCat</Authors> <Authors>NoiTheCat</Authors>
</PropertyGroup> </PropertyGroup>

View file

@ -1,16 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BirthdayBot.Data;
[Table("banned_users")]
public class BlocklistEntry {
[Key]
public ulong GuildId { get; set; }
[Key]
public ulong UserId { get; set; }
[ForeignKey(nameof(GuildConfig.GuildId))]
[InverseProperty(nameof(GuildConfig.BlockedUsers))]
public GuildConfig Guild { get; set; } = null!;
}

View file

@ -17,7 +17,6 @@ public class BotDatabaseContext : DbContext {
}.ToString(); }.ToString();
} }
public DbSet<BlocklistEntry> BlocklistEntries { get; set; } = null!;
public DbSet<GuildConfig> GuildConfigurations { get; set; } = null!; public DbSet<GuildConfig> GuildConfigurations { get; set; } = null!;
public DbSet<UserEntry> UserEntries { get; set; } = null!; public DbSet<UserEntry> UserEntries { get; set; } = null!;
@ -27,17 +26,6 @@ public class BotDatabaseContext : DbContext {
.UseSnakeCaseNamingConvention(); .UseSnakeCaseNamingConvention();
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<BlocklistEntry>(entity => {
entity.HasKey(e => new { e.GuildId, e.UserId })
.HasName("banned_users_pkey");
entity.HasOne(d => d.Guild)
.WithMany(p => p.BlockedUsers)
.HasForeignKey(d => d.GuildId)
.HasConstraintName("banned_users_guild_id_fkey")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<GuildConfig>(entity => { modelBuilder.Entity<GuildConfig>(entity => {
entity.HasKey(e => e.GuildId) entity.HasKey(e => e.GuildId)
.HasName("settings_pkey"); .HasName("settings_pkey");

View file

@ -4,11 +4,6 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace BirthdayBot.Data; namespace BirthdayBot.Data;
[Table("settings")] [Table("settings")]
public class GuildConfig { public class GuildConfig {
public GuildConfig() {
BlockedUsers = new HashSet<BlocklistEntry>();
UserEntries = new HashSet<UserEntry>();
}
[Key] [Key]
public ulong GuildId { get; set; } public ulong GuildId { get; set; }
@ -23,6 +18,7 @@ public class GuildConfig {
public bool Moderated { get; set; } public bool Moderated { get; set; }
[Obsolete("To be removed when RequireBotModeratorAttribute is also removed")]
public ulong? ModeratorRole { get; set; } public ulong? ModeratorRole { get; set; }
public string? AnnounceMessage { get; set; } public string? AnnounceMessage { get; set; }
@ -33,10 +29,8 @@ public class GuildConfig {
public DateTimeOffset LastSeen { get; set; } public DateTimeOffset LastSeen { get; set; }
[InverseProperty(nameof(BlocklistEntry.Guild))]
public ICollection<BlocklistEntry> BlockedUsers { get; set; }
[InverseProperty(nameof(UserEntry.Guild))] [InverseProperty(nameof(UserEntry.Guild))]
public ICollection<UserEntry> UserEntries { get; set; } public ICollection<UserEntry> UserEntries { get; set; } = null!;
/// <summary> /// <summary>
/// Gets if this instance is new and does not (yet) exist in the database. /// Gets if this instance is new and does not (yet) exist in the database.

View file

@ -0,0 +1,131 @@
// <auto-generated />
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("20230117053251_RemoveBlocking")]
partial class RemoveBlocking
{
/// <inheritdoc />
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<decimal>("GuildId")
.HasColumnType("numeric(20,0)")
.HasColumnName("guild_id");
b.Property<string>("AnnounceMessage")
.HasColumnType("text")
.HasColumnName("announce_message");
b.Property<string>("AnnounceMessagePl")
.HasColumnType("text")
.HasColumnName("announce_message_pl");
b.Property<bool>("AnnouncePing")
.HasColumnType("boolean")
.HasColumnName("announce_ping");
b.Property<decimal?>("AnnouncementChannel")
.HasColumnType("numeric(20,0)")
.HasColumnName("channel_announce_id");
b.Property<decimal?>("BirthdayRole")
.HasColumnType("numeric(20,0)")
.HasColumnName("role_id");
b.Property<string>("GuildTimeZone")
.HasColumnType("text")
.HasColumnName("time_zone");
b.Property<DateTimeOffset>("LastSeen")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen")
.HasDefaultValueSql("now()");
b.Property<bool>("Moderated")
.HasColumnType("boolean")
.HasColumnName("moderated");
b.Property<decimal?>("ModeratorRole")
.HasColumnType("numeric(20,0)")
.HasColumnName("moderator_role");
b.HasKey("GuildId")
.HasName("settings_pkey");
b.ToTable("settings", (string)null);
});
modelBuilder.Entity("BirthdayBot.Data.UserEntry", b =>
{
b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)")
.HasColumnName("guild_id");
b.Property<decimal>("UserId")
.HasColumnType("numeric(20,0)")
.HasColumnName("user_id");
b.Property<int>("BirthDay")
.HasColumnType("integer")
.HasColumnName("birth_day");
b.Property<int>("BirthMonth")
.HasColumnType("integer")
.HasColumnName("birth_month");
b.Property<DateTimeOffset>("LastSeen")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen")
.HasDefaultValueSql("now()");
b.Property<string>("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
}
}
}

View file

@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BirthdayBot.Data.Migrations
{
/// <inheritdoc />
public partial class RemoveBlocking : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "banned_users");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "banned_users",
columns: table => new
{
guildid = table.Column<decimal>(name: "guild_id", type: "numeric(20,0)", nullable: false),
userid = table.Column<decimal>(name: "user_id", type: "numeric(20,0)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("banned_users_pkey", x => new { x.guildid, x.userid });
table.ForeignKey(
name: "banned_users_guild_id_fkey",
column: x => x.guildid,
principalTable: "settings",
principalColumn: "guild_id",
onDelete: ReferentialAction.Cascade);
});
}
}
}

View file

@ -22,22 +22,6 @@ namespace BirthdayBot.Data.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BirthdayBot.Data.BlocklistEntry", b =>
{
b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)")
.HasColumnName("guild_id");
b.Property<decimal>("UserId")
.HasColumnType("numeric(20,0)")
.HasColumnName("user_id");
b.HasKey("GuildId", "UserId")
.HasName("banned_users_pkey");
b.ToTable("banned_users", (string)null);
});
modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b => modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b =>
{ {
b.Property<decimal>("GuildId") b.Property<decimal>("GuildId")
@ -122,18 +106,6 @@ namespace BirthdayBot.Data.Migrations
b.ToTable("user_birthdays", (string)null); b.ToTable("user_birthdays", (string)null);
}); });
modelBuilder.Entity("BirthdayBot.Data.BlocklistEntry", b =>
{
b.HasOne("BirthdayBot.Data.GuildConfig", "Guild")
.WithMany("BlockedUsers")
.HasForeignKey("GuildId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("banned_users_guild_id_fkey");
b.Navigation("Guild");
});
modelBuilder.Entity("BirthdayBot.Data.UserEntry", b => modelBuilder.Entity("BirthdayBot.Data.UserEntry", b =>
{ {
b.HasOne("BirthdayBot.Data.GuildConfig", "Guild") b.HasOne("BirthdayBot.Data.GuildConfig", "Guild")
@ -148,8 +120,6 @@ namespace BirthdayBot.Data.Migrations
modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b => modelBuilder.Entity("BirthdayBot.Data.GuildConfig", b =>
{ {
b.Navigation("BlockedUsers");
b.Navigation("UserEntries"); b.Navigation("UserEntries");
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618

View file

@ -150,8 +150,6 @@ public sealed class ShardInstance : IDisposable {
if (result.Error == InteractionCommandError.UnmetPrecondition) { if (result.Error == InteractionCommandError.UnmetPrecondition) {
var errReply = result.ErrorReason switch { var errReply = result.ErrorReason switch {
RequireBotModeratorAttribute.Error => RequireBotModeratorAttribute.Reply, RequireBotModeratorAttribute.Error => RequireBotModeratorAttribute.Reply,
EnforceBlockingAttribute.FailBlocked => EnforceBlockingAttribute.ReplyBlocked,
EnforceBlockingAttribute.FailModerated => EnforceBlockingAttribute.ReplyModerated,
RequireGuildContextAttribute.Error => RequireGuildContextAttribute.Reply, RequireGuildContextAttribute.Error => RequireGuildContextAttribute.Reply,
_ => result.ErrorReason _ => result.ErrorReason
}; };