added .masskick to complement massban and masskill

This commit is contained in:
Toastie 2025-03-18 13:44:23 +13:00
parent ca46786c5e
commit 8025b9f9fe
Signed by: toastie_t0ast
GPG key ID: 74226CF45EEE5AAF
3 changed files with 197 additions and 157 deletions
src/EllieBot
Modules/Administration/UserPunish
strings

View file

@ -44,12 +44,12 @@ public partial class Administration
try try
{ {
await _sender.Response(user) await _sender.Response(user)
.Embed(CreateEmbed() .Embed(CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString()))) .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString()) .AddField(GetText(strs.moderator), ctx.User.ToString())
.AddField(GetText(strs.reason), reason ?? "-")) .AddField(GetText(strs.reason), reason ?? "-"))
.SendAsync(); .SendAsync();
} }
catch catch
{ {
@ -65,8 +65,8 @@ public partial class Administration
{ {
Log.Warning(ex, "Exception occured while warning a user"); Log.Warning(ex, "Exception occured while warning a user");
var errorEmbed = CreateEmbed() var errorEmbed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.cant_apply_punishment)); .WithDescription(GetText(strs.cant_apply_punishment));
if (dmFailed) if (dmFailed)
errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -177,45 +177,45 @@ public partial class Administration
var allWarnings = _service.UserWarnings(ctx.Guild.Id, userId); var allWarnings = _service.UserWarnings(ctx.Guild.Id, userId);
await Response() await Response()
.Paginated() .Paginated()
.Items(allWarnings) .Items(allWarnings)
.PageSize(9) .PageSize(9)
.CurrentPage(inputPage) .CurrentPage(inputPage)
.Page((warnings, page) => .Page((warnings, page) =>
{ {
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString(); var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user))); var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
if (!warnings.Any()) if (!warnings.Any())
embed.WithDescription(GetText(strs.warnings_none)); embed.WithDescription(GetText(strs.warnings_none));
else else
{ {
var descText = GetText(strs.warn_count( var descText = GetText(strs.warn_count(
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()), Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
Format.Bold(warnings.Sum(x => x.Weight).ToString()))); Format.Bold(warnings.Sum(x => x.Weight).ToString())));
embed.WithDescription(descText); embed.WithDescription(descText);
var i = page * 9; var i = page * 9;
foreach (var w in warnings) foreach (var w in warnings)
{ {
i++; i++;
var name = GetText(strs.warned_on_by(w.DateAdded?.ToString("dd.MM.yyy"), var name = GetText(strs.warned_on_by(w.DateAdded?.ToString("dd.MM.yyy"),
w.DateAdded?.ToString("HH:mm"), w.DateAdded?.ToString("HH:mm"),
w.Moderator)); w.Moderator));
if (w.Forgiven) if (w.Forgiven)
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}"; name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
embed.AddField($"#`{i}` " + name, embed.AddField($"#`{i}` " + name,
Format.Code(GetText(strs.warn_weight(w.Weight))) + '\n' + w.Reason.TrimTo(1000)); Format.Code(GetText(strs.warn_weight(w.Weight))) + '\n' + w.Reason.TrimTo(1000));
} }
} }
return embed; return embed;
}) })
.SendAsync(); .SendAsync();
} }
[Cmd] [Cmd]
@ -228,29 +228,29 @@ public partial class Administration
var allWarnings = _service.WarnlogAll(ctx.Guild.Id); var allWarnings = _service.WarnlogAll(ctx.Guild.Id);
await Response() await Response()
.Paginated() .Paginated()
.Items(allWarnings) .Items(allWarnings)
.PageSize(15) .PageSize(15)
.CurrentPage(page) .CurrentPage(page)
.Page((warnings, _) => .Page((warnings, _) =>
{ {
var ws = warnings var ws = warnings
.Select(x => .Select(x =>
{ {
var all = x.Count(); var all = x.Count();
var forgiven = x.Count(y => y.Forgiven); var forgiven = x.Count(y => y.Forgiven);
var total = all - forgiven; var total = all - forgiven;
var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key); var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key);
return (usr?.ToString() ?? x.Key.ToString()) return (usr?.ToString() ?? x.Key.ToString())
+ $" | {total} ({all} - {forgiven})"; + $" | {total} ({all} - {forgiven})";
}); });
return CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.warnings_list)) .WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws)); .WithDescription(string.Join("\n", ws));
}) })
.SendAsync(); .SendAsync();
} }
[Cmd] [Cmd]
@ -332,17 +332,17 @@ public partial class Administration
if (timespan is null) if (timespan is null)
{ {
await Response() await Response()
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()), .Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()))) Format.Bold(number.ToString())))
.SendAsync(); .SendAsync();
} }
else else
{ {
await Response() await Response()
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()), .Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()), Format.Bold(number.ToString()),
Format.Bold(timespan.Input))) Format.Bold(timespan.Input)))
.SendAsync(); .SendAsync();
} }
} }
@ -368,17 +368,17 @@ public partial class Administration
if (timespan is null) if (timespan is null)
{ {
await Response() await Response()
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()), .Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()))) Format.Bold(number.ToString())))
.SendAsync(); .SendAsync();
} }
else else
{ {
await Response() await Response()
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()), .Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()), Format.Bold(number.ToString()),
Format.Bold(timespan.Input))) Format.Bold(timespan.Input)))
.SendAsync(); .SendAsync();
} }
} }
@ -458,13 +458,13 @@ public partial class Administration
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await _mute.TimedBan(ctx.Guild, userId, timespan.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune); await _mute.TimedBan(ctx.Guild, userId, timespan.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true) .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
.AddField("ID", userId.ToString(), true) .AddField("ID", userId.ToString(), true)
.AddField(GetText(strs.duration), .AddField(GetText(strs.duration),
timespan.Time.ToPrettyStringHm(), timespan.Time.ToPrettyStringHm(),
true); true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -486,11 +486,11 @@ public partial class Administration
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
await Response() await Response()
.Embed(CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField("ID", userId.ToString(), true)) .AddField("ID", userId.ToString(), true))
.SendAsync(); .SendAsync();
} }
else else
await Ban(user, msg); await Ban(user, msg);
@ -524,10 +524,10 @@ public partial class Administration
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -704,9 +704,9 @@ public partial class Administration
try try
{ {
await Response() await Response()
.Channel(await user.CreateDMChannelAsync()) .Channel(await user.CreateDMChannelAsync())
.Error(strs.sbdm(Format.Bold(ctx.Guild.Name), msg)) .Error(strs.sbdm(Format.Bold(ctx.Guild.Name), msg))
.SendAsync(); .SendAsync();
} }
catch catch
{ {
@ -717,18 +717,17 @@ public partial class Administration
await ctx.Guild.AddBanAsync(user, banPrune, ("Softban | " + ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, ("Softban | " + ctx.User + " | " + msg).TrimTo(512));
try try
{ {
await ctx.Guild.RemoveBanAsync(user); await ctx.Guild.RemoveBanAsync(user);
} }
catch catch
{ {
} }
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user)) .WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -768,9 +767,9 @@ public partial class Administration
try try
{ {
await Response() await Response()
.Channel(await user.CreateDMChannelAsync()) .Channel(await user.CreateDMChannelAsync())
.Error(GetText(strs.kickdm(Format.Bold(ctx.Guild.Name), msg))) .Error(GetText(strs.kickdm(Format.Bold(ctx.Guild.Name), msg)))
.SendAsync(); .SendAsync();
} }
catch catch
{ {
@ -780,10 +779,10 @@ public partial class Administration
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512)); await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.kicked_user)) .WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -812,10 +811,10 @@ public partial class Administration
{ {
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg)); var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
await _sender.Response(user) await _sender.Response(user)
.Embed(CreateEmbed() .Embed(CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(dmMessage)) .WithDescription(dmMessage))
.SendAsync(); .SendAsync();
} }
catch catch
{ {
@ -825,10 +824,10 @@ public partial class Administration
await user.SetTimeOutAsync(timespan.Time); await user.SetTimeOutAsync(timespan.Time);
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⏳ " + GetText(strs.timedout_user)) .WithTitle("⏳ " + GetText(strs.timedout_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true); .AddField("ID", user.Id.ToString(), true);
if (dmFailed) if (dmFailed)
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user)); toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
@ -836,21 +835,38 @@ public partial class Administration
await Response().Embed(toSend).SendAsync(); await Response().Embed(toSend).SendAsync();
} }
public enum PunishType
{
Kick,
Ban
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.KickMembers)]
[BotPerm(GuildPerm.KickMembers)]
[Ratelimit(30)]
public async Task MassKick(params string[] users)
=> await MassPunish(PunishType.Ban, users);
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)] [UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)] [BotPerm(GuildPerm.BanMembers)]
[Ratelimit(30)] [Ratelimit(30)]
public async Task MassBan(params string[] userStrings) public async Task MassBan(params string[] users)
=> await MassPunish(PunishType.Ban, users);
private async Task MassPunish(PunishType type, params string[] users)
{ {
if (userStrings.Length == 0) if (users.Length == 0)
return; return;
var missing = new List<string>(); var missing = new List<string>();
var banning = new HashSet<IUser>(); var punishing = new HashSet<IUser>();
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
foreach (var userStr in userStrings) foreach (var userStr in users)
{ {
if (ulong.TryParse(userStr, out var userId)) if (ulong.TryParse(userStr, out var userId))
{ {
@ -875,7 +891,7 @@ public partial class Administration
if (user is IGuildUser gu && !await CheckRoleHierarchy(gu)) if (user is IGuildUser gu && !await CheckRoleHierarchy(gu))
return; return;
banning.Add(user); punishing.Add(user);
} }
else else
missing.Add(userStr); missing.Add(userStr);
@ -886,33 +902,48 @@ public partial class Administration
missStr = "-"; missStr = "-";
var toSend = CreateEmbed() var toSend = CreateEmbed()
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count))) .WithDescription(GetText(strs.mass_ban_in_progress(punishing.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr) .AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor(); .WithPendingColor();
var banningMessage = await Response().Embed(toSend).SendAsync(); var banningMessage = await Response().Embed(toSend).SendAsync();
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
foreach (var toBan in banning)
foreach (var toPunish in punishing)
{ {
try if (type == PunishType.Kick)
{ {
await ctx.Guild.AddBanAsync(toBan.Id, banPrune, $"{ctx.User} | Massban"); try
{
if (toPunish is IGuildUser u)
await u.KickAsync($"{ctx.User} | Masskick");
}
catch (Exception ex)
{
Log.Warning(ex, "Error kicking {User} user in {GuildId} server", toPunish.Id, ctx.Guild.Id);
}
} }
catch (Exception ex) else
{ {
Log.Warning(ex, "Error banning {User} user in {GuildId} server", toBan.Id, ctx.Guild.Id); try
{
await ctx.Guild.AddBanAsync(toPunish.Id, banPrune, $"{ctx.User} | Massban");
}
catch (Exception ex)
{
Log.Warning(ex, "Error banning {User} user in {GuildId} server", toPunish.Id, ctx.Guild.Id);
}
} }
} }
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed() await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_ban_completed( GetText(strs.mass_ban_completed(
banning.Count()))) punishing.Count())))
.AddField(GetText(strs.invalid(missing.Count)), .AddField(GetText(strs.invalid(missing.Count)),
missStr) missStr)
.WithOkColor() .WithOkColor()
.Build()); .Build());
} }
[Cmd] [Cmd]
@ -933,33 +964,33 @@ public partial class Administration
//send a message but don't wait for it //send a message but don't wait for it
var banningMessageTask = Response() var banningMessageTask = Response()
.Embed(CreateEmbed() .Embed(CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_in_progress(bans.Count()))) GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)
.WithPendingColor()) .WithPendingColor())
.SendAsync(); .SendAsync();
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
//do the banning //do the banning
await Task.WhenAll(bans.Where(x => x.Id.HasValue) await Task.WhenAll(bans.Where(x => x.Id.HasValue)
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value, .Select(x => ctx.Guild.AddBanAsync(x.Id.Value,
banPrune, banPrune,
x.Reason, x.Reason,
new() new()
{ {
RetryMode = RetryMode.AlwaysRetry RetryMode = RetryMode.AlwaysRetry
}))); })));
//wait for the message and edit it //wait for the message and edit it
var banningMessage = await banningMessageTask; var banningMessage = await banningMessageTask;
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed() await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_completed(bans.Count()))) GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)
.WithOkColor() .WithOkColor()
.Build()); .Build());
} }
public class WarnExpireOptions : IEllieCommandOptions public class WarnExpireOptions : IEllieCommandOptions

