mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
fix(ModActions): don't create duplicate logs/cases for massbans; add more code comments
This commit is contained in:
parent
aac3ac7dd8
commit
86f7d268db
2 changed files with 46 additions and 105 deletions
|
@ -36,13 +36,13 @@ export class GuildLogs extends EventEmitter {
|
||||||
this.emit("log", { type, data });
|
this.emit("log", { type, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreLog(type: LogType, ignoreId: any, expiryTime: number = null) {
|
ignoreLog(type: LogType, ignoreId: any, timeout: number = null) {
|
||||||
this.ignoredLogs.push({ type, ignoreId });
|
this.ignoredLogs.push({ type, ignoreId });
|
||||||
|
|
||||||
// Clear after expiry (15sec by default)
|
// Clear after expiry (15sec by default)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.clearIgnoredLog(type, ignoreId);
|
this.clearIgnoredLog(type, ignoreId);
|
||||||
}, expiryTime || 1000 * 15);
|
}, timeout || 1000 * 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
isLogIgnored(type: LogType, ignoreId: any) {
|
isLogIgnored(type: LogType, ignoreId: any) {
|
||||||
|
@ -50,9 +50,6 @@ export class GuildLogs extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIgnoredLog(type: LogType, ignoreId: any) {
|
clearIgnoredLog(type: LogType, ignoreId: any) {
|
||||||
this.ignoredLogs.splice(
|
this.ignoredLogs.splice(this.ignoredLogs.findIndex(info => type === info.type && ignoreId === info.ignoreId), 1);
|
||||||
this.ignoredLogs.findIndex(info => type === info.type && ignoreId === info.ignoreId),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,13 +118,13 @@ export class ModActionsPlugin extends Plugin {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreEvent(type: IgnoredEventType, userId: any) {
|
ignoreEvent(type: IgnoredEventType, userId: any, timeout: number = null) {
|
||||||
this.ignoredEvents.push({ type, userId });
|
this.ignoredEvents.push({ type, userId });
|
||||||
|
|
||||||
// Clear after expiry (15sec by default)
|
// Clear after expiry (15sec by default)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.clearIgnoredEvent(type, userId);
|
this.clearIgnoredEvent(type, userId);
|
||||||
}, 1000 * 15);
|
}, timeout || 1000 * 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
isEventIgnored(type: IgnoredEventType, userId: any) {
|
isEventIgnored(type: IgnoredEventType, userId: any) {
|
||||||
|
@ -132,10 +132,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIgnoredEvent(type: IgnoredEventType, userId: any) {
|
clearIgnoredEvent(type: IgnoredEventType, userId: any) {
|
||||||
this.ignoredEvents.splice(
|
this.ignoredEvents.splice(this.ignoredEvents.findIndex(info => type === info.type && userId === info.userId), 1);
|
||||||
this.ignoredEvents.findIndex(info => type === info.type && userId === info.userId),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,14 +156,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
const modId = relevantAuditLogEntry.user.id;
|
const modId = relevantAuditLogEntry.user.id;
|
||||||
const auditLogId = relevantAuditLogEntry.id;
|
const auditLogId = relevantAuditLogEntry.id;
|
||||||
|
|
||||||
await this.createCase(
|
await this.createCase(user.id, modId, CaseType.Ban, auditLogId, relevantAuditLogEntry.reason, true);
|
||||||
user.id,
|
|
||||||
modId,
|
|
||||||
CaseType.Ban,
|
|
||||||
auditLogId,
|
|
||||||
relevantAuditLogEntry.reason,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
await this.createCase(user.id, null, CaseType.Ban);
|
await this.createCase(user.id, null, CaseType.Ban);
|
||||||
}
|
}
|
||||||
|
@ -214,9 +204,9 @@ export class ModActionsPlugin extends Plugin {
|
||||||
if (actions.length) {
|
if (actions.length) {
|
||||||
const alertChannel: any = this.guild.channels.get(alertChannelId);
|
const alertChannel: any = this.guild.channels.get(alertChannelId);
|
||||||
alertChannel.send(
|
alertChannel.send(
|
||||||
`<@!${member.id}> (${member.user.username}#${member.user.discriminator} \`${
|
`<@!${member.id}> (${member.user.username}#${member.user.discriminator} \`${member.id}\`) joined with ${
|
||||||
member.id
|
actions.length
|
||||||
}\`) joined with ${actions.length} prior record(s)`
|
} prior record(s)`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,9 +299,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!messageSent) {
|
if (!messageSent) {
|
||||||
const failedMsg = await msg.channel.createMessage(
|
const failedMsg = await msg.channel.createMessage("Failed to message the user. Log the warning anyway?");
|
||||||
"Failed to message the user. Log the warning anyway?"
|
|
||||||
);
|
|
||||||
const reply = await waitForReaction(this.bot, failedMsg, ["✅", "❌"], msg.author.id);
|
const reply = await waitForReaction(this.bot, failedMsg, ["✅", "❌"], msg.author.id);
|
||||||
failedMsg.delete();
|
failedMsg.delete();
|
||||||
if (!reply || reply.name === "❌") {
|
if (!reply || reply.name === "❌") {
|
||||||
|
@ -375,22 +363,14 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create a case
|
// Create a case
|
||||||
const caseId = await this.createCase(
|
const caseId = await this.createCase(args.member.id, msg.author.id, CaseType.Mute, null, args.reason);
|
||||||
args.member.id,
|
|
||||||
msg.author.id,
|
|
||||||
CaseType.Mute,
|
|
||||||
null,
|
|
||||||
args.reason
|
|
||||||
);
|
|
||||||
await this.mutes.setCaseId(args.member.id, caseId);
|
await this.mutes.setCaseId(args.member.id, caseId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message the user informing them of the mute
|
// Message the user informing them of the mute
|
||||||
// Don't message them if we're updating an old mute
|
// Don't message them if we're updating an old mute
|
||||||
if (args.reason && !hasOldCase) {
|
if (args.reason && !hasOldCase) {
|
||||||
const template = muteTime
|
const template = muteTime ? this.configValue("timed_mute_message") : this.configValue("mute_message");
|
||||||
? this.configValue("timed_mute_message")
|
|
||||||
: this.configValue("mute_message");
|
|
||||||
|
|
||||||
const muteMessage = formatTemplateString(template, {
|
const muteMessage = formatTemplateString(template, {
|
||||||
guildName: this.guild.name,
|
guildName: this.guild.name,
|
||||||
|
@ -409,13 +389,9 @@ export class ModActionsPlugin extends Plugin {
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
let response;
|
let response;
|
||||||
if (muteTime) {
|
if (muteTime) {
|
||||||
response = `Muted **${args.member.user.username}#${
|
response = `Muted **${args.member.user.username}#${args.member.user.discriminator}** for ${timeUntilUnmute}`;
|
||||||
args.member.user.discriminator
|
|
||||||
}** for ${timeUntilUnmute}`;
|
|
||||||
} else {
|
} else {
|
||||||
response = `Muted **${args.member.user.username}#${
|
response = `Muted **${args.member.user.username}#${args.member.user.discriminator}** indefinitely`;
|
||||||
args.member.user.discriminator
|
|
||||||
}** indefinitely`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!messageSent) response += " (failed to message user)";
|
if (!messageSent) response += " (failed to message user)";
|
||||||
|
@ -466,9 +442,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(
|
||||||
`Unmuting **${args.member.user.username}#${
|
`Unmuting **${args.member.user.username}#${args.member.user.discriminator}** in ${timeUntilUnmute}`
|
||||||
args.member.user.discriminator
|
|
||||||
}** in ${timeUntilUnmute}`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -551,9 +525,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
lines.push(
|
lines.push(
|
||||||
...manuallyMutedMembers.map(member => {
|
...manuallyMutedMembers.map(member => {
|
||||||
return `\`Manual mute\` **${member.user.username}#${member.user.discriminator}** (\`${
|
return `\`Manual mute\` **${member.user.username}#${member.user.discriminator}** (\`${member.id}\`)`;
|
||||||
member.id
|
|
||||||
}\`)`;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -678,9 +650,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(`Softbanned **${args.member.user.username}#${args.member.user.discriminator}**`)
|
||||||
`Softbanned **${args.member.user.username}#${args.member.user.discriminator}**`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
|
@ -722,9 +692,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
// If the user exists as a guild member, make sure we can act on them first
|
// If the user exists as a guild member, make sure we can act on them first
|
||||||
const member = this.guild.members.get(args.userId);
|
const member = this.guild.members.get(args.userId);
|
||||||
if (member && !this.canActOn(msg.member, member)) {
|
if (member && !this.canActOn(msg.member, member)) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage("Cannot forceban this user: insufficient permissions"));
|
||||||
errorMessage("Cannot forceban this user: insufficient permissions")
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,65 +722,62 @@ export class ModActionsPlugin extends Plugin {
|
||||||
@d.command("massban", "<userIds:string...>")
|
@d.command("massban", "<userIds:string...>")
|
||||||
@d.permission("massban")
|
@d.permission("massban")
|
||||||
async massbanCmd(msg: Message, args: { userIds: string[] }) {
|
async massbanCmd(msg: Message, args: { userIds: string[] }) {
|
||||||
|
// Limit to 100 users at once (arbitrary?)
|
||||||
|
if (args.userIds.length > 100) {
|
||||||
|
msg.channel.createMessage(errorMessage(`Can only massban max 100 users at once`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask for ban reason (cleaner this way instead of trying to cram it into the args)
|
||||||
msg.channel.createMessage("Ban reason? `cancel` to cancel");
|
msg.channel.createMessage("Ban reason? `cancel` to cancel");
|
||||||
const banReasonReply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id);
|
const banReasonReply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id);
|
||||||
if (
|
if (!banReasonReply || !banReasonReply.content || banReasonReply.content.toLowerCase().trim() === "cancel") {
|
||||||
!banReasonReply ||
|
|
||||||
!banReasonReply.content ||
|
|
||||||
banReasonReply.content.toLowerCase().trim() === "cancel"
|
|
||||||
) {
|
|
||||||
msg.channel.createMessage("Cancelled");
|
msg.channel.createMessage("Cancelled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const banReason = banReasonReply.content;
|
const banReason = banReasonReply.content;
|
||||||
|
|
||||||
if (args.userIds.length > 100) {
|
// Verify we can act on each of the users specified
|
||||||
msg.channel.createMessage(errorMessage(`Can only massban max 100 users at once`));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const userId of args.userIds) {
|
for (const userId of args.userIds) {
|
||||||
const member = this.guild.members.get(userId);
|
const member = this.guild.members.get(userId);
|
||||||
if (member && !this.canActOn(msg.member, member)) {
|
if (member && !this.canActOn(msg.member, member)) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage("Cannot massban one or more users: insufficient permissions"));
|
||||||
errorMessage("Cannot massban one or more users: insufficient permissions")
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore automatic ban cases and logs for these users
|
||||||
|
// We'll create our own cases below and post a single "mass banned" log instead
|
||||||
args.userIds.forEach(userId => {
|
args.userIds.forEach(userId => {
|
||||||
this.ignoreEvent(IgnoredEventType.Ban, userId);
|
// Use longer timeouts since this can take a while
|
||||||
this.serverLogs.ignoreLog(LogType.MEMBER_BAN, userId);
|
this.ignoreEvent(IgnoredEventType.Ban, userId, 120 * 1000);
|
||||||
|
this.serverLogs.ignoreLog(LogType.MEMBER_BAN, userId, 120 * 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show a loading indicator since this can take a while
|
||||||
const loadingMsg = await msg.channel.createMessage("Banning...");
|
const loadingMsg = await msg.channel.createMessage("Banning...");
|
||||||
|
|
||||||
|
// Ban each user and count failed bans (if any)
|
||||||
const failedBans = [];
|
const failedBans = [];
|
||||||
for (const userId of args.userIds) {
|
for (const userId of args.userIds) {
|
||||||
try {
|
try {
|
||||||
await this.guild.banMember(userId);
|
await this.guild.banMember(userId);
|
||||||
await this.createCase(
|
await this.createCase(userId, msg.author.id, CaseType.Ban, null, `Mass ban: ${banReason}`, false, false);
|
||||||
userId,
|
|
||||||
msg.author.id,
|
|
||||||
CaseType.Ban,
|
|
||||||
null,
|
|
||||||
`Mass ban: ${banReason}`,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failedBans.push(userId);
|
failedBans.push(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear loading indicator
|
||||||
loadingMsg.delete();
|
loadingMsg.delete();
|
||||||
|
|
||||||
const successfulBanCount = args.userIds.length - failedBans.length;
|
const successfulBanCount = args.userIds.length - failedBans.length;
|
||||||
if (successfulBanCount === 0) {
|
if (successfulBanCount === 0) {
|
||||||
|
// All bans failed - don't create a log entry and notify the user
|
||||||
msg.channel.createMessage(errorMessage("All bans failed. Make sure the IDs are valid."));
|
msg.channel.createMessage(errorMessage("All bans failed. Make sure the IDs are valid."));
|
||||||
} else {
|
} else {
|
||||||
|
// Some or all bans were successful. Create a log entry for the mass ban and notify the user.
|
||||||
this.serverLogs.log(LogType.MASSBAN, {
|
this.serverLogs.log(LogType.MASSBAN, {
|
||||||
mod: stripObjectToScalars(msg.author),
|
mod: stripObjectToScalars(msg.author),
|
||||||
count: successfulBanCount
|
count: successfulBanCount
|
||||||
|
@ -820,16 +785,10 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
if (failedBans.length) {
|
if (failedBans.length) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(`Banned ${successfulBanCount} users, ${failedBans.length} failed: ${failedBans.join(" ")}`)
|
||||||
`Banned ${successfulBanCount} users, ${failedBans.length} failed: ${failedBans.join(
|
|
||||||
" "
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(successMessage(`Banned ${successfulBanCount} users successfully`));
|
||||||
successMessage(`Banned ${successfulBanCount} users successfully`)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -846,9 +805,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
// If the user exists as a guild member, make sure we can act on them first
|
// If the user exists as a guild member, make sure we can act on them first
|
||||||
const member = this.guild.members.get(args.userId);
|
const member = this.guild.members.get(args.userId);
|
||||||
if (member && !this.canActOn(msg.member, member)) {
|
if (member && !this.canActOn(msg.member, member)) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage("Cannot add case on this user: insufficient permissions"));
|
||||||
errorMessage("Cannot add case on this user: insufficient permissions")
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,13 +817,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the case
|
// Create the case
|
||||||
const caseId = await this.createCase(
|
const caseId = await this.createCase(args.target, msg.author.id, CaseType[type], null, args.reason);
|
||||||
args.target,
|
|
||||||
msg.author.id,
|
|
||||||
CaseType[type],
|
|
||||||
null,
|
|
||||||
args.reason
|
|
||||||
);
|
|
||||||
const theCase = await this.cases.find(caseId);
|
const theCase = await this.cases.find(caseId);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
|
@ -923,9 +874,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
if (reason.length > CASE_LIST_REASON_MAX_LENGTH) {
|
if (reason.length > CASE_LIST_REASON_MAX_LENGTH) {
|
||||||
const match = reason.slice(CASE_LIST_REASON_MAX_LENGTH, 20).match(/(?:[.,!?\s]|$)/);
|
const match = reason.slice(CASE_LIST_REASON_MAX_LENGTH, 20).match(/(?:[.,!?\s]|$)/);
|
||||||
const nextWhitespaceIndex = match
|
const nextWhitespaceIndex = match ? CASE_LIST_REASON_MAX_LENGTH + match.index : CASE_LIST_REASON_MAX_LENGTH;
|
||||||
? CASE_LIST_REASON_MAX_LENGTH + match.index
|
|
||||||
: CASE_LIST_REASON_MAX_LENGTH;
|
|
||||||
if (nextWhitespaceIndex < reason.length) {
|
if (nextWhitespaceIndex < reason.length) {
|
||||||
reason = reason.slice(0, nextWhitespaceIndex - 1) + "...";
|
reason = reason.slice(0, nextWhitespaceIndex - 1) + "...";
|
||||||
}
|
}
|
||||||
|
@ -963,12 +912,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
* Attempts to message the specified user through DMs and/or the message channel.
|
* Attempts to message the specified user through DMs and/or the message channel.
|
||||||
* Returns a promise that resolves to a boolean indicating whether we were able to message them or not.
|
* Returns a promise that resolves to a boolean indicating whether we were able to message them or not.
|
||||||
*/
|
*/
|
||||||
protected async tryToMessageUser(
|
protected async tryToMessageUser(user: User, str: string, useDM: boolean, useChannel: boolean): Promise<boolean> {
|
||||||
user: User,
|
|
||||||
str: string,
|
|
||||||
useDM: boolean,
|
|
||||||
useChannel: boolean
|
|
||||||
): Promise<boolean> {
|
|
||||||
let messageSent = false;
|
let messageSent = false;
|
||||||
|
|
||||||
if (!useDM && !useChannel) {
|
if (!useDM && !useChannel) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue