Adjust names of commands

Check permissions before changing nickname
Display table instead of raw text in list command
Add periodic check if forced nicknames are still valid.
Add GuildId in order to not list forced nicknames added on different guild.
This commit is contained in:
Laxynium
2019-11-16 12:13:23 +01:00
parent cc5de15fd6
commit e04c4548a0
8 changed files with 186 additions and 104 deletions

View File

@@ -1,9 +1,10 @@
using System.Linq;
using System.Text;
using System;
using System.Linq;
using System.Threading.Tasks;
using CompatBot.Commands.Attributes;
using CompatBot.Database;
using CompatBot.Utils;
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
@@ -11,59 +12,85 @@ using Microsoft.EntityFrameworkCore;
namespace CompatBot.Commands
{
[Group("forced-nicknames"), RequiresWhitelistedRole]
[Group("rename"), RequiresBotModRole]
[Description("Manage users who has forced nickname.")]
internal sealed class ForcedNicknames : BaseCommandModuleCustom
{
[Command("add")]
[GroupCommand]
[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())
try
{
if (!(await context.ForcedNicknames.SingleOrDefaultAsync(x => x.UserId == discordMember.Id).ConfigureAwait(false) is null))
using (var context = new BotDb())
{
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{discordMember.Mention} is already on blacklist.").ConfigureAwait(false);
return;
var forcedNickname = context.ForcedNicknames.SingleOrDefault(x => x.UserId == discordMember.Id && x.GuildId == discordMember.Guild.Id);
if (forcedNickname is {})
{
await ChangeNickname(discordMember, expectedNickname, forcedNickname, context,ctx);
await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false);
return;
}
context.ForcedNicknames.Add(
new ForcedNickname {UserId = discordMember.Id, GuildId = discordMember.Guild.Id, Nickname = expectedNickname}
);
await context.SaveChangesAsync().ConfigureAwait(false);
await discordMember.ModifyAsync(x => x.Nickname = expectedNickname).ConfigureAwait(false);
await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false);
}
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);
}
catch (Exception)
{
await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);
throw;
}
}
[Command("remove")]
private static async Task ChangeNickname(DiscordMember discordMember, string expectedNickname,
ForcedNickname forcedNickname, BotDb context, CommandContext ctx)
{
if (forcedNickname.Nickname == expectedNickname)
return;
forcedNickname.Nickname = expectedNickname;
await context.SaveChangesAsync().ConfigureAwait(false);
await discordMember.ModifyAsync(x => x.Nickname = expectedNickname).ConfigureAwait(false);
}
[Command("clear")]
[Aliases("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())
try
{
var forcedNickname = await context.ForcedNicknames.SingleOrDefaultAsync(x => x.UserId == discordMember.Id).ConfigureAwait(false);
if (forcedNickname is null)
using (var context = new BotDb())
{
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{discordMember.Mention} is not on blacklist.").ConfigureAwait(false);
return;
var forcedNickname = context.ForcedNicknames.SingleOrDefault(x => x.UserId == discordMember.Id && x.GuildId == discordMember.Guild.Id);
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).ConfigureAwait(false);
}
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);
}
catch (Exception)
{
await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);
throw;
}
}
@@ -73,16 +100,21 @@ namespace CompatBot.Commands
{
using (var context = new BotDb())
{
var forcedNicknames = await context.ForcedNicknames.AsNoTracking().ToListAsync().ConfigureAwait(false);
var forcedNicknames = await context.ForcedNicknames.AsNoTracking().Where(x=>x.GuildId == ctx.Guild.Id).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());
var table = new AsciiTable(
new AsciiColumn("ID", !ctx.Channel.IsPrivate),
new AsciiColumn("Username", maxWidth: 15),
new AsciiColumn("Forced nickname")
);
if (string.IsNullOrEmpty(displayString))
displayString = "Not found any forced nicknames.";
foreach (var forcedNickname in forcedNicknames)
{
var username = await ctx.GetUserNameAsync(forcedNickname.UserId).ConfigureAwait(false);
table.Add(forcedNickname.UserId.ToString(), username, forcedNickname.Nickname);
}
await ctx.RespondAsync(displayString).ConfigureAwait(false);
await ctx.RespondAsync(table.ToString()).ConfigureAwait(false);
}
}
}

View File

@@ -54,7 +54,7 @@ namespace CompatBot
public static int BuildNumberDifferenceForOutdatedBuilds => config.GetValue(nameof(BuildNumberDifferenceForOutdatedBuilds), 10);
public static int MinimumPiracyTriggerLength => config.GetValue(nameof(MinimumPiracyTriggerLength), 4);
public static int MaxSyscallResultLines => config.GetValue(nameof(MaxSyscallResultLines), 13);
public static int ForcedNicknamesRecheckTimeInSeconds => config.GetValue(nameof(ForcedNicknamesRecheckTimeInSeconds), 3 * 60 * 60);
public static string Token => config.GetValue(nameof(Token), "");
public static string LogPath => config.GetValue(nameof(LogPath), "./logs/"); // paths are relative to the working directory
public static string IrdCachePath => config.GetValue(nameof(IrdCachePath), "./ird/");

View File

