Skip to content

Commit

Permalink
Merge pull request #565 from aternosorg/audit-log
Browse files Browse the repository at this point in the history
Listen to the audit log event
  • Loading branch information
JulianVennen authored Apr 28, 2023
2 parents eb0d364 + e70b5b0 commit 132d341
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 2,671 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ has punished this user in the last 5 minutes.

### Add ModBot to your server
By adding the bot to your server you agree to our [privacy policy](https://aternos.gmbh/en/modbot/privacy). <br>
Invite: [Click me](https://discord.com/oauth2/authorize?client_id=790967448111153153&scope=bot&permissions=268446806)
Invite: [Click me](https://discord.com/oauth2/authorize?client_id=790967448111153153&scope=bot%20applications.commands&permissions=1099780074646)

You can view all commands by typing a slash `/` in the text input field.
All commands and options have clear descriptions.
Expand Down Expand Up @@ -57,7 +57,7 @@ In both cases you will need a [MySQL](https://dev.mysql.com/downloads/mysql/) da
2. Add a bot to the application and copy the auth token
3. Configure the bot (see [CONFIGURATION.md](./CONFIGURATION.md))
4. To invite the bot to your server replace `ID` with the client ID of your application
https://discord.com/oauth2/authorize?client_id=ID&scope=bot%20applications.commands&permissions=1099780074518 and open the link
https://discord.com/oauth2/authorize?client_id=ID&scope=bot%20applications.commands&permissions=1099780074646 and open the link
5. Follow the instructions for the installation method you want to use

#### Docker
Expand Down
2,714 changes: 120 additions & 2,594 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"@google-cloud/vision": "^3.1.2",
"@googleapis/youtube": "^8.0.2",
"diff": "^5.1.0",
"discord-api-types": "^0.37.36",
"discord.js": "^14.8.0",
"discord-api-types": "^0.37.37",
"discord.js": "^14.9.0",
"fuse.js": "^6.6.2",
"got": "^12.6.0",
"mysql2": "^3.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/bot/Bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class Bot {
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildBans,
GatewayIntentBits.GuildModeration,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.DirectMessages,
Expand Down
5 changes: 3 additions & 2 deletions src/commands/bot/InfoCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ export const PERMISSIONS = new PermissionsBitField()
.add(PermissionFlagsBits.BanMembers)
.add(PermissionFlagsBits.ModerateMembers)
.add(PermissionFlagsBits.ManageMessages)
.add(PermissionFlagsBits.SendMessages);
export const INVITE_LINK = `https://discordapp.com/oauth2/authorize?client_id=${CLIENT_ID}&scope=${SCOPES.join('%20')}&permissions=${PERMISSIONS.bitfield}`;
.add(PermissionFlagsBits.SendMessages)
.add(PermissionFlagsBits.ViewAuditLog);
export const INVITE_LINK = `https://discord.com/oauth2/authorize?client_id=${CLIENT_ID}&scope=${SCOPES.join('%20')}&permissions=${PERMISSIONS.bitfield}`;

export const VERSION = await getPackageVersion();
async function getPackageVersion() {
Expand Down
53 changes: 51 additions & 2 deletions src/database/Moderation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import TypeChecker from '../settings/TypeChecker.js';
import database from '../bot/Database.js';
import UserWrapper from '../discord/UserWrapper.js';
import WhereParameter from './WhereParameter.js';
import KeyValueEmbed from '../embeds/KeyValueEmbed.js';
import {resolveColor} from '../util/colors.js';
import {toTitleCase} from '../util/format.js';
import {userMention} from 'discord.js';
import {formatTime} from '../util/timeutils.js';
import MemberWrapper from '../discord/MemberWrapper.js';
import GuildWrapper from '../discord/GuildWrapper.js';

export default class Moderation {

Expand Down Expand Up @@ -214,14 +221,40 @@ export default class Moderation {
...this.getParameters(), this.id);
}
else {
const result = await database.query(
`INSERT INTO moderations (${fields.join(', ')}) VALUES ${'?'.repeat(fields.length)}`,
const result = await database.queryAll(
`INSERT INTO moderations (${fields.join(', ')}) VALUES (${fields.map(() => '?').join(', ')})`,
...this.getParameters());
this.id = result.insertId;
}
return this.id;
}

/**
* log this moderation to the guild's log channel
* @param {?number} total total strike count
* @return {Promise<Moderation>}
*/
async log(total = null) {
const user = await this.getUser();
return (await this.getGuildWrapper()).log({
embeds: [
new KeyValueEmbed()
.setColor(resolveColor(this.action))
.setAuthor({
name: `Case ${this.id} | ${toTitleCase(this.action)} | ${user.tag}`,
iconURL: user.avatarURL()
})
.setFooter({text: user.id})
.addPair('User', userMention(user.id))
.addPair('Moderator', userMention((await this.getModerator()).id))
.addPairIf(this.expireTime, 'Duration', formatTime(this.expireTime - this.created))
.addPairIf(this.value, 'Amount', this.value)
.addPairIf(total, 'Total Strikes', total)
.addPair('Reason', this.reason.substring(0, 1024))
]
});
}

/**
* Delete this moderation from the database
* @return {Promise}
Expand All @@ -246,6 +279,22 @@ export default class Moderation {
return await (new UserWrapper(this.userid)).fetchUser();
}

/**
* fetch the guild this moderation was executed in.
* @return {Promise<GuildWrapper>}
*/
async getGuildWrapper() {
return await GuildWrapper.fetch(this.guildid);
}

/**
* fetch the user targeted by this moderation.
* @return {Promise<MemberWrapper>}
*/
async getMemberWrapper() {
return new MemberWrapper(await this.getUser(), await GuildWrapper.fetch(this.guildid));
}

/**
* fetch the moderator who executed this moderation.
* @return {Promise<?import('discord.js').User>}
Expand Down
103 changes: 35 additions & 68 deletions src/discord/MemberWrapper.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import GuildSettings from '../settings/GuildSettings.js';
import {bold, Guild, RESTJSONErrorCodes, userMention} from 'discord.js';
import {bold, Guild, RESTJSONErrorCodes} from 'discord.js';
import {formatTime, parseTime} from '../util/timeutils.js';
import database from '../bot/Database.js';
import GuildWrapper from './GuildWrapper.js';
import {resolveColor} from '../util/colors.js';
import {toTitleCase} from '../util/format.js';
import {BAN_MESSAGE_DELETE_LIMIT, TIMEOUT_LIMIT} from '../util/apiLimits.js';
import Moderation from '../database/Moderation.js';
import UserWrapper from './UserWrapper.js';
import KeyValueEmbed from '../embeds/KeyValueEmbed.js';
import ErrorEmbed from '../embeds/ErrorEmbed.js';

export default class MemberWrapper {
Expand Down Expand Up @@ -271,12 +268,11 @@ export default class MemberWrapper {
*/
async strike(reason, moderator, amount = 1){
await this.dmPunishedUser('striked', reason, null, 'in');
const id = await this.addModeration('strike', reason, null, moderator.id, amount);
const moderation = await this.createModeration('strike', reason, null, moderator.id, amount);
const total = await this.getStrikeSum();
await Promise.all([
this.#logModeration(moderator, reason, id, 'strike', null, amount, total),
this.executePunishment((await this.getGuildSettings()).findPunishment(total), `Reaching ${total} strikes`, true)
]);
await moderation.log(total);
const punishment = (await this.getGuildSettings()).findPunishment(total);
await this.executePunishment(punishment, `Reaching ${total} strikes`, true);
}

/**
Expand Down Expand Up @@ -342,8 +338,7 @@ export default class MemberWrapper {
async pardon(reason, moderator, amount = 1){
await this.guild.sendDM(this.user, `${amount} strikes have been pardoned in ${bold(this.guild.guild.name)} | ${reason}`);

const id = await this.addModeration('pardon', reason, null, moderator.id, -amount);
await this.#logModeration(moderator, reason, id, 'pardon', null, amount, await this.getStrikeSum());
await (await this.createModeration('pardon', reason, null, moderator.id, -amount)).log();
}

/**
Expand All @@ -365,8 +360,7 @@ export default class MemberWrapper {
reason: this._shortenReason(`${moderator.tag} ${duration ? `(${formatTime(duration)}) ` : ''}| ${reason}`),
deleteMessageSeconds,
});
const id = await this.addModeration('ban', reason, duration, moderator.id);
await this.#logModeration(moderator, reason, id, 'ban', formatTime(duration));
await (await this.createModeration('ban', reason, duration, moderator.id)).log();
}

/**
Expand All @@ -385,8 +379,7 @@ export default class MemberWrapper {
throw e;
}
}
const id = await this.addModeration('unban', reason, null, moderator.id);
await this.#logModeration(moderator, reason, id, 'unban');
await (await this.createModeration('unban', reason, null, moderator.id)).log();
}

/**
Expand All @@ -408,8 +401,7 @@ export default class MemberWrapper {
reason: this._shortenReason(`${moderator.tag} | ${reason}`)
});
await this.guild.guild.members.unban(this.user.id, 'softban');
const id = await this.addModeration('softban', reason, null, moderator.id);
await this.#logModeration(moderator, reason, id, 'softban');
await (await this.createModeration('softban', reason, null, moderator.id)).log();
}

/**
Expand All @@ -422,8 +414,7 @@ export default class MemberWrapper {
await this.dmPunishedUser('kicked', reason, null, 'from');
if (!this.member && await this.fetchMember() === null) return;
await this.member.kick(this._shortenReason(`${moderator.tag} | ${reason}`));
const id = await this.addModeration('kick', reason, null, moderator.id);
await this.#logModeration(moderator, reason, id, 'kick');
await (await this.createModeration('kick', reason, null, moderator.id)).log();
}

/**
Expand Down Expand Up @@ -452,8 +443,7 @@ export default class MemberWrapper {
await this.member.roles.add(mutedRole, shortedReason);
}
}
const id = await this.addModeration('mute', reason, duration, moderator.id);
await this.#logModeration(moderator, reason, id, 'mute', formatTime(duration));
await (await this.createModeration('mute', reason, duration, moderator.id)).log();
}

/**
Expand All @@ -472,28 +462,36 @@ export default class MemberWrapper {
await this.member.timeout(null);
}
await this.disableActiveModerations('mute');
const id = await this.addModeration('unmute', reason, null, moderator.id);
await this.#logModeration(moderator, reason, id, 'unmute');
await (await this.createModeration('unmute', reason, null, moderator.id)).log();
}

/**
* add a moderation
* @param {String} action moderation type (e.g. 'ban')
* @param {String} reason reason for the moderation
* @param {Number} [duration] duration of the moderation
* @param {import('discord.js').Snowflake} [moderatorId] id of the moderator
* @param {Number} [value] strike count
* @return {Promise<Number>} the id of the moderation
* create a new moderation and save it to the database
* @param {string} action moderation type (e.g. 'ban')
* @param {?String} reason reason for the moderation
* @param {?number} duration duration of the moderation
* @param {import('discord.js').Snowflake} moderatorId id of the moderator
* @param {number} [value] value of the moderation (e.g. strike count)
* @return {Promise<Moderation>}
*/
async addModeration(action, reason, duration, moderatorId, value= 0) {
async createModeration(action, reason, duration, moderatorId, value = 0) {
await this.disableActiveModerations(action);

const now = Math.floor(Date.now()/1000);
/** @property {Number} insertId*/
const insert = await database.queryAll(
'INSERT INTO moderations (guildid, userid, action, created, expireTime, reason, moderator, value) VALUES (?,?,?,?,?,?,?,?)',
this.guild.guild.id, this.user.id, action, now, duration ? now + duration : null, reason, moderatorId, value);
return insert.insertId;
const created = Math.floor(Date.now() / 1000);
const moderation = new Moderation({
guildid: this.guild.guild.id,
userid: this.user.id,
action,
created,
value,
reason,
expireTime: duration ? created + duration : null,
moderator: moderatorId,
active: true,
});

await moderation.save();
return moderation;
}

/**
Expand All @@ -507,37 +505,6 @@ export default class MemberWrapper {
this.guild.guild.id, this.user.id, type);
}

/**
*
* @param {import('discord.js').User} moderator
* @param {string} reason
* @param {number} id
* @param {string} type
* @param {?string} time
* @param {?number} amount
* @param {?number} total
* @return {Promise<?Message>}
*/
async #logModeration(moderator, reason, id, type, time = null, amount = null, total = null) {
return this.guild.log({
embeds: [
new KeyValueEmbed()
.setColor(resolveColor(type))
.setAuthor({
name: `Case ${id} | ${toTitleCase(type)} | ${this.user.tag}`,
iconURL: this.user.avatarURL()
})
.setFooter({text: this.user.id})
.addPair('User', userMention(this.user.id))
.addPair('Moderator', userMention(moderator.id))
.addPairIf(time, 'Duration', time)
.addPairIf(amount, 'Amount', amount)
.addPairIf(amount, 'Total Strikes', total)
.addPair('Reason', reason.substring(0, 1024))
]
});
}

/**
* send the user a dm about this punishment
* @param {String} verb
Expand Down
2 changes: 2 additions & 0 deletions src/events/discord/DiscordEventManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import WarnEventListener from './WarnEventListener.js';
import CommandEventListener from './interactionCreate/CommandEventListener.js';
import DeleteConfirmationEventListener from './interactionCreate/DeleteConfirmationEventListener.js';
import AutoModMessageEditEventListener from './messageUpdate/AutoModMessageEditEventListener.js';
import GuildAuditLogCreateEventListener from './GuildAuditLogCreateEventListener.js';

export default class DiscordEventManager extends EventManager {

Expand All @@ -31,6 +32,7 @@ export default class DiscordEventManager extends EventManager {
new BanRemoveEventListener(),
new GuildDeleteEventListener(),
new WarnEventListener(),
new GuildAuditLogCreateEventListener(),

// members
// join
Expand Down
Loading

0 comments on commit 132d341

Please sign in to comment.