Feature/vba 77 (#178)

* Add say command (#174)

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/174

* Add repo and funding link to about message (#176)

Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/176

* Add other subreddits to bunny command (#177)

Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/177

* Add database table

* Save moderation actions to database

* Add audit command to see a user's audits

* Add audit view subcommand

* Add audit clear subcommand

* Create add audit subcommand

* Fix bot crashing when viewing an audit with no reason

* Fix changes requested
This commit is contained in:
Vylpes 2022-09-05 18:10:04 +01:00 committed by GitHub
parent 2da5e1aa75
commit cd666d24fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 293 additions and 1 deletions

View file

@ -22,6 +22,7 @@
"email": "helpdesk@vylpes.com"
},
"homepage": "https://github.com/Vylpes/vylbot-app",
"funding": "https://ko-fi.com/vylpes",
"dependencies": {
"@types/jest": "^27.0.3",
"@types/uuid": "^8.3.4",

View file

@ -1,4 +1,4 @@
import { Emoji, MessageActionRow, MessageButton } from "discord.js";
import { MessageActionRow, MessageButton } from "discord.js";
import { MessageButtonStyles } from "discord.js/typings/enums";
import { ICommandContext } from "../contracts/ICommandContext";
import PublicEmbed from "../helpers/embeds/PublicEmbed";

144
src/commands/audits.ts Normal file
View file

@ -0,0 +1,144 @@
import { ICommandContext } from "../contracts/ICommandContext";
import Audit from "../entity/Audit";
import AuditTools from "../helpers/AuditTools";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command";
import SettingsHelper from "../helpers/SettingsHelper";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
export default class Audits extends Command {
constructor() {
super();
super.Category = "Moderation";
super.Roles = [
"moderator"
];
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
switch (context.args[0]) {
case "user":
await this.SendAuditForUser(context);
break;
case "view":
await this.SendAudit(context);
break;
case "clear":
await this.ClearAudit(context);
break;
case "add":
await this.AddAudit(context);
break;
default:
await this.SendUsage(context);
}
}
private async SendUsage(context: ICommandContext) {
const prefix = await SettingsHelper.GetServerPrefix(context.message.guild!.id);
const description = [
`\`${prefix}audits user <id>\` - Send the audits for this user`,
`\`${prefix}audits view <id>\` - Send information about an audit`,
`\`${prefix}audits clear <id>\` - Clears an audit for a user by audit id`,
`\`${prefix}audits add <userid> <type> [reason]\` - Manually add an audit for a user`,
]
const publicEmbed = new PublicEmbed(context, "Usage", description.join("\n"));
await publicEmbed.SendToCurrentChannel();
}
private async SendAuditForUser(context: ICommandContext) {
const userId = context.args[1];
const audits = await Audit.FetchAuditsByUserId(userId, context.message.guild!.id);
if (!audits || audits.length == 0) {
const publicEmbed = new PublicEmbed(context, "", "There are no audits logged for this user.");
await publicEmbed.SendToCurrentChannel();
return;
}
const publicEmbed = new PublicEmbed(context, "Audit Log", "");
for (let audit of audits) {
publicEmbed.addField(`${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, audit.WhenCreated.toString());
}
await publicEmbed.SendToCurrentChannel();
}
private async SendAudit(context: ICommandContext) {
const auditId = context.args[1];
if (!auditId) {
await this.SendUsage(context);
return;
}
const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id);
if (!audit) {
const errorEmbed = new ErrorEmbed(context, "This audit can not be found.");
await errorEmbed.SendToCurrentChannel();
return;
}
const publicEmbed = new PublicEmbed(context, `Audit ${audit.AuditId.toUpperCase()}`, "");
publicEmbed.addField("Reason", audit.Reason || "*none*", true);
publicEmbed.addField("Type", AuditTools.TypeToFriendlyText(audit.AuditType), true);
publicEmbed.addField("Moderator", `<@${audit.ModeratorId}>`, true);
await publicEmbed.SendToCurrentChannel();
}
private async ClearAudit(context: ICommandContext) {
const auditId = context.args[1];
if (!auditId) {
await this.SendUsage(context);
return;
}
const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id);
if (!audit) {
const errorEmbed = new ErrorEmbed(context, "This audit can not be found.");
await errorEmbed.SendToCurrentChannel();
return;
}
await Audit.Remove(Audit, audit);
const publicEmbed = new PublicEmbed(context, "", "Audit cleared");
await publicEmbed.SendToCurrentChannel();
}
private async AddAudit(context: ICommandContext) {
const userId = context.args[1];
const typeString = context.args[2];
const reason = context.args.splice(3)
.join(" ");
if (!userId || !typeString) {
await this.SendUsage(context);
return;
}
const type = AuditTools.StringToType(typeString);
const audit = new Audit(userId, type, reason, context.message.author.id, context.message.guild!.id);
await audit.Save(Audit, audit);
const publicEmbed = new PublicEmbed(context, "", `Created new audit with ID \`${audit.AuditId}\``);
await publicEmbed.SendToCurrentChannel();
}
}

View file

@ -5,6 +5,8 @@ import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit";
import { AuditType } from "../constants/AuditType";
export default class Ban extends Command {
constructor() {
@ -75,6 +77,12 @@ export default class Ban extends Command {
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Ban, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,
embeds: [logEmbed, publicEmbed],

View file

@ -1,6 +1,8 @@
import { AuditType } from "../constants/AuditType";
import ErrorMessages from "../constants/ErrorMessages";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
@ -74,6 +76,12 @@ export default class Kick extends Command {
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Kick, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,

View file

@ -1,6 +1,8 @@
import { AuditType } from "../constants/AuditType";
import ErrorMessages from "../constants/ErrorMessages";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
@ -87,6 +89,12 @@ export default class Mute extends Command {
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Mute, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,

View file

@ -1,5 +1,7 @@
import { AuditType } from "../constants/AuditType";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
@ -62,6 +64,12 @@ export default class Warn extends Command {
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(user.id, AuditType.Warn, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,

View file

@ -0,0 +1,7 @@
export enum AuditType {
General,
Warn,
Mute,
Kick,
Ban,
}

56
src/entity/Audit.ts Normal file
View file

@ -0,0 +1,56 @@
import { Column, Entity, getConnection, ManyToOne } from "typeorm";
import { AuditType } from "../constants/AuditType";
import BaseEntity from "../contracts/BaseEntity";
import StringTools from "../helpers/StringTools";
@Entity()
export default class Audit extends BaseEntity {
constructor(userId: string, auditType: AuditType, reason: string, moderatorId: string, serverId: string) {
super();
this.AuditId = StringTools.RandomString(5).toUpperCase();
this.UserId = userId;
this.AuditType = auditType;
this.Reason = reason;
this.ModeratorId = moderatorId;
this.ServerId = serverId;
}
@Column()
AuditId: string;
@Column()
UserId: string;
@Column()
AuditType: AuditType;
@Column()
Reason: string;
@Column()
ModeratorId: string;
@Column()
ServerId: string;
public static async FetchAuditsByUserId(userId: string, serverId: string): Promise<Audit[] | undefined> {
const connection = getConnection();
const repository = connection.getRepository(Audit);
const all = await repository.find({ UserId: userId, ServerId: serverId });
return all;
}
public static async FetchAuditByAuditId(auditId: string, serverId: string): Promise<Audit | undefined> {
const connection = getConnection();
const repository = connection.getRepository(Audit);
const single = await repository.findOne({ AuditId: auditId, ServerId: serverId });
return single;
}
}

37
src/helpers/AuditTools.ts Normal file
View file

@ -0,0 +1,37 @@
import { AuditType } from "../constants/AuditType";
export default class AuditTools {
public static TypeToFriendlyText(auditType: AuditType): string {
switch (auditType) {
case AuditType.General:
return "General";
case AuditType.Warn:
return "Warn";
case AuditType.Mute:
return "Mute";
case AuditType.Kick:
return "Kick";
case AuditType.Ban:
return "Ban";
default:
return "Other";
}
}
public static StringToType(str: string): AuditType {
switch (str.toLowerCase()) {
case "general":
return AuditType.General;
case "warn":
return AuditType.Warn;
case "mute":
return AuditType.Mute;
case "kick":
return AuditType.Kick;
case "ban":
return AuditType.Ban;
default:
return AuditType.General;
}
}
}

View file

@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from "typeorm"
export class CreateAudit1660754832945 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
queryRunner.query(`CREATE TABLE audit (Id varchar(255), WhenCreated datetime, WhenUpdated datetime, auditId varchar(255), userId varchar(255), auditType int, reason varchar(255), moderatorId varchar(255), serverId varchar(255), PRIMARY KEY (Id))`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
queryRunner.query(`DROP TABLE audit`);
}
}

View file

@ -2,6 +2,7 @@ import { CoreClient } from "./client/client";
// Command Imports
import About from "./commands/about";
import Audits from "./commands/audits";
import Ban from "./commands/ban";
import Clear from "./commands/clear";
import Code from "./commands/code";
@ -48,6 +49,7 @@ export default class Registry {
CoreClient.RegisterCommand("warn", new Warn());
CoreClient.RegisterCommand("setup", new Setup());
CoreClient.RegisterCommand("say", new Say());
CoreClient.RegisterCommand("audits", new Audits());
// Exclusive Commands: MankBot
CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357");