Switch from Knex to TypeORM. Update Knub.

This commit is contained in:
Dragory 2018-10-26 06:41:20 +03:00
parent e3ff4cef45
commit f9c16263ae
49 changed files with 1192 additions and 1395 deletions

View file

@ -11,14 +11,13 @@ import {
errorMessage,
findRelevantAuditLogEntry,
formatTemplateString,
sleep,
stripObjectToScalars,
successMessage,
trimLines
} from "../utils";
import { GuildMutes } from "../data/GuildMutes";
import Case from "../models/Case";
import { CaseType } from "../data/CaseType";
import { Case } from "../data/entities/Case";
import { CaseTypes } from "../data/CaseTypes";
import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType";
import Timer = NodeJS.Timer;
@ -47,8 +46,8 @@ export class ModActionsPlugin extends Plugin {
protected ignoredEvents: IIgnoredEvent[];
async onLoad() {
this.cases = new GuildCases(this.guildId);
this.mutes = new GuildMutes(this.guildId);
this.cases = GuildCases.getInstance(this.guildId);
this.mutes = GuildMutes.getInstance(this.guildId);
this.serverLogs = new GuildLogs(this.guildId);
this.ignoredEvents = [];
@ -156,9 +155,9 @@ export class ModActionsPlugin extends Plugin {
const modId = relevantAuditLogEntry.user.id;
const auditLogId = relevantAuditLogEntry.id;
await this.createCase(user.id, modId, CaseType.Ban, auditLogId, relevantAuditLogEntry.reason, true);
await this.createCase(user.id, modId, CaseTypes.Ban, auditLogId, relevantAuditLogEntry.reason, true);
} else {
await this.createCase(user.id, null, CaseType.Ban);
await this.createCase(user.id, null, CaseTypes.Ban);
}
}
@ -183,9 +182,9 @@ export class ModActionsPlugin extends Plugin {
const modId = relevantAuditLogEntry.user.id;
const auditLogId = relevantAuditLogEntry.id;
await this.createCase(user.id, modId, CaseType.Unban, auditLogId, null, true);
await this.createCase(user.id, modId, CaseTypes.Unban, auditLogId, null, true);
} else {
await this.createCase(user.id, null, CaseType.Unban);
await this.createCase(user.id, null, CaseTypes.Unban);
}
}
@ -228,7 +227,7 @@ export class ModActionsPlugin extends Plugin {
this.createCase(
member.id,
kickAuditLogEntry.user.id,
CaseType.Kick,
CaseTypes.Kick,
kickAuditLogEntry.id,
kickAuditLogEntry.reason,
true
@ -274,12 +273,13 @@ export class ModActionsPlugin extends Plugin {
const user = await this.bot.users.get(args.userId);
const userName = user ? `${user.username}#${user.discriminator}` : "member";
await this.createCase(args.userId, msg.author.id, CaseType.Note, null, args.note);
await this.createCase(args.userId, msg.author.id, CaseTypes.Note, null, args.note);
msg.channel.createMessage(successMessage(`Note added on ${userName}`));
}
@d.command("warn", "<member:Member> <reason:string$>")
@d.permission("warn")
@d.nonBlocking()
async warnCmd(msg: Message, args: any) {
// Make sure we're allowed to warn this member
if (!this.canActOn(msg.member, args.member)) {
@ -307,7 +307,7 @@ export class ModActionsPlugin extends Plugin {
}
}
await this.createCase(args.member.id, msg.author.id, CaseType.Warn, null, args.reason);
await this.createCase(args.member.id, msg.author.id, CaseTypes.Warn, null, args.reason);
msg.channel.createMessage(
successMessage(`Warned **${args.member.user.username}#${args.member.user.discriminator}**`)
@ -363,7 +363,7 @@ export class ModActionsPlugin extends Plugin {
}
} else {
// Create a case
const caseId = 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, CaseTypes.Mute, null, args.reason);
await this.mutes.setCaseId(args.member.id, caseId);
}
@ -458,7 +458,7 @@ export class ModActionsPlugin extends Plugin {
}
// Create a case
await this.createCase(args.member.id, msg.author.id, CaseType.Unmute, null, args.reason);
await this.createCase(args.member.id, msg.author.id, CaseTypes.Unmute, null, args.reason);
// Log the action
this.serverLogs.log(LogType.MEMBER_UNMUTE, {
@ -568,7 +568,7 @@ export class ModActionsPlugin extends Plugin {
args.member.kick(args.reason);
// Create a case for this action
await this.createCase(args.member.id, msg.author.id, CaseType.Kick, null, args.reason);
await this.createCase(args.member.id, msg.author.id, CaseTypes.Kick, null, args.reason);
// Confirm the action to the moderator
let response = `Kicked **${args.member.user.username}#${args.member.user.discriminator}**`;
@ -613,7 +613,7 @@ export class ModActionsPlugin extends Plugin {
args.member.ban(1, args.reason);
// Create a case for this action
await this.createCase(args.member.id, msg.author.id, CaseType.Ban, null, args.reason);
await this.createCase(args.member.id, msg.author.id, CaseTypes.Ban, null, args.reason);
// Confirm the action to the moderator
let response = `Banned **${args.member.user.username}#${args.member.user.discriminator}**`;
@ -646,7 +646,7 @@ export class ModActionsPlugin extends Plugin {
await this.guild.unbanMember(args.member.id);
// Create a case for this action
await this.createCase(args.member.id, msg.author.id, CaseType.Softban, null, args.reason);
await this.createCase(args.member.id, msg.author.id, CaseTypes.Softban, null, args.reason);
// Confirm the action to the moderator
msg.channel.createMessage(
@ -677,7 +677,7 @@ export class ModActionsPlugin extends Plugin {
msg.channel.createMessage(successMessage("Member unbanned!"));
// Create a case
this.createCase(args.userId, msg.author.id, CaseType.Unban, null, args.reason);
this.createCase(args.userId, msg.author.id, CaseTypes.Unban, null, args.reason);
// Log the action
this.serverLogs.log(LogType.MEMBER_UNBAN, {
@ -710,7 +710,7 @@ export class ModActionsPlugin extends Plugin {
msg.channel.createMessage(successMessage("Member forcebanned!"));
// Create a case
this.createCase(args.userId, msg.author.id, CaseType.Ban, null, args.reason);
this.createCase(args.userId, msg.author.id, CaseTypes.Ban, null, args.reason);
// Log the action
this.serverLogs.log(LogType.MEMBER_FORCEBAN, {
@ -721,6 +721,7 @@ export class ModActionsPlugin extends Plugin {
@d.command("massban", "<userIds:string...>")
@d.permission("massban")
@d.nonBlocking()
async massbanCmd(msg: Message, args: { userIds: string[] }) {
// Limit to 100 users at once (arbitrary?)
if (args.userIds.length > 100) {
@ -763,7 +764,7 @@ export class ModActionsPlugin extends Plugin {
for (const userId of args.userIds) {
try {
await this.guild.banMember(userId);
await this.createCase(userId, msg.author.id, CaseType.Ban, null, `Mass ban: ${banReason}`, false, false);
await this.createCase(userId, msg.author.id, CaseTypes.Ban, null, `Mass ban: ${banReason}`, false, false);
} catch (e) {
failedBans.push(userId);
}
@ -811,13 +812,13 @@ export class ModActionsPlugin extends Plugin {
// Verify the case type is valid
const type: string = args.type[0].toUpperCase() + args.type.slice(1).toLowerCase();
if (!CaseType[type]) {
if (!CaseTypes[type]) {
msg.channel.createMessage(errorMessage("Cannot add case: invalid case type"));
return;
}
// Create the case
const caseId = await this.createCase(args.target, msg.author.id, CaseType[type], null, args.reason);
const caseId = await this.createCase(args.target, msg.author.id, CaseTypes[type], null, args.reason);
const theCase = await this.cases.find(caseId);
// Log the action
@ -852,7 +853,7 @@ export class ModActionsPlugin extends Plugin {
@d.command(/cases|usercases/, "<userId:userId> [expanded:string]")
@d.permission("view")
async usercasesCmd(msg: Message, args: { userId: string; expanded?: string }) {
const cases = await this.cases.getByUserId(args.userId);
const cases = await this.cases.with("notes").getByUserId(args.userId);
const user = this.bot.users.get(args.userId);
const userName = user ? `${user.username}#${user.discriminator}` : "Unknown#0000";
const prefix = this.knub.getGuildData(this.guildId).config.prefix;
@ -869,7 +870,8 @@ export class ModActionsPlugin extends Plugin {
// Compact view (= regular message with a preview of each case)
const lines = [];
for (const theCase of cases) {
const firstNote = await this.cases.findFirstCaseNote(theCase.id);
theCase.notes.sort((a, b) => (a.created_at > b.created_at ? 1 : -1));
const firstNote = theCase.notes[0];
let reason = firstNote ? firstNote.body : "";
if (reason.length > CASE_LIST_REASON_MAX_LENGTH) {
@ -882,7 +884,7 @@ export class ModActionsPlugin extends Plugin {
reason = disableLinkPreviews(reason);
lines.push(`Case \`#${theCase.case_number}\` __${CaseType[theCase.type]}__ ${reason}`);
lines.push(`Case \`#${theCase.case_number}\` __${CaseTypes[theCase.type]}__ ${reason}`);
}
const finalMessage = trimLines(`
@ -945,7 +947,7 @@ export class ModActionsPlugin extends Plugin {
protected async displayCase(caseOrCaseId: Case | number, channelId: string) {
let theCase: Case;
if (typeof caseOrCaseId === "number") {
theCase = await this.cases.find(caseOrCaseId);
theCase = await this.cases.with("notes").find(caseOrCaseId);
} else {
theCase = caseOrCaseId;
}
@ -953,10 +955,8 @@ export class ModActionsPlugin extends Plugin {
if (!theCase) return;
if (!this.guild.channels.get(channelId)) return;
const notes = await this.cases.getCaseNotes(theCase.id);
const createdAt = moment(theCase.created_at);
const actionTypeStr = CaseType[theCase.type].toUpperCase();
const actionTypeStr = CaseTypes[theCase.type].toUpperCase();
const embed: any = {
title: `${actionTypeStr} - Case #${theCase.case_number}`,
@ -981,8 +981,8 @@ export class ModActionsPlugin extends Plugin {
embed.color = CaseTypeColors[theCase.type];
}
if (notes.length) {
notes.forEach((note: any) => {
if (theCase.notes.length) {
theCase.notes.forEach((note: any) => {
const noteDate = moment(note.created_at);
embed.fields.push({
name: `${note.mod_name} at ${noteDate.format("YYYY-MM-DD [at] HH:mm")}:`,
@ -1014,7 +1014,7 @@ export class ModActionsPlugin extends Plugin {
public async createCase(
userId: string,
modId: string,
caseType: CaseType,
caseType: CaseTypes,
auditLogId: string = null,
reason: string = null,
automatic = false,

View file

@ -21,7 +21,7 @@ export class PersistPlugin extends Plugin {
}
onLoad() {
this.persistedData = new GuildPersistedData(this.guildId);
this.persistedData = GuildPersistedData.getInstance(this.guildId);
this.logs = new GuildLogs(this.guildId);
}

View file

@ -30,20 +30,17 @@ export class ReactionRolesPlugin extends Plugin {
}
async onLoad() {
this.reactionRoles = new GuildReactionRoles(this.guildId);
this.reactionRoles = GuildReactionRoles.getInstance(this.guildId);
return;
// Pre-fetch all messages with reaction roles so we get their events
const reactionRoles = await this.reactionRoles.all();
const channelMessages: Map<string, Set<string>> = reactionRoles.reduce(
(map: Map<string, Set<string>>, row) => {
if (!map.has(row.channel_id)) map.set(row.channel_id, new Set());
map.get(row.channel_id).add(row.message_id);
return map;
},
new Map()
);
const channelMessages: Map<string, Set<string>> = reactionRoles.reduce((map: Map<string, Set<string>>, row) => {
if (!map.has(row.channel_id)) map.set(row.channel_id, new Set());
map.get(row.channel_id).add(row.message_id);
return map;
}, new Map());
const msgLoadPromises = [];
@ -62,10 +59,7 @@ export class ReactionRolesPlugin extends Plugin {
@d.command("reaction_roles", "<channel:channel> <messageId:string> <reactionRolePairs:string$>")
@d.permission("manage")
async reactionRolesCmd(
msg: Message,
args: { channel: Channel; messageId: string; reactionRolePairs: string }
) {
async reactionRolesCmd(msg: Message, args: { channel: Channel; messageId: string; reactionRolePairs: string }) {
if (!(args.channel instanceof TextChannel)) {
msg.channel.createMessage(errorMessage("Channel must be a text channel!"));
return;
@ -100,9 +94,7 @@ export class ReactionRolesPlugin extends Plugin {
// Verify the specified emojis and roles are valid
for (const pair of newRolePairs) {
if (isSnowflake(pair[0]) && !guildEmojiIds.includes(pair[0])) {
msg.channel.createMessage(
errorMessage("I can only use regular emojis and custom emojis from this server")
);
msg.channel.createMessage(errorMessage("I can only use regular emojis and custom emojis from this server"));
return;
}
@ -113,9 +105,7 @@ export class ReactionRolesPlugin extends Plugin {
}
const oldReactionRoles = await this.reactionRoles.getForMessage(targetMessage.id);
const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map(
r => [r.emoji, r.role_id] as ReactionRolePair
);
const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map(r => [r.emoji, r.role_id] as ReactionRolePair);
// Remove old reaction/role pairs that weren't included in the new pairs or were changed in some way
const toRemove = oldRolePairs.filter(
@ -154,10 +144,7 @@ export class ReactionRolesPlugin extends Plugin {
@d.event("messageReactionAdd")
async onAddReaction(msg: Message, emoji: CustomEmoji, userId: string) {
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(
msg.id,
emoji.id || emoji.name
);
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name);
if (!matchingReactionRole) return;
const member = this.guild.members.get(userId);
@ -168,10 +155,7 @@ export class ReactionRolesPlugin extends Plugin {
@d.event("messageReactionRemove")
async onRemoveReaction(msg: Message, emoji: CustomEmoji, userId: string) {
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(
msg.id,
emoji.id || emoji.name
);
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name);
if (!matchingReactionRole) return;
const member = this.guild.members.get(userId);

View file

@ -1,19 +1,18 @@
import { decorators as d, Plugin } from "knub";
import { Channel, Message, TextChannel, User } from "eris";
import { Channel, Message, User } from "eris";
import {
formatTemplateString,
getEmojiInString,
getRoleMentions,
getUrlsInString,
getUserMentions,
sleep,
stripObjectToScalars,
trimLines
} from "../utils";
import { LogType } from "../data/LogType";
import { GuildLogs } from "../data/GuildLogs";
import { ModActionsPlugin } from "./ModActions";
import { CaseType } from "../data/CaseType";
import { CaseTypes } from "../data/CaseTypes";
import { GuildArchives } from "../data/GuildArchives";
import moment from "moment-timezone";
@ -23,7 +22,8 @@ enum RecentActionType {
Link,
Attachment,
Emoji,
Newline
Newline,
Censor
}
interface IRecentAction {
@ -96,7 +96,7 @@ export class SpamPlugin extends Plugin {
onLoad() {
this.logs = new GuildLogs(this.guildId);
this.archives = new GuildArchives(this.guildId);
this.archives = GuildArchives.getInstance(this.guildId);
this.recentActions = [];
this.expiryInterval = setInterval(() => this.clearOldRecentActions(), 1000 * 60);
@ -260,7 +260,7 @@ export class SpamPlugin extends Plugin {
const logUrl = await this.saveSpamArchives(uniqueMessages, msg.channel, msg.author);
// Create a case and log the actions taken above
const caseType = spamConfig.mute ? CaseType.Mute : CaseType.Note;
const caseType = spamConfig.mute ? CaseTypes.Mute : CaseTypes.Note;
const caseText = trimLines(`
Automatic spam detection: ${description} (over ${spamConfig.count} in ${spamConfig.interval}s)
${logUrl}
@ -297,6 +297,14 @@ export class SpamPlugin extends Plugin {
);
}
// For interoperability with the Censor plugin
async logCensor(msg: Message) {
const spamConfig = this.configValueForMsg(msg, "max_censor");
if (spamConfig) {
this.logAndDetectSpam(msg, RecentActionType.Censor, spamConfig, 1, "too many censored messages");
}
}
@d.event("messageCreate")
async onMessageCreate(msg: Message) {
if (msg.author.bot) return;

View file

@ -1,5 +1,5 @@
import { Plugin, decorators as d } from "knub";
import { Channel, Message, TextChannel } from "eris";
import { Message } from "eris";
import { errorMessage, successMessage } from "../utils";
import { GuildTags } from "../data/GuildTags";
@ -29,7 +29,7 @@ export class TagsPlugin extends Plugin {
}
onLoad() {
this.tags = new GuildTags(this.guildId);
this.tags = GuildTags.getInstance(this.guildId);
}
@d.command("tag", "<tag:string> <body:string$>")

View file

@ -1,19 +1,12 @@
import { Plugin, decorators as d, reply } from "knub";
import { Channel, EmbedOptions, Message, TextChannel, User, VoiceChannel } from "eris";
import {
embedPadding,
errorMessage,
getMessages,
stripObjectToScalars,
successMessage,
trimLines
} from "../utils";
import { embedPadding, errorMessage, getMessages, stripObjectToScalars, successMessage, trimLines } from "../utils";
import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType";
import moment from "moment-timezone";
import humanizeDuration from "humanize-duration";
import { GuildCases } from "../data/GuildCases";
import { CaseType } from "../data/CaseType";
import { CaseTypes } from "../data/CaseTypes";
const MAX_SEARCH_RESULTS = 15;
const MAX_CLEAN_COUNT = 50;
@ -54,7 +47,7 @@ export class UtilityPlugin extends Plugin {
onLoad() {
this.logs = new GuildLogs(this.guildId);
this.cases = new GuildCases(this.guildId);
this.cases = GuildCases.getInstance(this.guildId);
if (activeReloads && activeReloads.has(this.guildId)) {
activeReloads.get(this.guildId).createMessage(successMessage("Reloaded!"));
@ -80,9 +73,7 @@ export class UtilityPlugin extends Plugin {
}
const level = this.getMemberLevel(member);
msg.channel.createMessage(
`The permission level of ${member.username}#${member.discriminator} is **${level}**`
);
msg.channel.createMessage(`The permission level of ${member.username}#${member.discriminator} is **${level}**`);
}
@d.command("search", "<query:string$>")
@ -126,9 +117,7 @@ export class UtilityPlugin extends Plugin {
});
lines = lines.slice(from, to);
const footer = paginated
? "Add a page number to the end of the command to browse results"
: "";
const footer = paginated ? "Add a page number to the end of the command to browse results" : "";
msg.channel.createMessage(`${header}\n\`\`\`${lines.join("\n")}\`\`\`${footer}`);
} else {
@ -152,25 +141,17 @@ export class UtilityPlugin extends Plugin {
@d.permission("clean")
async cleanAllCmd(msg: Message, args: { count: number }) {
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
msg.channel.createMessage(
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
);
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
return;
}
const messagesToClean = await getMessages(
msg.channel as TextChannel,
m => m.id !== msg.id,
args.count
);
const messagesToClean = await getMessages(msg.channel as TextChannel, m => m.id !== msg.id, args.count);
if (messagesToClean.length > 0) {
await this.cleanMessages(msg.channel, messagesToClean.map(m => m.id), msg.author);
}
msg.channel.createMessage(
successMessage(
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
)
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
);
}
@ -178,9 +159,7 @@ export class UtilityPlugin extends Plugin {
@d.permission("clean")
async cleanUserCmd(msg: Message, args: { userId: string; count: number }) {
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
msg.channel.createMessage(
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
);
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
return;
}
@ -194,9 +173,7 @@ export class UtilityPlugin extends Plugin {
}
msg.channel.createMessage(
successMessage(
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
)
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
);
}
@ -204,9 +181,7 @@ export class UtilityPlugin extends Plugin {
@d.permission("clean")
async cleanBotCmd(msg: Message, args: { count: number }) {
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
msg.channel.createMessage(
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
);
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
return;
}
@ -220,9 +195,7 @@ export class UtilityPlugin extends Plugin {
}
msg.channel.createMessage(
successMessage(
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
)
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
);
}
@ -283,7 +256,7 @@ export class UtilityPlugin extends Plugin {
});
const caseSummaries = cases.map(c => {
return `${CaseType[c.type]} (#${c.case_number})`;
return `${CaseTypes[c.type]} (#${c.case_number})`;
});
embed.fields.push({