Allow admins to force nickname on particular member

This commit is contained in:
Grzegorz Gruszczyk 2019-10-30 01:19:45 +01:00 committed by Laxynium
parent 8ff3fc96ae
commit cc5de15fd6
7 changed files with 575 additions and 6 deletions

View File

@ -0,0 +1,89 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CompatBot.Commands.Attributes;
using CompatBot.Database;
using CompatBot.Utils;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
using Microsoft.EntityFrameworkCore;
namespace CompatBot.Commands
{
[Group("forced-nicknames"), RequiresWhitelistedRole]
[Description("Manage users who has forced nickname.")]
internal sealed class ForcedNicknames : BaseCommandModuleCustom
{
[Command("add")]
[Description("Enforces specific nickname for particular user.")]
public async Task Add(CommandContext ctx,
[Description("Discord user to add to forced nickname list.")] DiscordMember discordMember,
[Description("Nickname which should be displayed.")] string expectedNickname)
{
using (var context = new BotDb())
{
if (!(await context.ForcedNicknames.SingleOrDefaultAsync(x => x.UserId == discordMember.Id).ConfigureAwait(false) is null))
{
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{discordMember.Mention} is already on blacklist.").ConfigureAwait(false);
return;
}
context.ForcedNicknames.Add(
new ForcedNickname {UserId = discordMember.Id, Nickname = expectedNickname}
);
await context.SaveChangesAsync().ConfigureAwait(false);
await discordMember.ModifyAsync(x => x.Nickname = expectedNickname).ConfigureAwait(false);
await ctx.ReactWithAsync(Config.Reactions.Success,
$"{discordMember.Mention} was successfully added to blacklist!\n" +
$"Try using `{ctx.Prefix}help` to see new commands available to you"
).ConfigureAwait(false);
}
}
[Command("remove")]
[Description("Removes nickname restriction from particular user.")]
public async Task Remove(CommandContext ctx,
[Description("Discord user to remove from forced nickname list.")] DiscordMember discordMember)
{
using (var context = new BotDb())
{
var forcedNickname = await context.ForcedNicknames.SingleOrDefaultAsync(x => x.UserId == discordMember.Id).ConfigureAwait(false);
if (forcedNickname is null)
{
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{discordMember.Mention} is not on blacklist.").ConfigureAwait(false);
return;
}
context.ForcedNicknames.Remove(forcedNickname);
await context.SaveChangesAsync().ConfigureAwait(false);
await ctx.ReactWithAsync(Config.Reactions.Success,
$"{discordMember.Mention} was successfully removed from blacklist!\n" +
$"Try using `{ctx.Prefix}help` to see new commands available to you"
).ConfigureAwait(false);
}
}
[Command("list")]
[Description("Lists all users who has restricted nickname.")]
public async Task List(CommandContext ctx)
{
using (var context = new BotDb())
{
var forcedNicknames = await context.ForcedNicknames.AsNoTracking().ToListAsync().ConfigureAwait(false);
var displayString = forcedNicknames.Select(x =>
$"User: {ctx.Client.GetMember(x.UserId).Username}, forced nickname: {x.Nickname}")
.Aggregate(new StringBuilder(), (agg, x) => agg.AppendJoin("\n", x),x=>x.ToString());
if (string.IsNullOrEmpty(displayString))
displayString = "Not found any forced nicknames.";
await ctx.RespondAsync(displayString).ConfigureAwait(false);
}
}
}
}

View File

@ -19,7 +19,7 @@ namespace CompatBot.Database
public DbSet<Stats> Stats { get; set; }
public DbSet<Kot> Kot { get; set; }
public DbSet<Doggo> Doggo { get; set; }
public DbSet<ForcedNickname> ForcedNicknames { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dbPath = DbImporter.GetDbPath("bot.db", Environment.SpecialFolder.ApplicationData);
@ -45,6 +45,7 @@ namespace CompatBot.Database
modelBuilder.Entity<Stats>().HasIndex(s => new { s.Category, s.Key }).IsUnique().HasName("stats_category_key");
modelBuilder.Entity<Kot>().HasIndex(k => k.UserId).IsUnique().HasName("kot_user_id");
modelBuilder.Entity<Doggo>().HasIndex(d => d.UserId).IsUnique().HasName("doggo_user_id");
modelBuilder.Entity<ForcedNickname>().HasIndex(d => d.UserId).IsUnique().HasName("forced_nickname_user_id");
//configure default policy of Id being the primary key
modelBuilder.ConfigureDefaultPkConvention();
@ -173,4 +174,13 @@ namespace CompatBot.Database
public int Id { get; set; }
public ulong UserId { get; set; }
}
internal class ForcedNickname
{
public int Id { get; set; }
[Required]
public ulong UserId { set; get; }
[Required]
public string Nickname { get; set; }
}
}

View File