@@ -45,7 +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");
modelBuilder.Entity<ForcedNickname>().HasIndex(d => new { d.UserId, d.GuildId }).IsUnique().HasName("forced_nickname_user_id_guild_id");
//configure default policy of Id being the primary key
modelBuilder.ConfigureDefaultPkConvention();
@@ -178,8 +178,8 @@ namespace CompatBot.Database
internal class ForcedNickname
{
public int Id { get; set; }
[Required]
public ulong UserId { set; get; }
public ulong GuildId { set; get; }
[Required]
public string Nickname { get; set; }
}

View File

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

View File

@@ -13,7 +13,8 @@ namespace CompatBot.Database.Migrations
id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
user_id = table.Column<ulong>(nullable: false),
nickname = table.Column<string>(nullable: false)
guild_id = table.Column<ulong>(nullable: false),
nickname = table.Column<string>(nullable: true)
},
constraints: table =>
{
@@ -21,9 +22,9 @@ namespace CompatBot.Database.Migrations
});
migrationBuilder.CreateIndex(
name: "blacklisted_user_user_id",
name: "forced_nickname_user_id_guild_id",
table: "forced_nicknames",
column: "user_id",
columns: new[] { "user_id", "guild_id" },
unique: true);
}

View File

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

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CompatBot.Database;
using CompatBot.Utils;
@@ -11,29 +13,26 @@ 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);
}
await UpdateDisplayName(args.Guild.CurrentMember, args.Member).ConfigureAwait(false);
}
public static async Task OnMemberAdded(GuildMemberAddEventArgs args)
{
await UpdateDisplayName(args.Client, () => args.Member).ConfigureAwait(false);
await UpdateDisplayName(args.Guild.CurrentMember, args.Member).ConfigureAwait(false);
}
private static async Task UpdateDisplayName(DiscordClient client, Func<DiscordMember> getGuildMember)
private static async Task UpdateDisplayName(DiscordMember bot, DiscordMember guildMember)
{
try
{
if (guildMember.IsWhitelisted())
return;
using (var context = new BotDb())
{
var guildMember = getGuildMember();
var forcedNickname = await context.ForcedNicknames.FirstOrDefaultAsync(x => x.UserId == guildMember.Id).ConfigureAwait(false);
var forcedNickname = await context.ForcedNicknames.FirstOrDefaultAsync(x => x.UserId == guildMember.Id && x.GuildId == guildMember.Guild.Id).ConfigureAwait(false);
if (forcedNickname is null)
return;
@@ -41,13 +40,6 @@ namespace CompatBot.EventHandlers
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)
@@ -55,5 +47,52 @@ namespace CompatBot.EventHandlers
Config.Log.Error(e);
}
}
private static readonly IList<DiscordGuild> AvailableGuilds = new List<DiscordGuild>();
public static void SetupGuilds(IEnumerable<DiscordGuild>guilds)
{
foreach (var discordGuild in guilds)
{
AvailableGuilds.Add(discordGuild);
}
}
public static async Task MonitorAsync()
{
while (!Config.Cts.IsCancellationRequested)
{
if (!AvailableGuilds.Any())
{
await Task.Delay(TimeSpan.FromSeconds(3), Config.Cts.Token).ConfigureAwait(false);
continue;
}
foreach (var guild in AvailableGuilds)
{
try
{
using (var context = new BotDb())
{
var forcedNicknames = await context.ForcedNicknames.Where(x=>x.GuildId == guild.Id).ToDictionaryAsync(x => x.UserId).ConfigureAwait(false);
var membersToUpdate = guild.Members.Where(x => forcedNicknames.ContainsKey(x.Key))
.Select(x => (discordMember: x.Value, forcedNickname: forcedNicknames[x.Key]))
.Where(x => x.discordMember.DisplayName != x.forcedNickname.Nickname)
.ToList();
foreach (var (discordMember, forcedNickname) in membersToUpdate)
{
await discordMember.ModifyAsync(x => x.Nickname = forcedNickname.Nickname).ConfigureAwait(false);
}
}
}
catch (Exception e)
{
Config.Log.Error(e);
}
}
await Task.Delay(TimeSpan.FromSeconds(Config.ForcedNicknamesRecheckTimeInSeconds), Config.Cts.Token).ConfigureAwait(false);
}
}
}
}

View File

@@ -190,6 +190,11 @@ namespace CompatBot
}
Config.Log.Info($"All moderation backlogs checked in {gaArgs.Guild.Name}.");
};
client.GuildDownloadCompleted += async gdcArgs =>
{
UsernameValidationMonitor.SetupGuilds(gdcArgs.Guilds.Select(x => x.Value).ToList());
await Task.CompletedTask;
};
client.GuildUnavailable += guArgs =>
{
Config.Log.Warn($"{guArgs.Guild.Name} is unavailable");
@@ -310,13 +315,14 @@ namespace CompatBot
Config.Log.Debug("Args: " + string.Join(" ", pArgs));
}
Config.Log.Debug("Running RPCS3 update check thread");
backgroundTasks = Task.WhenAll(
backgroundTasks,
NewBuildsMonitor.MonitorAsync(client),
Watchdog.Watch(client),
InviteWhitelistProvider.CleanupAsync(client)
);
Config.Log.Debug("Running RPCS3 update check thread");
backgroundTasks = Task.WhenAll(
backgroundTasks,
NewBuildsMonitor.MonitorAsync(client),
Watchdog.Watch(client),
InviteWhitelistProvider.CleanupAsync(client),
UsernameValidationMonitor.MonitorAsync()
);
while (!Config.Cts.IsCancellationRequested)
{