View file

@ -1220,6 +1220,8 @@ crypto:
- c - c
massban: massban:
- massban - massban
masskick:
- masskick
masskill: masskill:
- masskill - masskill
reroadd: reroadd:

View file

@ -3952,13 +3952,20 @@ stock:
params: params:
- query: - query:
desc: "The ticker symbol or company name used to retrieve the stock's information." desc: "The ticker symbol or company name used to retrieve the stock's information."
masskick:
desc: Kicks multiple users at once. Specify a space separated list of IDs of users who you wish to kick.
ex:
- 123123123 3333333333 444444444
params:
- users:
desc: "The list of user IDs to kick. Whitespace-separated string."
massban: massban:
desc: Bans multiple users at once. Specify a space separated list of IDs of users who you wish to ban. desc: Bans multiple users at once. Specify a space separated list of IDs of users who you wish to ban.
ex: ex:
- 123123123 3333333333 444444444 - 123123123 3333333333 444444444
params: params:
- userStrings: - users:
desc: "The list of user IDs to ban, provided as a comma-separated string." desc: "The list of user IDs to ban. Whitespace-separated string."
masskill: masskill:
desc: Specify a new-line separated list of `userid reason`. You can use Username#discrim instead of UserId. Specified users will be banned from the current server, blacklisted from the bot, and have all of their currency taken away. desc: Specify a new-line separated list of `userid reason`. You can use Username#discrim instead of UserId. Specified users will be banned from the current server, blacklisted from the bot, and have all of their currency taken away.
ex: ex: