3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-10 20:35:02 +00:00

Notify user for spam detection mutes. Add notification status ('user notified in DMs' etc.) to the case. Log case updates. Add 'unmuted immediately' to the case for unmutes without a time.

This commit is contained in:
Dragory 2019-04-13 17:35:02 +03:00
parent 40cb74ee28
commit fe88766f02
10 changed files with 455 additions and 332 deletions

View file

@ -1,15 +1,27 @@
import { Member, Message, User } from "eris";
import { GuildCases } from "../data/GuildCases";
import { GuildCases, ICaseDetails } from "../data/GuildCases";
import moment from "moment-timezone";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildActions } from "../data/GuildActions";
import { GuildMutes } from "../data/GuildMutes";
import { DBDateFormat, chunkMessageLines, stripObjectToScalars, successMessage, errorMessage, sleep } from "../utils";
import {
chunkMessageLines,
DBDateFormat,
errorMessage,
INotifyUserResult,
notifyUser,
NotifyUserStatus,
stripObjectToScalars,
successMessage,
ucfirst,
} from "../utils";
import humanizeDuration from "humanize-duration";
import { LogType } from "../data/LogType";
import { GuildLogs } from "../data/GuildLogs";
import { decorators as d, IPluginOptions, logger } from "knub";
import { Mute } from "../data/entities/Mute";
import { renderTemplate } from "../templateFormatter";
import { CaseTypes } from "../data/CaseTypes";
interface IMuteWithDetails extends Mute {
member?: Member;
@ -20,6 +32,12 @@ interface IMutesPluginConfig {
mute_role: string;
move_to_voice_channel: string;
dm_on_mute: boolean;
message_on_mute: boolean;
message_channel: string;
mute_message: string;
timed_mute_message: string;
can_view_list: boolean;
can_cleanup: boolean;
}
@ -39,6 +57,12 @@ export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig> {
mute_role: null,
move_to_voice_channel: null,
dm_on_mute: false,
message_on_mute: false,
message_channel: null,
mute_message: "You have been muted on {guildName}. Reason given: {reason}",
timed_mute_message: "You have been muted on {guildName} for {time}. Reason given: {reason}",
can_view_list: false,
can_cleanup: false,
},
@ -66,10 +90,10 @@ export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig> {
this.serverLogs = new GuildLogs(this.guildId);
this.actions.register("mute", args => {
return this.muteMember(args.member, args.muteTime);
return this.muteMember(args.member, args.muteTime, args.reason, args.caseDetails);
});
this.actions.register("unmute", args => {
return this.unmuteMember(args.member, args.unmuteTime);
return this.unmuteMember(args.member, args.unmuteTime, args.caseDetails);
});
// Check for expired mutes every 5s
@ -84,33 +108,128 @@ export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig> {
clearInterval(this.muteClearIntervalId);
}
public async muteMember(member: Member, muteTime: number = null) {
public async muteMember(
member: Member,
muteTime: number = null,
reason: string = null,
caseDetails: ICaseDetails = {},
) {
const muteRole = this.getConfig().mute_role;
if (!muteRole) return;
// Add muted role
await member.addRole(muteRole);
const timeUntilUnmute = muteTime && humanizeDuration(muteTime);
// No mod specified -> mark Zeppelin as the mod
if (!caseDetails.modId) {
caseDetails.modId = this.bot.user.id;
}
// Apply mute role if it's missing
if (!member.roles.includes(muteRole)) {
await member.addRole(muteRole);
}
// If enabled, move the user to the mute voice channel (e.g. afk - just to apply the voice perms from the mute role)
const moveToVoiceChannelId = this.getConfig().move_to_voice_channel;
if (moveToVoiceChannelId && member.voiceState.channelID) {
try {
await member.edit({
channelID: moveToVoiceChannelId,
});
await member.edit({ channelID: moveToVoiceChannelId });
} catch (e) {
logger.warn(`Could not move user ${member.id} to voice channel ${moveToVoiceChannelId} when muting`);
}
}
// Create & return mute record
return this.mutes.addOrUpdateMute(member.id, muteTime);
// If the user is already muted, update the duration of their existing mute
const existingMute = await this.mutes.findExistingMuteForUserId(member.id);
let notifyResult: INotifyUserResult = { status: NotifyUserStatus.Ignored };
if (existingMute) {
await this.mutes.updateExpiryTime(member.id, muteTime);
} else {
await this.mutes.addMute(member.id, muteTime);
// If it's a new mute, attempt to message the user
const config = this.getMatchingConfig({ member });
const template = muteTime ? config.timed_mute_message : config.mute_message;
const muteMessage =
template &&
(await renderTemplate(template, {
guildName: this.guild.name,
reason,
time: timeUntilUnmute,
}));
if (muteMessage) {
notifyResult = await notifyUser(this.bot, this.guild, member.user, muteMessage, {
useDM: config.dm_on_mute,
useChannel: config.message_on_mute,
channelId: config.message_channel,
});
}
}
// Create/update a case
let theCase;
if (existingMute && existingMute.case_id) {
// Update old case
theCase = await this.cases.find(existingMute.case_id);
const noteDetails = [`Mute updated to ${muteTime ? timeUntilUnmute : "indefinite"}`];
await this.actions.fire("createCaseNote", {
caseId: existingMute.case_id,
modId: caseDetails.modId,
note: reason,
noteDetails,
});
} else {
// Create new case
const noteDetails = [`Muted ${muteTime ? `for ${timeUntilUnmute}` : "indefinitely"}`];
if (notifyResult.status !== NotifyUserStatus.Ignored) {
noteDetails.push(ucfirst(notifyResult.text));
}
theCase = await this.actions.fire("createCase", {
userId: member.id,
modId: caseDetails.modId,
type: CaseTypes.Mute,
reason,
ppId: caseDetails.ppId,
noteDetails,
extraNotes: caseDetails.extraNotes,
});
await this.mutes.setCaseId(member.id, theCase.id);
}
// Log the action
if (muteTime) {
this.serverLogs.log(LogType.MEMBER_TIMED_MUTE, {
mod: stripObjectToScalars(caseDetails.modId),
member: stripObjectToScalars(member, ["user"]),
time: timeUntilUnmute,
});
} else {
this.serverLogs.log(LogType.MEMBER_MUTE, {
mod: stripObjectToScalars(caseDetails.modId),
member: stripObjectToScalars(member, ["user"]),
});
}
return {
case: theCase,
notifyResult,
updatedExistingMute: !!existingMute,
};
}
public async unmuteMember(member: Member, unmuteTime: number = null) {
public async unmuteMember(member: Member, unmuteTime: number = null, caseDetails: ICaseDetails = {}) {
const existingMute = await this.mutes.findExistingMuteForUserId(member.id);
if (!existingMute) return;
if (unmuteTime) {
await this.mutes.addOrUpdateMute(member.id, unmuteTime);
// Schedule timed unmute (= just set the mute's duration)
await this.mutes.updateExpiryTime(member.id, unmuteTime);
} else {
// Unmute immediately
const muteRole = this.getConfig().mute_role;
if (member.roles.includes(muteRole)) {
await member.removeRole(muteRole);
@ -118,6 +237,44 @@ export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig> {
await this.mutes.clear(member.id);
}
const timeUntilUnmute = unmuteTime && humanizeDuration(unmuteTime);
// Create a case
const noteDetails = [];
if (unmuteTime) {
noteDetails.push(`Scheduled unmute in ${timeUntilUnmute}`);
} else {
noteDetails.push(`Unmuted immediately`);
}
const createdCase = await this.actions.fire("createCase", {
userId: member.id,
modId: caseDetails.modId,
type: CaseTypes.Unmute,
reason: caseDetails.reason,
ppId: caseDetails.ppId,
noteDetails,
});
// Log the action
const mod = this.bot.users.get(caseDetails.modId);
if (unmuteTime) {
this.serverLogs.log(LogType.MEMBER_TIMED_UNMUTE, {
mod: stripObjectToScalars(mod),
member: stripObjectToScalars(member, ["user"]),
time: timeUntilUnmute,
});
} else {
this.serverLogs.log(LogType.MEMBER_UNMUTE, {
mod: stripObjectToScalars(mod),
member: stripObjectToScalars(member, ["user"]),
});
}
return {
case: createdCase,
};
}
@d.command("mutes", [], {
@ -361,7 +518,6 @@ export class MutesPlugin extends ZeppelinPlugin<IMutesPluginConfig> {
if (!member) continue;
try {
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, member.id);
await member.removeRole(this.getConfig().mute_role);
} catch (e) {} // tslint:disable-line