@ -0,0 +1,350 @@
// <auto-generated />
using System;
using CompatBot.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace CompatBot.Database.Migrations
{
[DbContext(typeof(BotDb))]
[Migration("20191031234946_AddForcedNickname")]
partial class AddForcedNickname
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("CompatBot.Database.BotState", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<string>("Key")
.HasColumnName("key");
b.Property<string>("Value")
.HasColumnName("value");
b.HasKey("Id")
.HasName("id");
b.HasIndex("Key")
.IsUnique()
.HasName("bot_state_key");
b.ToTable("bot_state");
});
modelBuilder.Entity("CompatBot.Database.DisabledCommand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<string>("Command")
.IsRequired()
.HasColumnName("command");
b.HasKey("Id")
.HasName("id");
b.HasIndex("Command")
.IsUnique()
.HasName("disabled_command_command");
b.ToTable("disabled_commands");
});
modelBuilder.Entity("CompatBot.Database.Doggo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<ulong>("UserId")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("id");
b.HasIndex("UserId")
.IsUnique()
.HasName("doggo_user_id");
b.ToTable("doggo");
});
modelBuilder.Entity("CompatBot.Database.EventSchedule", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<long>("End")
.HasColumnName("end");
b.Property<string>("EventName")
.HasColumnName("event_name");
b.Property<string>("Name")
.HasColumnName("name");
b.Property<long>("Start")
.HasColumnName("start");
b.Property<int>("Year")
.HasColumnName("year");
b.HasKey("Id")
.HasName("id");
b.HasIndex("Year", "EventName")
.HasName("event_schedule_year_event_name");
b.ToTable("event_schedule");
});
modelBuilder.Entity("CompatBot.Database.Explanation", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<byte[]>("Attachment")
.HasColumnName("attachment")
.HasMaxLength(7340032);
b.Property<string>("AttachmentFilename")
.HasColumnName("attachment_filename");
b.Property<string>("Keyword")
.IsRequired()
.HasColumnName("keyword");
b.Property<string>("Text")
.IsRequired()
.HasColumnName("text");
b.HasKey("Id")
.HasName("id");
b.HasIndex("Keyword")
.IsUnique()
.HasName("explanation_keyword");
b.ToTable("explanation");
});
modelBuilder.Entity("CompatBot.Database.ForcedNickname", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<string>("Nickname")
.IsRequired()
.HasColumnName("nickname");
b.Property<ulong>("UserId")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("id");
b.HasIndex("UserId")
.IsUnique()
.HasName("blacklisted_user_user_id");
b.ToTable("forced_nicknames");
});
modelBuilder.Entity("CompatBot.Database.Kot", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<ulong>("UserId")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("id");
b.HasIndex("UserId")
.IsUnique()
.HasName("kot_user_id");
b.ToTable("kot");
});
modelBuilder.Entity("CompatBot.Database.Moderator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<ulong>("DiscordId")
.HasColumnName("discord_id");
b.Property<bool>("Sudoer")
.HasColumnName("sudoer");
b.HasKey("Id")
.HasName("id");
b.HasIndex("DiscordId")
.IsUnique()
.HasName("moderator_discord_id");
b.ToTable("moderator");
});
modelBuilder.Entity("CompatBot.Database.Piracystring", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<int>("Actions")
.ValueGeneratedOnAdd()
.HasColumnName("actions")
.HasDefaultValue(11);
b.Property<byte>("Context")
.ValueGeneratedOnAdd()
.HasColumnName("context")
.HasDefaultValue((byte)3);
b.Property<string>("CustomMessage")
.HasColumnName("custom_message");
b.Property<bool>("Disabled")
.HasColumnName("disabled");
b.Property<string>("ExplainTerm")
.HasColumnName("explain_term");
b.Property<string>("String")
.IsRequired()
.HasColumnName("string")
.HasColumnType("varchar(255)");
b.Property<string>("ValidatingRegex")
.HasColumnName("validating_regex");
b.HasKey("Id")
.HasName("id");
b.HasIndex("String")
.HasName("piracystring_string");
b.ToTable("piracystring");
});
modelBuilder.Entity("CompatBot.Database.Stats", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<string>("Category")
.IsRequired()
.HasColumnName("category");
b.Property<long>("ExpirationTimestamp")
.HasColumnName("expiration_timestamp");
b.Property<string>("Key")
.IsRequired()
.HasColumnName("key");
b.Property<int>("Value")
.HasColumnName("value");
b.HasKey("Id")
.HasName("id");
b.HasIndex("Category", "Key")
.IsUnique()
.HasName("stats_category_key");
b.ToTable("stats");
});
modelBuilder.Entity("CompatBot.Database.Warning", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<ulong>("DiscordId")
.HasColumnName("discord_id");
b.Property<string>("FullReason")
.IsRequired()
.HasColumnName("full_reason");
b.Property<ulong>("IssuerId")
.HasColumnName("issuer_id");
b.Property<string>("Reason")
.IsRequired()
.HasColumnName("reason");
b.Property<bool>("Retracted")
.HasColumnName("retracted");
b.Property<ulong?>("RetractedBy")
.HasColumnName("retracted_by");
b.Property<string>("RetractionReason")
.HasColumnName("retraction_reason");
b.Property<long?>("RetractionTimestamp")
.HasColumnName("retraction_timestamp");
b.Property<long?>("Timestamp")
.HasColumnName("timestamp");
b.HasKey("Id")
.HasName("id");
b.HasIndex("DiscordId")
.HasName("warning_discord_id");
b.ToTable("warning");
});
modelBuilder.Entity("CompatBot.Database.WhitelistedInvite", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<ulong>("GuildId")
.HasColumnName("guild_id");
b.Property<string>("InviteCode")
.HasColumnName("invite_code");
b.Property<string>("Name")
.HasColumnName("name");
b.HasKey("Id")
.HasName("id");
b.HasIndex("GuildId")
.IsUnique()
.HasName("whitelisted_invite_guild_id");
b.ToTable("whitelisted_invites");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace CompatBot.Database.Migrations
{
public partial class AddForcedNickname : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "forced_nicknames",
columns: table => new
{
id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
user_id = table.Column<ulong>(nullable: false),
nickname = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("id", x => x.id);
});
migrationBuilder.CreateIndex(
name: "blacklisted_user_user_id",
table: "forced_nicknames",
column: "user_id",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "forced_nicknames");
}
}
}

