Add case_id to mutes. Add !mutes command to list mutes.

This commit is contained in:
Dragory 2018-08-05 00:18:50 +03:00
parent 60c434999e
commit 7a372533ec
9 changed files with 131 additions and 3 deletions

View file

@ -0,0 +1,12 @@
exports.up = async function(knex, Promise) {
await knex.schema.table('mutes', table => {
table.integer('case_id').unsigned().nullable().defaultTo(null).after('user_id').references('id').inTable('cases').onDelete('SET NULL');
});
};
exports.down = async function(knex, Promise) {
await knex.schema.table('mutes', table => {
table.dropForeign('case_id');
table.dropColumn('case_id');
});
};

5
package-lock.json generated
View file

@ -2824,6 +2824,11 @@
"resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
"integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g="
},
"lodash.chunk": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
"integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",

View file

@ -35,6 +35,7 @@
"knex": "0.12.6",
"knub": "^9.6.4",
"lodash.at": "^4.6.0",
"lodash.chunk": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.intersection": "^4.4.0",
"lodash.isequal": "^4.5.0",

View file

@ -9,6 +9,14 @@ export class GuildCases {
this.guildId = guildId;
}
async get(ids: number[]): Promise<Case[]> {
const result = await knex("cases")
.whereIn("id", ids)
.select();
return result.map(r => new Case(r));
}
async find(id: number): Promise<Case> {
const result = await knex("cases")
.where("guild_id", this.guildId)

View file

@ -69,6 +69,22 @@ export class GuildMutes {
}
}
async getActiveMutes(): Promise<Mute[]> {
const result = await knex("mutes")
.where("guild_id", this.guildId)
.where(q => q.whereRaw("expires_at > NOW()").orWhereNull("expires_at"))
.select();
return result.map(r => new Mute(r));
}
async setCaseId(userId, caseId) {
await knex("mutes")
.where("guild_id", this.guildId)
.where("user_id", userId)
.update({ case_id: caseId });
}
async clear(userId) {
return knex("mutes")
.where("guild_id", this.guildId)

View file

@ -3,6 +3,7 @@ import Model from "./Model";
export default class Mute extends Model {
public guild_id: string;
public user_id: string;
public case_id: number;
public created_at: string;
public expires_at: string;
}

View file

@ -2,9 +2,11 @@ import { decorators as d, Plugin, waitForReaction } from "knub";
import { Constants as ErisConstants, Guild, Member, Message, TextChannel, User } from "eris";
import moment from "moment-timezone";
import humanizeDuration from "humanize-duration";
import chunk from "lodash.chunk";
import { GuildCases } from "../data/GuildCases";
import {
convertDelayStringToMS,
DBDateFormat,
disableLinkPreviews,
errorMessage,
findRelevantAuditLogEntry,
@ -36,8 +38,8 @@ interface IIgnoredEvent {
const CASE_LIST_REASON_MAX_LENGTH = 80;
export class ModActionsPlugin extends Plugin {
public mutes: GuildMutes;
protected cases: GuildCases;
protected mutes: GuildMutes;
protected serverLogs: GuildLogs;
protected muteClearIntervalId: Timer;
@ -355,7 +357,14 @@ export class ModActionsPlugin extends Plugin {
this.muteMember(args.member, muteTime, args.reason);
// Create a case
await this.createCase(args.member.id, msg.author.id, CaseType.Mute, null, args.reason);
const caseId = await this.createCase(
args.member.id,
msg.author.id,
CaseType.Mute,
null,
args.reason
);
await this.mutes.setCaseId(args.member.id, caseId);
// Message the user informing them of the mute
let messageSent = true;
@ -441,6 +450,78 @@ export class ModActionsPlugin extends Plugin {
});
}
@d.command("mutes")
@d.permission("view")
async mutesCmd(msg: Message) {
const lines = [];
// Active, logged mutes
const activeMutes = await this.mutes.getActiveMutes();
activeMutes.sort((a, b) => {
if (a.expires_at == null && b.expires_at != null) return 1;
if (b.expires_at == null && a.expires_at != null) return -1;
if (a.expires_at == null && b.expires_at == null) {
return a.created_at > b.created_at ? 1 : -1;
}
return a.expires_at > b.expires_at ? 1 : -1;
});
const caseIds = activeMutes.map(m => m.case_id).filter(v => !!v);
const cases = caseIds ? await this.cases.get(caseIds) : [];
const casesById = cases.reduce((map, c) => map.set(c.id, c), new Map());
lines.push(
...activeMutes.map(mute => {
const user = this.bot.users.get(mute.user_id);
const username = user ? `${user.username}#${user.discriminator}` : "Unknown#0000";
const theCase = casesById[mute.case_id] || null;
const caseName = theCase ? `Case #${theCase.case_number}` : "No case";
let line = `\`${caseName}\` **${username}** (\`${mute.user_id}\`)`;
if (mute.expires_at) {
const timeUntilExpiry = moment().diff(moment(mute.expires_at, DBDateFormat));
const humanizedTime = humanizeDuration(timeUntilExpiry, { largest: 2, round: true });
line += ` (expires in ${humanizedTime})`;
} else {
line += ` (doesn't expire)`;
}
const mutedAt = moment(mute.created_at, DBDateFormat);
line += ` (muted at ${mutedAt.format("YYYY-MM-DD HH:mm:ss")})`;
return line;
})
);
// Manually added mute roles
const muteUserIds = activeMutes.reduce((set, m) => set.add(m.user_id), new Set());
const manuallyMutedMembers = [];
const muteRole = this.configValue("mute_role");
if (muteRole) {
this.guild.members.forEach(member => {
if (muteUserIds.has(member.id)) return;
if (member.roles.includes(muteRole)) manuallyMutedMembers.push(member);
});
}
lines.push(
...manuallyMutedMembers.map(member => {
return `\`Manual mute\` **${member.user.username}#${member.user.discriminator}** (\`${
member.id
}\`)`;
})
);
const chunks = chunk(lines, 15);
for (const [i, chunkLines] of chunks.entries()) {
let body = chunkLines.join("\n");
if (i === 0) body = `Active mutes:\n\n${body}`;
msg.channel.createMessage(body);
}
}
@d.command("kick", "<member:Member> [reason:string$]")
@d.permission("kick")
async kickCmd(msg, args) {

View file

@ -192,7 +192,7 @@ export class SpamPlugin extends Plugin {
spamConfig.mute_time ? spamConfig.mute_time * 60 * 1000 : 120 * 1000,
"Automatic spam detection"
);
await modActionsPlugin.createCase(
const caseId = await modActionsPlugin.createCase(
msg.member.id,
this.bot.user.id,
CaseType.Mute,
@ -205,6 +205,8 @@ export class SpamPlugin extends Plugin {
`),
true
);
await modActionsPlugin.mutes.setCaseId(msg.member.id, caseId);
this.logs.log(LogType.MEMBER_MUTE_SPAM, {
member: stripObjectToScalars(msg.member, ["user"]),
channel: stripObjectToScalars(msg.channel),

View file

@ -274,3 +274,5 @@ export function getRoleMentions(str: string) {
export function disableLinkPreviews(str: string): string {
return str.replace(/(?<!\<)(https?:\/\/\S+)/gi, "<$1>");
}
export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";