From e5bc336f9586b1f5515be3f1d239d2194489e9c5 Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Fri, 4 Feb 2022 11:05:30 -0500 Subject: refactor models --- src/lib/extensions/discord-akairo/BushClient.ts | 18 +- src/lib/extensions/discord.js/BushGuild.ts | 4 +- src/lib/index.ts | 18 +- src/lib/models/ActivePunishment.ts | 91 ------ src/lib/models/Global.ts | 64 ---- src/lib/models/Guild.ts | 414 ------------------------ src/lib/models/Level.ts | 75 ----- src/lib/models/ModLog.ts | 124 ------- src/lib/models/Reminder.ts | 81 ----- src/lib/models/Shared.ts | 81 ----- src/lib/models/Stat.ts | 51 --- src/lib/models/StickyRole.ts | 55 ---- src/lib/models/instance/ActivePunishment.ts | 91 ++++++ src/lib/models/instance/Guild.ts | 414 ++++++++++++++++++++++++ src/lib/models/instance/Level.ts | 75 +++++ src/lib/models/instance/ModLog.ts | 124 +++++++ src/lib/models/instance/Reminder.ts | 81 +++++ src/lib/models/instance/StickyRole.ts | 55 ++++ src/lib/models/shared/Global.ts | 64 ++++ src/lib/models/shared/Shared.ts | 81 +++++ src/lib/models/shared/Stat.ts | 51 +++ 21 files changed, 1056 insertions(+), 1056 deletions(-) delete mode 100644 src/lib/models/ActivePunishment.ts delete mode 100644 src/lib/models/Global.ts delete mode 100644 src/lib/models/Guild.ts delete mode 100644 src/lib/models/Level.ts delete mode 100644 src/lib/models/ModLog.ts delete mode 100644 src/lib/models/Reminder.ts delete mode 100644 src/lib/models/Shared.ts delete mode 100644 src/lib/models/Stat.ts delete mode 100644 src/lib/models/StickyRole.ts create mode 100644 src/lib/models/instance/ActivePunishment.ts create mode 100644 src/lib/models/instance/Guild.ts create mode 100644 src/lib/models/instance/Level.ts create mode 100644 src/lib/models/instance/ModLog.ts create mode 100644 src/lib/models/instance/Reminder.ts create mode 100644 src/lib/models/instance/StickyRole.ts create mode 100644 src/lib/models/shared/Global.ts create mode 100644 src/lib/models/shared/Shared.ts create mode 100644 src/lib/models/shared/Stat.ts (limited to 'src/lib') diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 7acd3df..0fc8896 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -47,15 +47,15 @@ import type { Options as SequelizeOptions, Sequelize as SequelizeType } from 'se import { fileURLToPath } from 'url'; import UpdateCacheTask from '../../../tasks/updateCache.js'; import UpdateStatsTask from '../../../tasks/updateStats.js'; -import { ActivePunishment } from '../../models/ActivePunishment.js'; -import { Global } from '../../models/Global.js'; -import { Guild as GuildModel } from '../../models/Guild.js'; -import { Level } from '../../models/Level.js'; -import { ModLog } from '../../models/ModLog.js'; -import { Reminder } from '../../models/Reminder.js'; -import { Shared } from '../../models/Shared.js'; -import { Stat } from '../../models/Stat.js'; -import { StickyRole } from '../../models/StickyRole.js'; +import { ActivePunishment } from '../../models/instance/ActivePunishment.js'; +import { Guild as GuildModel } from '../../models/instance/Guild.js'; +import { Level } from '../../models/instance/Level.js'; +import { ModLog } from '../../models/instance/ModLog.js'; +import { Reminder } from '../../models/instance/Reminder.js'; +import { StickyRole } from '../../models/instance/StickyRole.js'; +import { Global } from '../../models/shared/Global.js'; +import { Shared } from '../../models/shared/Shared.js'; +import { Stat } from '../../models/shared/Stat.js'; import { AllowedMentions } from '../../utils/AllowedMentions.js'; import { BushCache } from '../../utils/BushCache.js'; import { BushConstants } from '../../utils/BushConstants.js'; diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index 4aacc73..b13d0f8 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -22,8 +22,8 @@ import { Collection, Guild, PermissionFlagsBits, Snowflake, type MessageOptions, import type { RawGuildData } from 'discord.js/typings/rawDataTypes'; import _ from 'lodash'; import { Moderation } from '../../common/util/Moderation.js'; -import { Guild as GuildDB } from '../../models/Guild.js'; -import { ModLogType } from '../../models/ModLog.js'; +import { Guild as GuildDB } from '../../models/instance/Guild.js'; +import { ModLogType } from '../../models/instance/ModLog.js'; /** * Represents a guild (or a server) on Discord. diff --git a/src/lib/index.ts b/src/lib/index.ts index 79bc553..0c73875 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -66,16 +66,16 @@ export type { BushUserManager } from './extensions/discord.js/BushUserManager.js export * from './extensions/discord.js/BushVoiceChannel.js'; export * from './extensions/discord.js/BushVoiceState.js'; export * from './extensions/discord.js/other.js'; -export * from './models/ActivePunishment.js'; export * from './models/BaseModel.js'; -export * from './models/Global.js'; -export * from './models/Guild.js'; -export * from './models/Level.js'; -export * from './models/ModLog.js'; -export * from './models/Reminder.js'; -export * from './models/Shared.js'; -export * from './models/Stat.js'; -export * from './models/StickyRole.js'; +export * from './models/instance/ActivePunishment.js'; +export * from './models/instance/Guild.js'; +export * from './models/instance/Level.js'; +export * from './models/instance/ModLog.js'; +export * from './models/instance/Reminder.js'; +export * from './models/instance/StickyRole.js'; +export * from './models/shared/Global.js'; +export * from './models/shared/Shared.js'; +export * from './models/shared/Stat.js'; export * from './utils/AllowedMentions.js'; export * from './utils/BushCache.js'; export * from './utils/BushConstants.js'; diff --git a/src/lib/models/ActivePunishment.ts b/src/lib/models/ActivePunishment.ts deleted file mode 100644 index 5fae2ac..0000000 --- a/src/lib/models/ActivePunishment.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { type Snowflake } from 'discord.js'; -import { nanoid } from 'nanoid'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export enum ActivePunishmentType { - BAN = 'BAN', - MUTE = 'MUTE', - ROLE = 'ROLE', - BLOCK = 'BLOCK' -} - -export interface ActivePunishmentModel { - id: string; - type: ActivePunishmentType; - user: Snowflake; - guild: Snowflake; - extraInfo: Snowflake; - expires: Date | null; - modlog: string; -} - -export interface ActivePunishmentModelCreationAttributes { - id?: string; - type: ActivePunishmentType; - user: Snowflake; - guild: Snowflake; - extraInfo?: Snowflake; - expires?: Date; - modlog: string; -} - -export class ActivePunishment - extends BaseModel - implements ActivePunishmentModel -{ - /** - * The ID of this punishment (no real use just for a primary key) - */ - public declare id: string; - - /** - * The type of punishment. - */ - public declare type: ActivePunishmentType; - - /** - * The user who is punished. - */ - public declare user: Snowflake; - - /** - * The guild they are punished in. - */ - public declare guild: Snowflake; - - /** - * Additional info about the punishment if applicable. The channel id for channel blocks and role for punishment roles. - */ - public declare extraInfo: Snowflake; - - /** - * The date when this punishment expires (optional). - */ - public declare expires: Date | null; - - /** - * The reference to the modlog entry. - */ - public declare modlog: string; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - ActivePunishment.init( - { - id: { type: DataTypes.STRING, primaryKey: true, allowNull: false, defaultValue: nanoid }, - type: { type: DataTypes.STRING, allowNull: false }, - user: { type: DataTypes.STRING, allowNull: false }, - guild: { type: DataTypes.STRING, allowNull: false, references: { model: 'Guilds', key: 'id' } }, - extraInfo: { type: DataTypes.STRING, allowNull: true }, - expires: { type: DataTypes.DATE, allowNull: true }, - modlog: { type: DataTypes.STRING, allowNull: true, references: { model: 'ModLogs', key: 'id' } } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/Global.ts b/src/lib/models/Global.ts deleted file mode 100644 index 8e467f6..0000000 --- a/src/lib/models/Global.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { type Snowflake } from 'discord.js'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface GlobalModel { - environment: 'production' | 'development' | 'beta'; - disabledCommands: string[]; - blacklistedUsers: Snowflake[]; - blacklistedGuilds: Snowflake[]; - blacklistedChannels: Snowflake[]; -} - -export interface GlobalModelCreationAttributes { - environment: 'production' | 'development' | 'beta'; - disabledCommands?: string[]; - blacklistedUsers?: Snowflake[]; - blacklistedGuilds?: Snowflake[]; - blacklistedChannels?: Snowflake[]; -} - -export class Global extends BaseModel implements GlobalModel { - /** - * The bot's environment. - */ - public declare environment: 'production' | 'development' | 'beta'; - - /** - * Globally disabled commands. - */ - public declare disabledCommands: string[]; - - /** - * Globally blacklisted users. - */ - public declare blacklistedUsers: Snowflake[]; - - /** - * Guilds blacklisted from using the bot. - */ - public declare blacklistedGuilds: Snowflake[]; - - /** - * Channels where the bot is prevented from running commands in. - */ - public declare blacklistedChannels: Snowflake[]; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - Global.init( - { - environment: { type: DataTypes.STRING, primaryKey: true }, - disabledCommands: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - blacklistedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - blacklistedGuilds: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - blacklistedChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts deleted file mode 100644 index 7e67e21..0000000 --- a/src/lib/models/Guild.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { ChannelType, type Snowflake } from 'discord.js'; -import { type Sequelize } from 'sequelize'; -import { BadWordDetails } from '../common/AutoMod.js'; -import { type BushClient } from '../extensions/discord-akairo/BushClient.js'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface GuildModel { - id: Snowflake; - prefix: string; - autoPublishChannels: Snowflake[]; - blacklistedChannels: Snowflake[]; - blacklistedUsers: Snowflake[]; - welcomeChannel: Snowflake; - muteRole: Snowflake; - punishmentEnding: string; - disabledCommands: string[]; - lockdownChannels: Snowflake[]; - autoModPhases: BadWordDetails[]; - enabledFeatures: GuildFeatures[]; - joinRoles: Snowflake[]; - logChannels: LogChannelDB; - bypassChannelBlacklist: Snowflake[]; - noXpChannels: Snowflake[]; - levelRoles: { [level: number]: Snowflake }; - levelUpChannel: 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?: BadWordDetails[]; - enabledFeatures?: GuildFeatures[]; - joinRoles?: Snowflake[]; - logChannels?: LogChannelDB; - bypassChannelBlacklist?: Snowflake[]; - noXpChannels?: Snowflake[]; - levelRoles?: { [level: number]: Snowflake }; - levelUpChannel?: Snowflake; -} - -export class Guild extends BaseModel implements GuildModel { - /** - * The ID of the guild - */ - public declare id: Snowflake; - - /** - * The bot's prefix for the guild - */ - public declare prefix: string; - - /** - * Channels that will have their messages automatically published - */ - public declare autoPublishChannels: Snowflake[]; - - /** - * Channels where the bot won't respond in. - */ - public declare blacklistedChannels: Snowflake[]; - - /** - * Users that the bot ignores in this guild - */ - public declare blacklistedUsers: Snowflake[]; - - /** - * The channels where the welcome messages are sent - */ - public declare welcomeChannel: Snowflake; - - /** - * The role given out when muting someone - */ - public declare muteRole: Snowflake; - - /** - * The message that gets sent after someone gets a punishment dm - */ - public declare punishmentEnding: string; - - /** - * Guild specific disabled commands - */ - public declare disabledCommands: string[]; - - /** - * Channels that should get locked down when the lockdown command gets used. - */ - public declare lockdownChannels: Snowflake[]; - - /** - * Custom automod phases - */ - public declare autoModPhases: BadWordDetails[]; - - /** - * The features enabled in a guild - */ - public declare enabledFeatures: GuildFeatures[]; - - /** - * The roles to assign to a user if they are not assigned sticky roles - */ - public declare joinRoles: Snowflake[]; - - /** - * The channels where logging messages will be sent. - */ - public declare logChannels: LogChannelDB; - - /** - * These users will be able to use commands in channels blacklisted - */ - public declare bypassChannelBlacklist: Snowflake[]; - - /** - * Channels where users will not earn xp for leveling. - */ - public declare noXpChannels: Snowflake[]; - - /** - * What roles get given to users when they reach certain levels. - */ - public declare levelRoles: { [level: number]: Snowflake }; - - /** - * The channel to send level up messages in instead of last channel. - */ - public declare levelUpChannel: Snowflake; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize, client: BushClient): void { - Guild.init( - { - id: { type: DataTypes.STRING, primaryKey: true }, - prefix: { type: DataTypes.TEXT, allowNull: false, defaultValue: client.config.prefix }, - autoPublishChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - blacklistedChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - blacklistedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - welcomeChannel: { type: DataTypes.STRING, allowNull: true }, - muteRole: { type: DataTypes.STRING, allowNull: true }, - punishmentEnding: { type: DataTypes.TEXT, allowNull: true }, - disabledCommands: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - lockdownChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - autoModPhases: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - enabledFeatures: { - type: DataTypes.JSONB, - allowNull: false, - defaultValue: Object.keys(guildFeaturesObj).filter( - (key) => guildFeaturesObj[key as keyof typeof guildFeaturesObj].default - ) - }, - joinRoles: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - logChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, - bypassChannelBlacklist: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - noXpChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - levelRoles: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, - levelUpChannel: { type: DataTypes.STRING, allowNull: true } - }, - { sequelize } - ); - } -} - -export type BaseGuildSetting = 'channel' | 'role' | 'user'; -export type GuildSettingType = 'string' | 'custom' | BaseGuildSetting | `${BaseGuildSetting}-array`; - -export interface GuildSetting { - name: string; - description: string; - type: GuildSettingType; - subType: ChannelType[] | undefined; - configurable: boolean; -} -const asGuildSetting = (et: { [K in keyof T]: GuildSetting }) => et; - -export const guildSettingsObj = asGuildSetting({ - prefix: { - name: 'Prefix', - description: 'The phrase required to trigger text commands in this server.', - type: 'string', - subType: undefined, - configurable: true - }, - autoPublishChannels: { - name: 'Auto Publish Channels', - description: 'Channels were every message is automatically published.', - type: 'channel-array', - subType: [ChannelType.GuildNews], - configurable: true - }, - welcomeChannel: { - name: 'Welcome Channel', - description: 'The channel where the bot will send join and leave message.', - type: 'channel', - subType: [ - ChannelType.GuildText, - ChannelType.GuildNews, - ChannelType.GuildNewsThread, - ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread - ], - configurable: true - }, - muteRole: { - name: 'Mute Role', - description: 'The role assigned when muting someone.', - type: 'role', - subType: undefined, - configurable: true - }, - punishmentEnding: { - name: 'Punishment Ending', - description: 'The message after punishment information to a user in a dm.', - type: 'string', - subType: undefined, - configurable: true - }, - lockdownChannels: { - name: 'Lockdown Channels', - description: 'Channels that are locked down when a mass lockdown is specified.', - type: 'channel-array', - subType: [ChannelType.GuildText], - configurable: true - }, - joinRoles: { - name: 'Join Roles', - description: 'Roles assigned to users on join who do not have sticky role information.', - type: 'role-array', - subType: undefined, - configurable: true - }, - bypassChannelBlacklist: { - name: 'Bypass Channel Blacklist', - description: 'These users will be able to use commands in channels blacklisted.', - type: 'user-array', - subType: undefined, - configurable: true - }, - logChannels: { - name: 'Log Channels', - description: 'The channel were logs are sent.', - type: 'custom', - subType: [ChannelType.GuildText], - configurable: false - }, - autoModPhases: { - name: 'Automod Phases', - description: 'Custom phrases to be detected by automod.', - type: 'custom', - subType: undefined, - configurable: false - }, - noXpChannels: { - name: 'No Xp Channels', - description: 'Channels where users will not earn xp for leveling.', - type: 'channel-array', - subType: [ - ChannelType.GuildText, - ChannelType.GuildNews, - ChannelType.GuildNewsThread, - ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread - ], - configurable: true - }, - levelRoles: { - name: 'Level Roles', - description: 'What roles get given to users when they reach certain levels.', - type: 'custom', - subType: undefined, - configurable: false - }, - levelUpChannel: { - name: 'Level Up Channel', - description: 'The channel to send level up messages in instead of last channel.', - type: 'channel', - subType: [ - ChannelType.GuildText, - ChannelType.GuildNews, - ChannelType.GuildNewsThread, - ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread - ], - configurable: true - } -}); - -export type GuildSettings = keyof typeof guildSettingsObj; -export const settingsArr = Object.keys(guildSettingsObj).filter( - (s) => guildSettingsObj[s as GuildSettings].configurable -) as GuildSettings[]; - -interface GuildFeature { - name: string; - description: string; - default: boolean; -} -const asGuildFeature = (gf: { [K in keyof T]: GuildFeature }) => gf; - -export const guildFeaturesObj = asGuildFeature({ - automod: { - name: 'Automod', - description: 'Deletes offensive content as well as phishing links.', - default: false - }, - excludeDefaultAutomod: { - name: 'Exclude Default Automod', - description: 'Opt out of using the default automod options.', - default: false - }, - excludeAutomodScamLinks: { - name: 'Exclude Automod Scam Links', - description: 'Opt out of having automod delete scam links.', - default: false - }, - delScamMentions: { - name: 'Delete Scam Mentions', - description: 'Deletes messages with @everyone and @here mentions that have common scam phrases.', - default: false - }, - autoPublish: { - name: 'Auto Publish', - description: 'Publishes messages in configured announcement channels.', - default: false - }, - // todo implement a better auto thread system - // autoThread: { - // name: 'Auto Thread', - // description: 'Creates a new thread for messages in configured channels.', - // default: false - // }, - blacklistedFile: { - name: 'Blacklisted File', - description: 'Automatically deletes malicious files.', - default: false - }, - boosterMessageReact: { - name: 'Booster Message React', - description: 'Reacts to booster messages with the boost emoji.', - default: false - }, - leveling: { - name: 'Leveling', - description: "Tracks users' messages and assigns them xp.", - default: false - }, - stickyRoles: { - name: 'Sticky Roles', - description: 'Restores past roles to a user when they rejoin.', - default: false - }, - reporting: { - name: 'Reporting', - description: 'Allow users to make reports.', - default: false - }, - modsCanPunishMods: { - name: 'Mods Can Punish Mods', - description: 'Allow moderators to punish other moderators.', - default: false - }, - sendLevelUpMessages: { - name: 'Send Level Up Messages', - description: 'Send a message when a user levels up.', - default: true - }, - logManualPunishments: { - name: 'Log Manual Punishments', - description: "Adds manual punishment to the user's modlogs and the logging channels.", - default: true - } -}); - -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 - }, - report: { - description: 'Logs user reports.', - configurable: true - }, - error: { - description: 'Logs errors that occur with the bot.', - 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[]; diff --git a/src/lib/models/Level.ts b/src/lib/models/Level.ts deleted file mode 100644 index e247779..0000000 --- a/src/lib/models/Level.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { type Snowflake } from 'discord.js'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface LevelModel { - user: Snowflake; - guild: Snowflake; - xp: number; -} - -export interface LevelModelCreationAttributes { - user: Snowflake; - guild: Snowflake; - xp?: number; -} - -export class Level extends BaseModel implements LevelModel { - /** - * The user's id. - */ - public declare user: Snowflake; - - /** - * The guild where the user is gaining xp. - */ - public declare guild: Snowflake; - - /** - * The user's xp. - */ - public declare xp: number; - - /** - * The user's level. - */ - public get level(): number { - return Level.convertXpToLevel(this.xp); - } - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - Level.init( - { - user: { type: DataTypes.STRING, allowNull: false }, - guild: { type: DataTypes.STRING, allowNull: false }, - xp: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 } - }, - { sequelize } - ); - } - - public static convertXpToLevel(xp: number): number { - let i = 0; - while (Level.convertLevelToXp(i + 1) < xp) { - i++; - } - return i; - } - - public static convertLevelToXp(level: number): number { - let xp = 0; - for (let i = 0; i < level; i++) { - xp += 100 * i + 75; - } - return xp; - } - - public static genRandomizedXp(): number { - return Math.floor(Math.random() * (40 - 15 + 1)) + 15; - } -} diff --git a/src/lib/models/ModLog.ts b/src/lib/models/ModLog.ts deleted file mode 100644 index 8d6a08c..0000000 --- a/src/lib/models/ModLog.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { type Snowflake } from 'discord.js'; -import { nanoid } from 'nanoid'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export enum ModLogType { - PERM_BAN = 'PERM_BAN', - TEMP_BAN = 'TEMP_BAN', - UNBAN = 'UNBAN', - KICK = 'KICK', - PERM_MUTE = 'PERM_MUTE', - TEMP_MUTE = 'TEMP_MUTE', - UNMUTE = 'UNMUTE', - WARN = 'WARN', - PERM_PUNISHMENT_ROLE = 'PERM_PUNISHMENT_ROLE', - TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE', - REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE', - PERM_CHANNEL_BLOCK = 'PERM_CHANNEL_BLOCK', - TEMP_CHANNEL_BLOCK = 'TEMP_CHANNEL_BLOCK', - CHANNEL_UNBLOCK = 'CHANNEL_UNBLOCK', - TIMEOUT = 'TIMEOUT', - REMOVE_TIMEOUT = 'REMOVE_TIMEOUT' -} - -export interface ModLogModel { - id: string; - type: ModLogType; - user: Snowflake; - moderator: Snowflake; - reason: string | null; - duration: number | null; - guild: Snowflake; - evidence: string; - pseudo: boolean; - hidden: boolean; -} - -export interface ModLogModelCreationAttributes { - id?: string; - type: ModLogType; - user: Snowflake; - moderator: Snowflake; - reason?: string | null; - duration?: number; - guild: Snowflake; - evidence?: string; - pseudo?: boolean; - hidden?: boolean; -} - -export class ModLog extends BaseModel implements ModLogModel { - /** - * The primary key of the modlog entry. - */ - public declare id: string; - - /** - * The type of punishment. - */ - public declare type: ModLogType; - - /** - * The user being punished. - */ - public declare user: Snowflake; - - /** - * The user carrying out the punishment. - */ - public declare moderator: Snowflake; - - /** - * The reason the user is getting punished. - */ - public declare reason: string | null; - - /** - * The amount of time the user is getting punished for. - */ - public declare duration: number | null; - - /** - * The guild the user is getting punished in. - */ - public declare guild: Snowflake; - - /** - * Evidence of what the user is getting punished for. - */ - public declare evidence: string; - - /** - * Not an actual modlog just used so a punishment entry can be made. - */ - public declare pseudo: boolean; - - /** - * Hides from the modlog command unless show hidden is specified. - */ - public declare hidden: boolean; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - ModLog.init( - { - id: { type: DataTypes.STRING, primaryKey: true, allowNull: false, defaultValue: nanoid }, - type: { type: DataTypes.STRING, allowNull: false }, //? This is not an enum because of a sequelize issue: https://github.com/sequelize/sequelize/issues/2554 - user: { type: DataTypes.STRING, allowNull: false }, - moderator: { type: DataTypes.STRING, allowNull: false }, - duration: { type: DataTypes.STRING, allowNull: true }, - reason: { type: DataTypes.TEXT, allowNull: true }, - guild: { type: DataTypes.STRING, references: { model: 'Guilds', key: 'id' } }, - evidence: { type: DataTypes.TEXT, allowNull: true }, - pseudo: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, - hidden: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/Reminder.ts b/src/lib/models/Reminder.ts deleted file mode 100644 index d1460bf..0000000 --- a/src/lib/models/Reminder.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Snowflake } from 'discord.js'; -import { nanoid } from 'nanoid'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface ReminderModel { - id: string; - user: Snowflake; - messageUrl: string; - content: string; - created: Date; - expires: Date; - notified: boolean; -} - -export interface ReminderModelCreationAttributes { - id?: string; - user: Snowflake; - messageUrl: string; - content: string; - created: Date; - expires: Date; - notified?: boolean; -} - -export class Reminder extends BaseModel implements ReminderModel { - /** - * The id of the reminder. - */ - public declare id: string; - - /** - * The user that the reminder is for. - */ - public declare user: Snowflake; - - /** - * The url of the message where the reminder was created. - */ - public declare messageUrl: string; - - /** - * The content of the reminder. - */ - public declare content: string; - - /** - * The date the reminder was created. - */ - public declare created: Date; - - /** - * The date when the reminder expires. - */ - public declare expires: Date; - - /** - * Whether the user has been notified about the reminder. - */ - public declare notified: boolean; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - Reminder.init( - { - id: { type: DataTypes.STRING, primaryKey: true, defaultValue: nanoid }, - user: { type: DataTypes.STRING, allowNull: false }, - messageUrl: { type: DataTypes.STRING, allowNull: false }, - content: { type: DataTypes.TEXT, allowNull: false }, - created: { type: DataTypes.DATE, allowNull: false }, - expires: { type: DataTypes.DATE, allowNull: false }, - notified: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/Shared.ts b/src/lib/models/Shared.ts deleted file mode 100644 index acb5c1e..0000000 --- a/src/lib/models/Shared.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Snowflake } from 'discord.js'; -import type { Sequelize } from 'sequelize'; -import type { BadWords } from '../common/AutoMod.js'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface SharedModel { - primaryKey: 0; - superUsers: Snowflake[]; - privilegedUsers: Snowflake[]; - badLinksSecret: string[]; - badLinks: string[]; - badWords: BadWords; - autoBanCode: string | null; -} - -export interface SharedModelCreationAttributes { - primaryKey?: 0; - superUsers?: Snowflake[]; - privilegedUsers?: Snowflake[]; - badLinksSecret?: string[]; - badLinks?: string[]; - badWords?: BadWords; - autoBanCode?: string; -} - -export class Shared extends BaseModel implements SharedModel { - /** - * The primary key of the shared model. - */ - public declare primaryKey: 0; - - /** - * Trusted users. - */ - public declare superUsers: Snowflake[]; - - /** - * Users that have all permissions that devs have except eval. - */ - public declare privilegedUsers: Snowflake[]; - - /** - * Non-public bad links. - */ - public declare badLinksSecret: string[]; - - /** - * Public Bad links. - */ - public declare badLinks: string[]; - - /** - * Bad words. - */ - public declare badWords: BadWords; - - /** - * Code that is used to match for auto banning users in moulberry's bush - */ - public declare autoBanCode: string; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - Shared.init( - { - primaryKey: { type: DataTypes.INTEGER, primaryKey: true, validate: { min: 0, max: 0 } }, - superUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - privilegedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - badLinksSecret: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - badLinks: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - badWords: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, - autoBanCode: { type: DataTypes.TEXT } - }, - { sequelize, freezeTableName: true } - ); - } -} diff --git a/src/lib/models/Stat.ts b/src/lib/models/Stat.ts deleted file mode 100644 index 8f90724..0000000 --- a/src/lib/models/Stat.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -type Environment = 'production' | 'development' | 'beta'; - -export interface StatModel { - environment: Environment; - commandsUsed: bigint; -} - -export interface StatModelCreationAttributes { - environment: Environment; - commandsUsed?: bigint; -} - -export class Stat extends BaseModel implements StatModel { - /** - * The bot's environment. - */ - public declare environment: Environment; - - /** - * The number of commands used - */ - public declare commandsUsed: bigint; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - Stat.init( - { - environment: { type: DataTypes.STRING, primaryKey: true }, - commandsUsed: { - type: DataTypes.TEXT, - get: function (): bigint { - return BigInt(this.getDataValue('commandsUsed')); - }, - set: function (val: bigint) { - return this.setDataValue('commandsUsed', `${val}`); - }, - allowNull: false, - defaultValue: `${0n}` - } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/StickyRole.ts b/src/lib/models/StickyRole.ts deleted file mode 100644 index 4beb4cf..0000000 --- a/src/lib/models/StickyRole.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { type Snowflake } from 'discord.js'; -import { type Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel.js'; -const { DataTypes } = (await import('sequelize')).default; - -export interface StickyRoleModel { - user: Snowflake; - guild: Snowflake; - roles: Snowflake[]; - nickname: string; -} -export interface StickyRoleModelCreationAttributes { - user: Snowflake; - guild: Snowflake; - roles: Snowflake[]; - nickname?: string; -} - -export class StickyRole extends BaseModel implements StickyRoleModel { - /** - * The id of the user the roles belongs to. - */ - public declare user: Snowflake; - - /** - * The guild where this should happen. - */ - public declare guild: Snowflake; - - /** - * The roles that the user should have returned - */ - public declare roles: Snowflake[]; - - /** - * The user's previous nickname - */ - public declare nickname: string; - - /** - * Initializes the model. - * @param sequelize The sequelize instance. - */ - public static initModel(sequelize: Sequelize): void { - StickyRole.init( - { - user: { type: DataTypes.STRING, allowNull: false }, - guild: { type: DataTypes.STRING, allowNull: false }, - roles: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, - nickname: { type: DataTypes.STRING, allowNull: true } - }, - { sequelize } - ); - } -} diff --git a/src/lib/models/instance/ActivePunishment.ts b/src/lib/models/instance/ActivePunishment.ts new file mode 100644 index 0000000..1d6104f --- /dev/null +++ b/src/lib/models/instance/ActivePunishment.ts @@ -0,0 +1,91 @@ +import { type Snowflake } from 'discord.js'; +import { nanoid } from 'nanoid'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export enum ActivePunishmentType { + BAN = 'BAN', + MUTE = 'MUTE', + ROLE = 'ROLE', + BLOCK = 'BLOCK' +} + +export interface ActivePunishmentModel { + id: string; + type: ActivePunishmentType; + user: Snowflake; + guild: Snowflake; + extraInfo: Snowflake; + expires: Date | null; + modlog: string; +} + +export interface ActivePunishmentModelCreationAttributes { + id?: string; + type: ActivePunishmentType; + user: Snowflake; + guild: Snowflake; + extraInfo?: Snowflake; + expires?: Date; + modlog: string; +} + +export class ActivePunishment + extends BaseModel + implements ActivePunishmentModel +{ + /** + * The ID of this punishment (no real use just for a primary key) + */ + public declare id: string; + + /** + * The type of punishment. + */ + public declare type: ActivePunishmentType; + + /** + * The user who is punished. + */ + public declare user: Snowflake; + + /** + * The guild they are punished in. + */ + public declare guild: Snowflake; + + /** + * Additional info about the punishment if applicable. The channel id for channel blocks and role for punishment roles. + */ + public declare extraInfo: Snowflake; + + /** + * The date when this punishment expires (optional). + */ + public declare expires: Date | null; + + /** + * The reference to the modlog entry. + */ + public declare modlog: string; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + ActivePunishment.init( + { + id: { type: DataTypes.STRING, primaryKey: true, allowNull: false, defaultValue: nanoid }, + type: { type: DataTypes.STRING, allowNull: false }, + user: { type: DataTypes.STRING, allowNull: false }, + guild: { type: DataTypes.STRING, allowNull: false, references: { model: 'Guilds', key: 'id' } }, + extraInfo: { type: DataTypes.STRING, allowNull: true }, + expires: { type: DataTypes.DATE, allowNull: true }, + modlog: { type: DataTypes.STRING, allowNull: true, references: { model: 'ModLogs', key: 'id' } } + }, + { sequelize } + ); + } +} diff --git a/src/lib/models/instance/Guild.ts b/src/lib/models/instance/Guild.ts new file mode 100644 index 0000000..11469d2 --- /dev/null +++ b/src/lib/models/instance/Guild.ts @@ -0,0 +1,414 @@ +import { ChannelType, type Snowflake } from 'discord.js'; +import { type Sequelize } from 'sequelize'; +import { BadWordDetails } from '../../common/AutoMod.js'; +import { type BushClient } from '../../extensions/discord-akairo/BushClient.js'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface GuildModel { + id: Snowflake; + prefix: string; + autoPublishChannels: Snowflake[]; + blacklistedChannels: Snowflake[]; + blacklistedUsers: Snowflake[]; + welcomeChannel: Snowflake; + muteRole: Snowflake; + punishmentEnding: string; + disabledCommands: string[]; + lockdownChannels: Snowflake[]; + autoModPhases: BadWordDetails[]; + enabledFeatures: GuildFeatures[]; + joinRoles: Snowflake[]; + logChannels: LogChannelDB; + bypassChannelBlacklist: Snowflake[]; + noXpChannels: Snowflake[]; + levelRoles: { [level: number]: Snowflake }; + levelUpChannel: 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?: BadWordDetails[]; + enabledFeatures?: GuildFeatures[]; + joinRoles?: Snowflake[]; + logChannels?: LogChannelDB; + bypassChannelBlacklist?: Snowflake[]; + noXpChannels?: Snowflake[]; + levelRoles?: { [level: number]: Snowflake }; + levelUpChannel?: Snowflake; +} + +export class Guild extends BaseModel implements GuildModel { + /** + * The ID of the guild + */ + public declare id: Snowflake; + + /** + * The bot's prefix for the guild + */ + public declare prefix: string; + + /** + * Channels that will have their messages automatically published + */ + public declare autoPublishChannels: Snowflake[]; + + /** + * Channels where the bot won't respond in. + */ + public declare blacklistedChannels: Snowflake[]; + + /** + * Users that the bot ignores in this guild + */ + public declare blacklistedUsers: Snowflake[]; + + /** + * The channels where the welcome messages are sent + */ + public declare welcomeChannel: Snowflake; + + /** + * The role given out when muting someone + */ + public declare muteRole: Snowflake; + + /** + * The message that gets sent after someone gets a punishment dm + */ + public declare punishmentEnding: string; + + /** + * Guild specific disabled commands + */ + public declare disabledCommands: string[]; + + /** + * Channels that should get locked down when the lockdown command gets used. + */ + public declare lockdownChannels: Snowflake[]; + + /** + * Custom automod phases + */ + public declare autoModPhases: BadWordDetails[]; + + /** + * The features enabled in a guild + */ + public declare enabledFeatures: GuildFeatures[]; + + /** + * The roles to assign to a user if they are not assigned sticky roles + */ + public declare joinRoles: Snowflake[]; + + /** + * The channels where logging messages will be sent. + */ + public declare logChannels: LogChannelDB; + + /** + * These users will be able to use commands in channels blacklisted + */ + public declare bypassChannelBlacklist: Snowflake[]; + + /** + * Channels where users will not earn xp for leveling. + */ + public declare noXpChannels: Snowflake[]; + + /** + * What roles get given to users when they reach certain levels. + */ + public declare levelRoles: { [level: number]: Snowflake }; + + /** + * The channel to send level up messages in instead of last channel. + */ + public declare levelUpChannel: Snowflake; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize, client: BushClient): void { + Guild.init( + { + id: { type: DataTypes.STRING, primaryKey: true }, + prefix: { type: DataTypes.TEXT, allowNull: false, defaultValue: client.config.prefix }, + autoPublishChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + blacklistedChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + blacklistedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + welcomeChannel: { type: DataTypes.STRING, allowNull: true }, + muteRole: { type: DataTypes.STRING, allowNull: true }, + punishmentEnding: { type: DataTypes.TEXT, allowNull: true }, + disabledCommands: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + lockdownChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + autoModPhases: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + enabledFeatures: { + type: DataTypes.JSONB, + allowNull: false, + defaultValue: Object.keys(guildFeaturesObj).filter( + (key) => guildFeaturesObj[key as keyof typeof guildFeaturesObj].default + ) + }, + joinRoles: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + logChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, + bypassChannelBlacklist: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + noXpChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + levelRoles: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, + levelUpChannel: { type: DataTypes.STRING, allowNull: true } + }, + { sequelize } + ); + } +} + +export type BaseGuildSetting = 'channel' | 'role' | 'user'; +export type GuildSettingType = 'string' | 'custom' | BaseGuildSetting | `${BaseGuildSetting}-array`; + +export interface GuildSetting { + name: string; + description: string; + type: GuildSettingType; + subType: ChannelType[] | undefined; + configurable: boolean; +} +const asGuildSetting = (et: { [K in keyof T]: GuildSetting }) => et; + +export const guildSettingsObj = asGuildSetting({ + prefix: { + name: 'Prefix', + description: 'The phrase required to trigger text commands in this server.', + type: 'string', + subType: undefined, + configurable: true + }, + autoPublishChannels: { + name: 'Auto Publish Channels', + description: 'Channels were every message is automatically published.', + type: 'channel-array', + subType: [ChannelType.GuildNews], + configurable: true + }, + welcomeChannel: { + name: 'Welcome Channel', + description: 'The channel where the bot will send join and leave message.', + type: 'channel', + subType: [ + ChannelType.GuildText, + ChannelType.GuildNews, + ChannelType.GuildNewsThread, + ChannelType.GuildPublicThread, + ChannelType.GuildPrivateThread + ], + configurable: true + }, + muteRole: { + name: 'Mute Role', + description: 'The role assigned when muting someone.', + type: 'role', + subType: undefined, + configurable: true + }, + punishmentEnding: { + name: 'Punishment Ending', + description: 'The message after punishment information to a user in a dm.', + type: 'string', + subType: undefined, + configurable: true + }, + lockdownChannels: { + name: 'Lockdown Channels', + description: 'Channels that are locked down when a mass lockdown is specified.', + type: 'channel-array', + subType: [ChannelType.GuildText], + configurable: true + }, + joinRoles: { + name: 'Join Roles', + description: 'Roles assigned to users on join who do not have sticky role information.', + type: 'role-array', + subType: undefined, + configurable: true + }, + bypassChannelBlacklist: { + name: 'Bypass Channel Blacklist', + description: 'These users will be able to use commands in channels blacklisted.', + type: 'user-array', + subType: undefined, + configurable: true + }, + logChannels: { + name: 'Log Channels', + description: 'The channel were logs are sent.', + type: 'custom', + subType: [ChannelType.GuildText], + configurable: false + }, + autoModPhases: { + name: 'Automod Phases', + description: 'Custom phrases to be detected by automod.', + type: 'custom', + subType: undefined, + configurable: false + }, + noXpChannels: { + name: 'No Xp Channels', + description: 'Channels where users will not earn xp for leveling.', + type: 'channel-array', + subType: [ + ChannelType.GuildText, + ChannelType.GuildNews, + ChannelType.GuildNewsThread, + ChannelType.GuildPublicThread, + ChannelType.GuildPrivateThread + ], + configurable: true + }, + levelRoles: { + name: 'Level Roles', + description: 'What roles get given to users when they reach certain levels.', + type: 'custom', + subType: undefined, + configurable: false + }, + levelUpChannel: { + name: 'Level Up Channel', + description: 'The channel to send level up messages in instead of last channel.', + type: 'channel', + subType: [ + ChannelType.GuildText, + ChannelType.GuildNews, + ChannelType.GuildNewsThread, + ChannelType.GuildPublicThread, + ChannelType.GuildPrivateThread + ], + configurable: true + } +}); + +export type GuildSettings = keyof typeof guildSettingsObj; +export const settingsArr = Object.keys(guildSettingsObj).filter( + (s) => guildSettingsObj[s as GuildSettings].configurable +) as GuildSettings[]; + +interface GuildFeature { + name: string; + description: string; + default: boolean; +} +const asGuildFeature = (gf: { [K in keyof T]: GuildFeature }) => gf; + +export const guildFeaturesObj = asGuildFeature({ + automod: { + name: 'Automod', + description: 'Deletes offensive content as well as phishing links.', + default: false + }, + excludeDefaultAutomod: { + name: 'Exclude Default Automod', + description: 'Opt out of using the default automod options.', + default: false + }, + excludeAutomodScamLinks: { + name: 'Exclude Automod Scam Links', + description: 'Opt out of having automod delete scam links.', + default: false + }, + delScamMentions: { + name: 'Delete Scam Mentions', + description: 'Deletes messages with @everyone and @here mentions that have common scam phrases.', + default: false + }, + autoPublish: { + name: 'Auto Publish', + description: 'Publishes messages in configured announcement channels.', + default: false + }, + // todo implement a better auto thread system + // autoThread: { + // name: 'Auto Thread', + // description: 'Creates a new thread for messages in configured channels.', + // default: false + // }, + blacklistedFile: { + name: 'Blacklisted File', + description: 'Automatically deletes malicious files.', + default: false + }, + boosterMessageReact: { + name: 'Booster Message React', + description: 'Reacts to booster messages with the boost emoji.', + default: false + }, + leveling: { + name: 'Leveling', + description: "Tracks users' messages and assigns them xp.", + default: false + }, + stickyRoles: { + name: 'Sticky Roles', + description: 'Restores past roles to a user when they rejoin.', + default: false + }, + reporting: { + name: 'Reporting', + description: 'Allow users to make reports.', + default: false + }, + modsCanPunishMods: { + name: 'Mods Can Punish Mods', + description: 'Allow moderators to punish other moderators.', + default: false + }, + sendLevelUpMessages: { + name: 'Send Level Up Messages', + description: 'Send a message when a user levels up.', + default: true + }, + logManualPunishments: { + name: 'Log Manual Punishments', + description: "Adds manual punishment to the user's modlogs and the logging channels.", + default: true + } +}); + +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 + }, + report: { + description: 'Logs user reports.', + configurable: true + }, + error: { + description: 'Logs errors that occur with the bot.', + 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[]; diff --git a/src/lib/models/instance/Level.ts b/src/lib/models/instance/Level.ts new file mode 100644 index 0000000..e18753d --- /dev/null +++ b/src/lib/models/instance/Level.ts @@ -0,0 +1,75 @@ +import { type Snowflake } from 'discord.js'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface LevelModel { + user: Snowflake; + guild: Snowflake; + xp: number; +} + +export interface LevelModelCreationAttributes { + user: Snowflake; + guild: Snowflake; + xp?: number; +} + +export class Level extends BaseModel implements LevelModel { + /** + * The user's id. + */ + public declare user: Snowflake; + + /** + * The guild where the user is gaining xp. + */ + public declare guild: Snowflake; + + /** + * The user's xp. + */ + public declare xp: number; + + /** + * The user's level. + */ + public get level(): number { + return Level.convertXpToLevel(this.xp); + } + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + Level.init( + { + user: { type: DataTypes.STRING, allowNull: false }, + guild: { type: DataTypes.STRING, allowNull: false }, + xp: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 } + }, + { sequelize } + ); + } + + public static convertXpToLevel(xp: number): number { + let i = 0; + while (Level.convertLevelToXp(i + 1) < xp) { + i++; + } + return i; + } + + public static convertLevelToXp(level: number): number { + let xp = 0; + for (let i = 0; i < level; i++) { + xp += 100 * i + 75; + } + return xp; + } + + public static genRandomizedXp(): number { + return Math.floor(Math.random() * (40 - 15 + 1)) + 15; + } +} diff --git a/src/lib/models/instance/ModLog.ts b/src/lib/models/instance/ModLog.ts new file mode 100644 index 0000000..bcccbb4 --- /dev/null +++ b/src/lib/models/instance/ModLog.ts @@ -0,0 +1,124 @@ +import { type Snowflake } from 'discord.js'; +import { nanoid } from 'nanoid'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export enum ModLogType { + PERM_BAN = 'PERM_BAN', + TEMP_BAN = 'TEMP_BAN', + UNBAN = 'UNBAN', + KICK = 'KICK', + PERM_MUTE = 'PERM_MUTE', + TEMP_MUTE = 'TEMP_MUTE', + UNMUTE = 'UNMUTE', + WARN = 'WARN', + PERM_PUNISHMENT_ROLE = 'PERM_PUNISHMENT_ROLE', + TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE', + REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE', + PERM_CHANNEL_BLOCK = 'PERM_CHANNEL_BLOCK', + TEMP_CHANNEL_BLOCK = 'TEMP_CHANNEL_BLOCK', + CHANNEL_UNBLOCK = 'CHANNEL_UNBLOCK', + TIMEOUT = 'TIMEOUT', + REMOVE_TIMEOUT = 'REMOVE_TIMEOUT' +} + +export interface ModLogModel { + id: string; + type: ModLogType; + user: Snowflake; + moderator: Snowflake; + reason: string | null; + duration: number | null; + guild: Snowflake; + evidence: string; + pseudo: boolean; + hidden: boolean; +} + +export interface ModLogModelCreationAttributes { + id?: string; + type: ModLogType; + user: Snowflake; + moderator: Snowflake; + reason?: string | null; + duration?: number; + guild: Snowflake; + evidence?: string; + pseudo?: boolean; + hidden?: boolean; +} + +export class ModLog extends BaseModel implements ModLogModel { + /** + * The primary key of the modlog entry. + */ + public declare id: string; + + /** + * The type of punishment. + */ + public declare type: ModLogType; + + /** + * The user being punished. + */ + public declare user: Snowflake; + + /** + * The user carrying out the punishment. + */ + public declare moderator: Snowflake; + + /** + * The reason the user is getting punished. + */ + public declare reason: string | null; + + /** + * The amount of time the user is getting punished for. + */ + public declare duration: number | null; + + /** + * The guild the user is getting punished in. + */ + public declare guild: Snowflake; + + /** + * Evidence of what the user is getting punished for. + */ + public declare evidence: string; + + /** + * Not an actual modlog just used so a punishment entry can be made. + */ + public declare pseudo: boolean; + + /** + * Hides from the modlog command unless show hidden is specified. + */ + public declare hidden: boolean; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + ModLog.init( + { + id: { type: DataTypes.STRING, primaryKey: true, allowNull: false, defaultValue: nanoid }, + type: { type: DataTypes.STRING, allowNull: false }, //? This is not an enum because of a sequelize issue: https://github.com/sequelize/sequelize/issues/2554 + user: { type: DataTypes.STRING, allowNull: false }, + moderator: { type: DataTypes.STRING, allowNull: false }, + duration: { type: DataTypes.STRING, allowNull: true }, + reason: { type: DataTypes.TEXT, allowNull: true }, + guild: { type: DataTypes.STRING, references: { model: 'Guilds', key: 'id' } }, + evidence: { type: DataTypes.TEXT, allowNull: true }, + pseudo: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, + hidden: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false } + }, + { sequelize } + ); + } +} diff --git a/src/lib/models/instance/Reminder.ts b/src/lib/models/instance/Reminder.ts new file mode 100644 index 0000000..9ca78f1 --- /dev/null +++ b/src/lib/models/instance/Reminder.ts @@ -0,0 +1,81 @@ +import { Snowflake } from 'discord.js'; +import { nanoid } from 'nanoid'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface ReminderModel { + id: string; + user: Snowflake; + messageUrl: string; + content: string; + created: Date; + expires: Date; + notified: boolean; +} + +export interface ReminderModelCreationAttributes { + id?: string; + user: Snowflake; + messageUrl: string; + content: string; + created: Date; + expires: Date; + notified?: boolean; +} + +export class Reminder extends BaseModel implements ReminderModel { + /** + * The id of the reminder. + */ + public declare id: string; + + /** + * The user that the reminder is for. + */ + public declare user: Snowflake; + + /** + * The url of the message where the reminder was created. + */ + public declare messageUrl: string; + + /** + * The content of the reminder. + */ + public declare content: string; + + /** + * The date the reminder was created. + */ + public declare created: Date; + + /** + * The date when the reminder expires. + */ + public declare expires: Date; + + /** + * Whether the user has been notified about the reminder. + */ + public declare notified: boolean; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + Reminder.init( + { + id: { type: DataTypes.STRING, primaryKey: true, defaultValue: nanoid }, + user: { type: DataTypes.STRING, allowNull: false }, + messageUrl: { type: DataTypes.STRING, allowNull: false }, + content: { type: DataTypes.TEXT, allowNull: false }, + created: { type: DataTypes.DATE, allowNull: false }, + expires: { type: DataTypes.DATE, allowNull: false }, + notified: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false } + }, + { sequelize } + ); + } +} diff --git a/src/lib/models/instance/StickyRole.ts b/src/lib/models/instance/StickyRole.ts new file mode 100644 index 0000000..fb1c30d --- /dev/null +++ b/src/lib/models/instance/StickyRole.ts @@ -0,0 +1,55 @@ +import { type Snowflake } from 'discord.js'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface StickyRoleModel { + user: Snowflake; + guild: Snowflake; + roles: Snowflake[]; + nickname: string; +} +export interface StickyRoleModelCreationAttributes { + user: Snowflake; + guild: Snowflake; + roles: Snowflake[]; + nickname?: string; +} + +export class StickyRole extends BaseModel implements StickyRoleModel { + /** + * The id of the user the roles belongs to. + */ + public declare user: Snowflake; + + /** + * The guild where this should happen. + */ + public declare guild: Snowflake; + + /** + * The roles that the user should have returned + */ + public declare roles: Snowflake[]; + + /** + * The user's previous nickname + */ + public declare nickname: string; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + StickyRole.init( + { + user: { type: DataTypes.STRING, allowNull: false }, + guild: { type: DataTypes.STRING, allowNull: false }, + roles: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + nickname: { type: DataTypes.STRING, allowNull: true } + }, + { sequelize } + ); + } +} diff --git a/src/lib/models/shared/Global.ts b/src/lib/models/shared/Global.ts new file mode 100644 index 0000000..6dfc36f --- /dev/null +++ b/src/lib/models/shared/Global.ts @@ -0,0 +1,64 @@ +import { type Snowflake } from 'discord.js'; +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface GlobalModel { + environment: 'production' | 'development' | 'beta'; + disabledCommands: string[]; + blacklistedUsers: Snowflake[]; + blacklistedGuilds: Snowflake[]; + blacklistedChannels: Snowflake[]; +} + +export interface GlobalModelCreationAttributes { + environment: 'production' | 'development' | 'beta'; + disabledCommands?: string[]; + blacklistedUsers?: Snowflake[]; + blacklistedGuilds?: Snowflake[]; + blacklistedChannels?: Snowflake[]; +} + +export class Global extends BaseModel implements GlobalModel { + /** + * The bot's environment. + */ + public declare environment: 'production' | 'development' | 'beta'; + + /** + * Globally disabled commands. + */ + public declare disabledCommands: string[]; + + /** + * Globally blacklisted users. + */ + public declare blacklistedUsers: Snowflake[]; + + /** + * Guilds blacklisted from using the bot. + */ + public declare blacklistedGuilds: Snowflake[]; + + /** + * Channels where the bot is prevented from running commands in. + */ + public declare blacklistedChannels: Snowflake[]; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + Global.init( + { + environment: { type: DataTypes.STRING, primaryKey: true }, + disabledCommands: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + blacklistedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + blacklistedGuilds: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + blacklistedChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] } + }, + { sequelize } + ); + } +} diff --git a/src/lib/models/shared/Shared.ts b/src/lib/models/shared/Shared.ts new file mode 100644 index 0000000..642ff85 --- /dev/null +++ b/src/lib/models/shared/Shared.ts @@ -0,0 +1,81 @@ +import { Snowflake } from 'discord.js'; +import type { Sequelize } from 'sequelize'; +import type { BadWords } from '../../common/AutoMod.js'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +export interface SharedModel { + primaryKey: 0; + superUsers: Snowflake[]; + privilegedUsers: Snowflake[]; + badLinksSecret: string[]; + badLinks: string[]; + badWords: BadWords; + autoBanCode: string | null; +} + +export interface SharedModelCreationAttributes { + primaryKey?: 0; + superUsers?: Snowflake[]; + privilegedUsers?: Snowflake[]; + badLinksSecret?: string[]; + badLinks?: string[]; + badWords?: BadWords; + autoBanCode?: string; +} + +export class Shared extends BaseModel implements SharedModel { + /** + * The primary key of the shared model. + */ + public declare primaryKey: 0; + + /** + * Trusted users. + */ + public declare superUsers: Snowflake[]; + + /** + * Users that have all permissions that devs have except eval. + */ + public declare privilegedUsers: Snowflake[]; + + /** + * Non-public bad links. + */ + public declare badLinksSecret: string[]; + + /** + * Public Bad links. + */ + public declare badLinks: string[]; + + /** + * Bad words. + */ + public declare badWords: BadWords; + + /** + * Code that is used to match for auto banning users in moulberry's bush + */ + public declare autoBanCode: string; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + Shared.init( + { + primaryKey: { type: DataTypes.INTEGER, primaryKey: true, validate: { min: 0, max: 0 } }, + superUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + privilegedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + badLinksSecret: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + badLinks: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }, + badWords: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} }, + autoBanCode: { type: DataTypes.TEXT } + }, + { sequelize, freezeTableName: true } + ); + } +} diff --git a/src/lib/models/shared/Stat.ts b/src/lib/models/shared/Stat.ts new file mode 100644 index 0000000..8f77b58 --- /dev/null +++ b/src/lib/models/shared/Stat.ts @@ -0,0 +1,51 @@ +import { type Sequelize } from 'sequelize'; +import { BaseModel } from '../BaseModel.js'; +const { DataTypes } = (await import('sequelize')).default; + +type Environment = 'production' | 'development' | 'beta'; + +export interface StatModel { + environment: Environment; + commandsUsed: bigint; +} + +export interface StatModelCreationAttributes { + environment: Environment; + commandsUsed?: bigint; +} + +export class Stat extends BaseModel implements StatModel { + /** + * The bot's environment. + */ + public declare environment: Environment; + + /** + * The number of commands used + */ + public declare commandsUsed: bigint; + + /** + * Initializes the model. + * @param sequelize The sequelize instance. + */ + public static initModel(sequelize: Sequelize): void { + Stat.init( + { + environment: { type: DataTypes.STRING, primaryKey: true }, + commandsUsed: { + type: DataTypes.TEXT, + get: function (): bigint { + return BigInt(this.getDataValue('commandsUsed')); + }, + set: function (val: bigint) { + return this.setDataValue('commandsUsed', `${val}`); + }, + allowNull: false, + defaultValue: `${0n}` + } + }, + { sequelize } + ); + } +} -- cgit