View File

@ -138,6 +138,29 @@ namespace CompatBot.Database.Migrations
b.ToTable("explanation");
});
modelBuilder.Entity("CompatBot.Database.ForcedNickname", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnName("id");
b.Property<string>("Nickname")
.IsRequired()
.HasColumnName("nickname");
b.Property<ulong>("UserId")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("id");
b.HasIndex("UserId")
.IsUnique()
.HasName("blacklisted_user_user_id");
b.ToTable("forced_nicknames");
});
modelBuilder.Entity("CompatBot.Database.Kot", b =>
{
b.Property<int>("Id")

View File

@ -0,0 +1,59 @@
using System;
using System.Threading.Tasks;
using CompatBot.Database;
using CompatBot.Utils;
using DSharpPlus;
using DSharpPlus.Entities;
using DSharpPlus.EventArgs;
using Microsoft.EntityFrameworkCore;
namespace CompatBot.EventHandlers
{
public static class UsernameValidationMonitor
{
public static async Task OnUserUpdated(UserUpdateEventArgs args)
{
await UpdateDisplayName(args.Client, () => args.Client.GetMember(args.UserAfter)).ConfigureAwait(false);
}
public static async Task OnMemberUpdated(GuildMemberUpdateEventArgs args)
{
await UpdateDisplayName(args.Client, () => args.Member).ConfigureAwait(false);
}
public static async Task OnMemberAdded(GuildMemberAddEventArgs args)
{
await UpdateDisplayName(args.Client, () => args.Member).ConfigureAwait(false);
}
private static async Task UpdateDisplayName(DiscordClient client, Func<DiscordMember> getGuildMember)
{
try
{
using (var context = new BotDb())
{
var guildMember = getGuildMember();
var forcedNickname = await context.ForcedNicknames.FirstOrDefaultAsync(x => x.UserId == guildMember.Id).ConfigureAwait(false);
if (forcedNickname is null)
return;
if (guildMember.DisplayName == forcedNickname.Nickname)
return;
await guildMember.ModifyAsync(x => x.Nickname = forcedNickname.Nickname).ConfigureAwait(false);
await client.ReportAsync(
"User nickname was changed.",
$"",
null,
ReportSeverity.Low
).ConfigureAwait(false);
}
}
catch (Exception e)
{
Config.Log.Error(e);
}
}
}
}

View File

@ -223,12 +223,14 @@ namespace CompatBot
client.UserUpdated += UsernameSpoofMonitor.OnUserUpdated;
client.UserUpdated += UsernameZalgoMonitor.OnUserUpdated;
client.GuildMemberAdded += Greeter.OnMemberAdded;
client.GuildMemberAdded += UsernameSpoofMonitor.OnMemberAdded;
client.GuildMemberAdded += UsernameZalgoMonitor.OnMemberAdded;
client.GuildMemberAdded += Greeter.OnMemberAdded;
client.GuildMemberAdded += UsernameSpoofMonitor.OnMemberAdded;
client.GuildMemberAdded += UsernameZalgoMonitor.OnMemberAdded;
client.GuildMemberAdded += UsernameValidationMonitor.OnMemberAdded;
client.GuildMemberUpdated += UsernameSpoofMonitor.OnMemberUpdated;
client.GuildMemberUpdated += UsernameZalgoMonitor.OnMemberUpdated;
client.GuildMemberUpdated += UsernameSpoofMonitor.OnMemberUpdated;
client.GuildMemberUpdated += UsernameZalgoMonitor.OnMemberUpdated;
client.GuildMemberUpdated += UsernameValidationMonitor.OnMemberUpdated;
client.DebugLogger.LogMessageReceived += (sender, eventArgs) =>
{