aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/config/features.ts1
-rw-r--r--src/commands/config/log.ts86
-rw-r--r--src/lib/models/Guild.ts115
-rw-r--r--src/lib/models/__helpers.ts6
-rw-r--r--src/listeners/message/automodCreate.ts39
5 files changed, 182 insertions, 65 deletions
diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts
index 7a7c3bb..3c607c7 100644
--- a/src/commands/config/features.ts
+++ b/src/commands/config/features.ts
@@ -19,6 +19,7 @@ export default class FeaturesCommand extends BushCommand {
}
public override async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`);
+
const featureEmbed = new MessageEmbed().setTitle(`${message.guild!.name}'s Features`).setColor(util.colors.default);
const enabledFeatures = await message.guild!.getSetting('enabledFeatures');
diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts
new file mode 100644
index 0000000..592f700
--- /dev/null
+++ b/src/commands/config/log.ts
@@ -0,0 +1,86 @@
+import { BushCommand, BushMessage, BushSlashMessage, guildLogsArr, GuildLogType } from '@lib';
+import { ArgumentOptions, Flag } from 'discord-akairo';
+import { TextChannel } from 'discord.js';
+
+export default class LogCommand extends BushCommand {
+ public constructor() {
+ super('log', {
+ aliases: ['log', 'logging'],
+ category: 'config',
+ description: {
+ content: 'Set or remove a log channel.',
+ usage: 'log <logType> [channel]',
+ examples: ['log automod #automod-logs']
+ },
+ slash: true,
+ slashOptions: [
+ {
+ name: 'log_type',
+ description: 'What log type would you like to change?',
+ type: 'STRING',
+ required: true,
+ choices: guildLogsArr.map((log) => ({ name: log, value: log }))
+ },
+ {
+ name: 'channel',
+ description: 'What channel would you like these logs to be sent in?',
+ type: 'CHANNEL',
+ required: false
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ });
+ }
+
+ *args(): IterableIterator<ArgumentOptions | Flag> {
+ const log_type = yield {
+ id: 'log',
+ type: guildLogsArr,
+ prompt: {
+ start: 'What log type would you like to change?',
+ retry: `{error} Choose either ${util.oxford(
+ guildLogsArr.map((l) => `\`${l}\``),
+ 'or'
+ )}`,
+ optional: false
+ }
+ };
+
+ const channel = yield {
+ id: 'channel',
+ type: 'textChannel',
+ prompt: {
+ start: `What channel would you like ${log_type} logs to be sent in?`,
+ retry: `{error} Choose a valid text channel for ${log_type} logs to be sent in.`
+ }
+ };
+
+ return { log_type, channel };
+ }
+
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { log_type: GuildLogType; channel: TextChannel }
+ ): Promise<unknown> {
+ if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`);
+ const currentLogs = await message.guild.getSetting('logChannels');
+ const oldChannel = currentLogs[args.log_type] ?? undefined;
+
+ const action = !!args.channel;
+
+ action ? (currentLogs[args.log_type] = args.channel.id) : delete currentLogs[args.log_type];
+
+ const success = await message.guild.setSetting('logChannels', currentLogs);
+ return await message.util.reply(
+ `${
+ success
+ ? `${util.emojis.success} Successfully ${oldChannel ? 'changed' : 'set'}`
+ : `${util.emojis.error} Unable to ${oldChannel ? 'change' : 'set'}`
+ } ${
+ oldChannel ? ` the \`${args.log_type}\` log channel from <#${oldChannel}>` : ` the \`${args.log_type}\` log channel`
+ } to ${args.channel ? `<#${args.channel.id}>` : '`disabled`'}`
+ );
+ }
+}
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index 1974725..38b5127 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -2,41 +2,7 @@ import { Snowflake } from 'discord.js';
import { DataTypes, Sequelize } from 'sequelize';
import { BushClient } from '../extensions/discord-akairo/BushClient';
import { BaseModel } from './BaseModel';
-import { jsonArrayInit, NEVER_USED } from './__helpers';
-
-export interface GuildModel {
- id: Snowflake;
- prefix: string;
- autoPublishChannels: Snowflake[];
- blacklistedChannels: Snowflake[];
- blacklistedUsers: Snowflake[];
- welcomeChannel: Snowflake;
- muteRole: Snowflake;
- punishmentEnding: string;
- disabledCommands: string[];
- lockdownChannels: Snowflake[];
- autoModPhases: string[];
- enabledFeatures: GuildFeatures[];
- joinRoles: Snowflake[];
- automodLogChannel: Snowflake;
-}
-
-export interface GuildModelCreationAttributes {
- id: Snowflake;
- prefix?: string;
- autoPublishChannels?: Snowflake[];
- blacklistedChannels?: Snowflake[];
- blacklistedUsers?: Snowflake[];
- welcomeChannel?: Snowflake;
- muteRole?: Snowflake;
- punishmentEnding?: string;
- disabledCommands?: string[];
- lockdownChannels?: Snowflake[];
- autoModPhases?: string[];
- enabledFeatures?: GuildFeatures[];
- joinRoles?: Snowflake[];
- automodLogChannel?: Snowflake;
-}
+import { jsonArrayInit, jsonParseGet, jsonParseSet, NEVER_USED } from './__helpers';
export const guildSettingsObj = {
prefix: {
@@ -80,12 +46,6 @@ export const guildSettingsObj = {
description: 'Roles assigned to users on join who do not have sticky role information.',
type: 'role-array',
configurable: true
- },
- automodLogChannel: {
- name: 'Automod Log Channel',
- description: 'The channel where all automod information is sent.',
- type: 'channel',
- configurable: true
}
};
export type GuildSettings = keyof typeof guildSettingsObj;
@@ -121,12 +81,66 @@ export const guildFeaturesObj = {
stickyRoles: {
name: 'Sticky Roles',
description: 'Restores past roles to a user when they rejoin.'
+ },
+ modsCanPunishMods: {
+ name: 'Mods Can Punish Mods',
+ description: 'Allow moderators to punish other moderators.'
}
};
+export const guildLogsObj = {
+ automod: {
+ description: 'Sends a message in this channel every time automod is activated.',
+ configurable: true
+ },
+ moderation: {
+ description: 'Sends a message in this channel every time a moderation action is performed.',
+ configurable: true
+ }
+};
+export type GuildLogType = keyof typeof guildLogsObj;
+export const guildLogsArr = Object.keys(guildLogsObj).filter(
+ (s) => guildLogsObj[s as GuildLogType].configurable
+) as GuildLogType[];
+type LogChannelDB = { [x in keyof typeof guildLogsObj]?: Snowflake };
+
export type GuildFeatures = keyof typeof guildFeaturesObj;
export const guildFeaturesArr: GuildFeatures[] = Object.keys(guildFeaturesObj) as GuildFeatures[];
+export interface GuildModel {
+ id: Snowflake;
+ prefix: string;
+ autoPublishChannels: Snowflake[];
+ blacklistedChannels: Snowflake[];
+ blacklistedUsers: Snowflake[];
+ welcomeChannel: Snowflake;
+ muteRole: Snowflake;
+ punishmentEnding: string;
+ disabledCommands: string[];
+ lockdownChannels: Snowflake[];
+ autoModPhases: string[];
+ enabledFeatures: GuildFeatures[];
+ joinRoles: Snowflake[];
+ logChannels: LogChannelDB;
+}
+
+export interface GuildModelCreationAttributes {
+ id: Snowflake;
+ prefix?: string;
+ autoPublishChannels?: Snowflake[];
+ blacklistedChannels?: Snowflake[];
+ blacklistedUsers?: Snowflake[];
+ welcomeChannel?: Snowflake;
+ muteRole?: Snowflake;
+ punishmentEnding?: string;
+ disabledCommands?: string[];
+ lockdownChannels?: Snowflake[];
+ autoModPhases?: string[];
+ enabledFeatures?: GuildFeatures[];
+ joinRoles?: Snowflake[];
+ logChannels?: LogChannelDB;
+}
+
export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> implements GuildModel {
/**
* The ID of the guild
@@ -259,12 +273,12 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
}
/**
- * The channel to send automod logs to.
+ * The channels where logging messages will be sent.
*/
- public get automodLogChannel(): Snowflake {
+ public get logChannels(): LogChannelDB {
throw new Error(NEVER_USED);
}
- public set automodLogChannel(_: Snowflake) {
+ public set logChannels(_: LogChannelDB) {
throw new Error(NEVER_USED);
}
@@ -300,9 +314,16 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
autoModPhases: jsonArrayInit('autoModPhases'),
enabledFeatures: jsonArrayInit('enabledFeatures'),
joinRoles: jsonArrayInit('joinRoles'),
- automodLogChannel: {
- type: DataTypes.STRING,
- allowNull: true
+ logChannels: {
+ type: DataTypes.TEXT,
+ get: function (): LogChannelDB {
+ return jsonParseGet('logChannels', this);
+ },
+ set: function (val: LogChannelDB) {
+ return jsonParseSet('logChannels', this, val);
+ },
+ allowNull: false,
+ defaultValue: '{}'
}
},
{ sequelize: sequelize }
diff --git a/src/lib/models/__helpers.ts b/src/lib/models/__helpers.ts
index f558ecb..b65c014 100644
--- a/src/lib/models/__helpers.ts
+++ b/src/lib/models/__helpers.ts
@@ -1,17 +1,17 @@
import { DataTypes, Model } from 'sequelize';
export const NEVER_USED = 'This should never be executed';
-function jsonParseGet(key: string, that: Model): any {
+export function jsonParseGet(key: string, that: Model): any {
return JSON.parse(that.getDataValue(key));
}
-function jsonParseSet(key: string, that: Model, value: any): any {
+export function jsonParseSet(key: string, that: Model, value: any): any {
return that.setDataValue(key, JSON.stringify(value));
}
export function jsonArrayInit(key: string): any {
return {
type: DataTypes.TEXT,
- get: function () {
+ get: function (): string[] {
return jsonParseGet(key, this);
},
set: function (val: string[]) {
diff --git a/src/listeners/message/automodCreate.ts b/src/listeners/message/automodCreate.ts
index 638e8d5..5993e7a 100644
--- a/src/listeners/message/automodCreate.ts
+++ b/src/listeners/message/automodCreate.ts
@@ -24,7 +24,9 @@ export default class AutomodMessageCreateListener extends BushListener {
public static async automod(message: BushMessage): Promise<unknown> {
if (message.channel.type === 'DM' || !message.guild) return;
if (!(await message.guild.hasFeature('automod'))) return;
+
/* await message.guild.getSetting('autoModPhases'); */
+
const badLinks: { [key: string]: number } = {};
let temp = _badLinks;
if (_badLinksSecret) temp = temp.concat(_badLinksSecret);
@@ -97,20 +99,27 @@ export default class AutomodMessageCreateListener extends BushListener {
: highestOffence === 2
? util.colors.orange
: util.colors.red;
- // TODO: remove hard coded value
- void (message.guild.channels.cache.get('783088333055066212') as TextChannel).send({
- embeds: [
- new MessageEmbed()
- .setTitle(`[Severity ${highestOffence}] Automod Action Performed`)
- .setDescription(
- `**User:** ${message.author} (${message.author.tag})\n**Sent From**: <#${message.channel.id}> [Jump to context](${
- message.url
- })\n**Blacklisted Words:** ${util.surroundArray(Object.keys(offences), '`').join(', ')}`
- )
- .addField('Message Content', `${await util.codeblock(message.content, 1024)}`)
- .setColor(color)
- .setTimestamp()
- ]
- });
+
+ const automodChannel = (await message.guild.getSetting('logChannels')).automod;
+ if (!automodChannel) return;
+ const fetchedChannel = (message.guild.channels.cache.get(automodChannel) ??
+ (await message.guild.channels.fetch(automodChannel).catch(() => null))) as TextChannel;
+ if (!fetchedChannel) return;
+
+ if (fetchedChannel.permissionsFor(message.guild.me!.id)?.has(['VIEW_CHANNEL', 'SEND_MESSAGES', 'EMBED_LINKS']))
+ void fetchedChannel.send({
+ embeds: [
+ new MessageEmbed()
+ .setTitle(`[Severity ${highestOffence}] Automod Action Performed`)
+ .setDescription(
+ `**User:** ${message.author} (${message.author.tag})\n**Sent From**: <#${message.channel.id}> [Jump to context](${
+ message.url
+ })\n**Blacklisted Words:** ${util.surroundArray(Object.keys(offences), '`').join(', ')}`
+ )
+ .addField('Message Content', `${await util.codeblock(message.content, 1024)}`)
+ .setColor(color)
+ .setTimestamp()
+ ]
+ });
}
}