Add !deletecase
This commit is contained in:
parent
8826b2521d
commit
ddbbc543c2
9 changed files with 146 additions and 6 deletions
|
@ -6,6 +6,6 @@ export enum CaseTypes {
|
|||
Kick,
|
||||
Mute,
|
||||
Unmute,
|
||||
Expunged,
|
||||
Deleted,
|
||||
Softban,
|
||||
}
|
||||
|
|
|
@ -66,5 +66,7 @@
|
|||
"AUTOMOD_ACTION": "\uD83E\uDD16 Automod rule **{rule}** triggered by {userMention(users)}\n{matchSummary}\nActions taken: **{actionsTaken}**",
|
||||
|
||||
"SET_ANTIRAID_USER": "⚔ {userMention(user)} set anti-raid to **{level}**",
|
||||
"SET_ANTIRAID_AUTO": "⚔ Anti-raid automatically set to **{level}**"
|
||||
"SET_ANTIRAID_AUTO": "⚔ Anti-raid automatically set to **{level}**",
|
||||
|
||||
"CASE_DELETE": "✂️ **Case #{case.case_number}** was deleted by {userMention(mod)}"
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { getRepository, In, Repository } from "typeorm";
|
|||
import { disableLinkPreviews } from "../utils";
|
||||
import { CaseTypes } from "./CaseTypes";
|
||||
import moment = require("moment-timezone");
|
||||
import { connection } from "./db";
|
||||
|
||||
const CASE_SUMMARY_REASON_MAX_LENGTH = 300;
|
||||
|
||||
|
@ -119,6 +120,37 @@ export class GuildCases extends BaseGuildRepository {
|
|||
return this.cases.update(id, data);
|
||||
}
|
||||
|
||||
async softDelete(id: number, deletedById: string, deletedByName: string, deletedByText: string) {
|
||||
return connection.transaction(async entityManager => {
|
||||
const cases = entityManager.getRepository(Case);
|
||||
const caseNotes = entityManager.getRepository(CaseNote);
|
||||
|
||||
await Promise.all([
|
||||
caseNotes.delete({
|
||||
case_id: id,
|
||||
}),
|
||||
cases.update(id, {
|
||||
user_id: "0",
|
||||
user_name: "Unknown#0000",
|
||||
mod_id: null,
|
||||
mod_name: "Unknown#0000",
|
||||
type: CaseTypes.Deleted,
|
||||
audit_log_id: null,
|
||||
is_hidden: false,
|
||||
pp_id: null,
|
||||
pp_name: null,
|
||||
}),
|
||||
]);
|
||||
|
||||
await caseNotes.insert({
|
||||
case_id: id,
|
||||
mod_id: deletedById,
|
||||
mod_name: deletedByName,
|
||||
body: deletedByText,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async createNote(caseId: number, data: any): Promise<void> {
|
||||
await this.caseNotes.insert({
|
||||
...data,
|
||||
|
|
|
@ -75,4 +75,6 @@ export enum LogType {
|
|||
MASS_UNASSIGN_ROLES,
|
||||
|
||||
MEMBER_NOTE,
|
||||
|
||||
CASE_DELETE,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
import { TableForeignKey } from "typeorm/index";
|
||||
|
||||
export class AddCaseNotesForeignKey1596994103885 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.createForeignKey(
|
||||
"case_notes",
|
||||
new TableForeignKey({
|
||||
name: "case_notes_case_id_fk",
|
||||
columnNames: ["case_id"],
|
||||
referencedTableName: "cases",
|
||||
referencedColumnNames: ["id"],
|
||||
onDelete: "CASCADE",
|
||||
onUpdate: "CASCADE",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.dropForeignKey("case_notes", "case_notes_case_id_fk");
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { Case } from "../../../data/entities/Case";
|
||||
import { MessageContent } from "eris";
|
||||
import { AdvancedMessageContent, MessageContent } from "eris";
|
||||
import moment from "moment-timezone";
|
||||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
import { PluginData, helpers } from "knub";
|
||||
|
@ -11,13 +11,19 @@ import { chunkLines, chunkMessageLines, emptyEmbedValue } from "../../../utils";
|
|||
export async function getCaseEmbed(
|
||||
pluginData: PluginData<CasesPluginType>,
|
||||
caseOrCaseId: Case | number,
|
||||
): Promise<MessageContent> {
|
||||
): Promise<AdvancedMessageContent> {
|
||||
const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId));
|
||||
if (!theCase) return null;
|
||||
|
||||
const createdAt = moment(theCase.created_at);
|
||||
const actionTypeStr = CaseTypes[theCase.type].toUpperCase();
|
||||
|
||||
let userName = theCase.user_name;
|
||||
if (theCase.user_id && theCase.user_id !== "0") userName += `\n<@!${theCase.user_id}>`;
|
||||
|
||||
let modName = theCase.mod_name;
|
||||
if (theCase.mod_id) modName += `\n<@!${theCase.mod_id}>`;
|
||||
|
||||
const embed: any = {
|
||||
title: `${actionTypeStr} - Case #${theCase.case_number}`,
|
||||
footer: {
|
||||
|
@ -26,12 +32,12 @@ export async function getCaseEmbed(
|
|||
fields: [
|
||||
{
|
||||
name: "User",
|
||||
value: `${theCase.user_name}\n<@!${theCase.user_id}>`,
|
||||
value: userName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Moderator",
|
||||
value: `${theCase.mod_name}\n<@!${theCase.mod_id}>`,
|
||||
value: modName,
|
||||
inline: true,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -34,6 +34,7 @@ import { kickMember } from "./functions/kickMember";
|
|||
import { banUserId } from "./functions/banUserId";
|
||||
import { MassmuteCmd } from "./commands/MassmuteCmd";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { DeleteCaseCmd } from "./commands/DeleteCaseCmd";
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -65,6 +66,7 @@ const defaultOptions = {
|
|||
can_massban: false,
|
||||
can_massmute: false,
|
||||
can_hidecase: false,
|
||||
can_deletecase: false,
|
||||
can_act_as_other: false,
|
||||
},
|
||||
overrides: [
|
||||
|
@ -134,6 +136,7 @@ export const ModActionsPlugin = zeppelinPlugin<ModActionsPluginType>()("mod_acti
|
|||
CasesModCmd,
|
||||
HideCaseCmd,
|
||||
UnhideCaseCmd,
|
||||
DeleteCaseCmd,
|
||||
],
|
||||
|
||||
public: {
|
||||
|
|
72
backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts
Normal file
72
backend/src/plugins/ModActions/commands/DeleteCaseCmd.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { modActionsCommand } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { helpers } from "knub";
|
||||
import { CasesPlugin } from "../../Cases/CasesPlugin";
|
||||
import { TextChannel } from "eris";
|
||||
import { SECONDS, stripObjectToScalars, trimLines } from "../../../utils";
|
||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
export const DeleteCaseCmd = modActionsCommand({
|
||||
trigger: ["delete_case", "deletecase"],
|
||||
permission: "can_deletecase",
|
||||
description: trimLines(`
|
||||
Delete the specified case. This operation can *not* be reversed.
|
||||
It is generally recommended to use \`!hidecase\` instead when possible.
|
||||
`),
|
||||
|
||||
signature: {
|
||||
caseNumber: ct.number(),
|
||||
|
||||
force: ct.switchOption({ shortcut: "f" }),
|
||||
},
|
||||
|
||||
async run({ pluginData, message, args }) {
|
||||
const theCase = await pluginData.state.cases.findByCaseNumber(args.caseNumber);
|
||||
if (!theCase) {
|
||||
sendErrorMessage(pluginData, message.channel, "Case not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.force) {
|
||||
const cases = pluginData.getPlugin(CasesPlugin);
|
||||
const embedContent = await cases.getCaseEmbed(theCase);
|
||||
message.channel.createMessage({
|
||||
content: "Delete the following case? Answer 'Yes' to continue, 'No' to cancel.",
|
||||
embed: embedContent.embed,
|
||||
});
|
||||
|
||||
const reply = await helpers.waitForReply(
|
||||
pluginData.client,
|
||||
message.channel as TextChannel,
|
||||
message.author.id,
|
||||
15 * SECONDS,
|
||||
);
|
||||
const normalizedReply = (reply?.content || "").toLowerCase().trim();
|
||||
if (normalizedReply !== "yes" && normalizedReply !== "y") {
|
||||
message.channel.createMessage("Cancelled. Case was not deleted.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const deletedByName = `${message.author.username}#${message.author.discriminator}`;
|
||||
const deletedAt = moment().format(`MMM D, YYYY [at] H:mm [UTC]`);
|
||||
|
||||
await pluginData.state.cases.softDelete(
|
||||
theCase.id,
|
||||
message.author.id,
|
||||
deletedByName,
|
||||
`Case deleted by **${deletedByName}** (\`${message.author.id}\`) on ${deletedAt}`,
|
||||
);
|
||||
|
||||
const logs = pluginData.getPlugin(LogsPlugin);
|
||||
logs.log(LogType.CASE_DELETE, {
|
||||
mod: stripObjectToScalars(message.member, ["user", "roles"]),
|
||||
case: stripObjectToScalars(theCase),
|
||||
});
|
||||
|
||||
sendSuccessMessage(pluginData, message.channel, `Case #${theCase.case_number} deleted!`);
|
||||
},
|
||||
});
|
|
@ -35,6 +35,7 @@ export const ConfigSchema = t.type({
|
|||
can_massban: t.boolean,
|
||||
can_massmute: t.boolean,
|
||||
can_hidecase: t.boolean,
|
||||
can_deletecase: t.boolean,
|
||||
can_act_as_other: t.boolean,
|
||||
});
|
||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
|
Loading…
Add table
Reference in a new issue