From 92231d9e8b34ed359b6532cdd38e444e8af3de87 Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Thu, 17 Nov 2022 00:37:40 +0100 Subject: [PATCH 1/6] make the warn command's DM not show who issued the warn --- src/commands/guild/warn.ts | 70 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/commands/guild/warn.ts b/src/commands/guild/warn.ts index c45747f..590b1f6 100644 --- a/src/commands/guild/warn.ts +++ b/src/commands/guild/warn.ts @@ -2,7 +2,7 @@ import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.j import { Command } from '../../types/interaction'; import { PermissionLevel } from '../../utils/permissions'; import { LogLevelColor } from '../../utils/log'; -import { formatDate, formatUserRaw } from '../../utils/utils'; +import { formatDate } from '../../utils/utils'; import * as persist from '../../utils/persist'; import { getWarnList } from '../shared/warnlist'; @@ -56,49 +56,49 @@ const Warn: Command = { const user = interaction.options.getUser('user', true); switch (interaction.options.getSubcommand()) { - case 'add': { - if (!(user.id in data.moderation.warns)) { - data.moderation.warns[user.id] = []; + case 'add': { + if (!(user.id in data.moderation.warns)) { + data.moderation.warns[user.id] = []; + persist.saveData(interaction.guild.id); + } + + const reason = interaction.options.getString('reason', true); + data.moderation.warns[user.id].push({ + date: Date.now(), + reason: reason, + issuer: interaction.user.id, + }); persist.saveData(interaction.guild.id); - } - const reason = interaction.options.getString('reason', true); - data.moderation.warns[user.id].push({ - date: Date.now(), - reason: reason, - issuer: interaction.user.id, - }); - persist.saveData(interaction.guild.id); + if (interaction.options.getBoolean('dm_offender', false) ?? true) { + const embed = new EmbedBuilder() + .setColor(LogLevelColor.WARNING) + .setTitle('WARN') + .setFields({ name: 'You have been warned for the following reason:', value: `\`${reason}\`` }) + .setFooter({ text: `Issued in "${interaction.guild.name}" at ${formatDate( Date.now() )}` }) + .setTimestamp(); - if (interaction.options.getBoolean('dm_offender', false) ?? true) { - const embed = new EmbedBuilder() - .setColor(LogLevelColor.WARNING) - .setTitle('WARN') - .setFields({ name: 'You have been warned for the following reason:', value: `\`${reason}\`` }) - .setFooter({ text: `Issued by ${formatUserRaw(interaction.user)} in "${interaction.guild.name}"` }) - .setTimestamp(); + await user.send({ embeds: [embed] }); + } - await user.send({ embeds: [embed] }); + return interaction.reply({ content: `Warned ${user}, this is warn \\#${data.moderation.warns[user.id].length}.`, ephemeral: true }); } - return interaction.reply({ content: `Warned ${user}, this is warn \\#${data.moderation.warns[user.id].length}.`, ephemeral: true }); - } - - case 'list': { - return getWarnList(interaction, interaction.guild, user, interaction.options.getBoolean('ephemeral', false) ?? false); - } - - case 'clear': { - if (!Object.hasOwn(data.moderation.warns, user.id)) { - return interaction.reply({ content: `${user} has no warns to clear.`, ephemeral: true }); + case 'list': { + return getWarnList(interaction, interaction.guild, user, interaction.options.getBoolean('ephemeral', false) ?? false); } - const amount = data.moderation.warns[user.id].length; - delete data.moderation.warns[user.id]; - persist.saveData(interaction.guild.id); + case 'clear': { + if (!Object.hasOwn(data.moderation.warns, user.id)) { + return interaction.reply({ content: `${user} has no warns to clear.`, ephemeral: true }); + } - return interaction.reply({ content: `Cleared ${amount} warns for ${user}.`, ephemeral: true }); - } + const amount = data.moderation.warns[user.id].length; + delete data.moderation.warns[user.id]; + persist.saveData(interaction.guild.id); + + return interaction.reply({ content: `Cleared ${amount} warns for ${user}.`, ephemeral: true }); + } } } }; From 14d820de7f6ca5e2b8d65c1051e6e0a94308cc2c Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Thu, 17 Nov 2022 00:41:02 +0100 Subject: [PATCH 2/6] remove timestamp from footer --- src/commands/guild/warn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/guild/warn.ts b/src/commands/guild/warn.ts index 590b1f6..73803ea 100644 --- a/src/commands/guild/warn.ts +++ b/src/commands/guild/warn.ts @@ -75,7 +75,7 @@ const Warn: Command = { .setColor(LogLevelColor.WARNING) .setTitle('WARN') .setFields({ name: 'You have been warned for the following reason:', value: `\`${reason}\`` }) - .setFooter({ text: `Issued in "${interaction.guild.name}" at ${formatDate( Date.now() )}` }) + .setFooter({ text: `Issued in "${interaction.guild.name}"` }) .setTimestamp(); await user.send({ embeds: [embed] }); From 4eb7b8a8cd12b6a2cf839ce322ac39a592e521bb Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Thu, 17 Nov 2022 00:42:46 +0100 Subject: [PATCH 3/6] unindent switch --- src/commands/guild/warn.ts | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/commands/guild/warn.ts b/src/commands/guild/warn.ts index 73803ea..f970bd5 100644 --- a/src/commands/guild/warn.ts +++ b/src/commands/guild/warn.ts @@ -56,49 +56,49 @@ const Warn: Command = { const user = interaction.options.getUser('user', true); switch (interaction.options.getSubcommand()) { - case 'add': { - if (!(user.id in data.moderation.warns)) { - data.moderation.warns[user.id] = []; - persist.saveData(interaction.guild.id); - } - - const reason = interaction.options.getString('reason', true); - data.moderation.warns[user.id].push({ - date: Date.now(), - reason: reason, - issuer: interaction.user.id, - }); + case 'add': { + if (!(user.id in data.moderation.warns)) { + data.moderation.warns[user.id] = []; persist.saveData(interaction.guild.id); + } - if (interaction.options.getBoolean('dm_offender', false) ?? true) { - const embed = new EmbedBuilder() - .setColor(LogLevelColor.WARNING) - .setTitle('WARN') - .setFields({ name: 'You have been warned for the following reason:', value: `\`${reason}\`` }) - .setFooter({ text: `Issued in "${interaction.guild.name}"` }) - .setTimestamp(); + const reason = interaction.options.getString('reason', true); + data.moderation.warns[user.id].push({ + date: Date.now(), + reason: reason, + issuer: interaction.user.id, + }); + persist.saveData(interaction.guild.id); - await user.send({ embeds: [embed] }); - } + if (interaction.options.getBoolean('dm_offender', false) ?? true) { + const embed = new EmbedBuilder() + .setColor(LogLevelColor.WARNING) + .setTitle('WARN') + .setFields({ name: 'You have been warned for the following reason:', value: `\`${reason}\`` }) + .setFooter({ text: `Issued in "${interaction.guild.name}"` }) + .setTimestamp(); - return interaction.reply({ content: `Warned ${user}, this is warn \\#${data.moderation.warns[user.id].length}.`, ephemeral: true }); + await user.send({ embeds: [embed] }); } - case 'list': { - return getWarnList(interaction, interaction.guild, user, interaction.options.getBoolean('ephemeral', false) ?? false); - } - - case 'clear': { - if (!Object.hasOwn(data.moderation.warns, user.id)) { - return interaction.reply({ content: `${user} has no warns to clear.`, ephemeral: true }); - } + return interaction.reply({ content: `Warned ${user}, this is warn \\#${data.moderation.warns[user.id].length}.`, ephemeral: true }); + } - const amount = data.moderation.warns[user.id].length; - delete data.moderation.warns[user.id]; - persist.saveData(interaction.guild.id); + case 'list': { + return getWarnList(interaction, interaction.guild, user, interaction.options.getBoolean('ephemeral', false) ?? false); + } - return interaction.reply({ content: `Cleared ${amount} warns for ${user}.`, ephemeral: true }); + case 'clear': { + if (!Object.hasOwn(data.moderation.warns, user.id)) { + return interaction.reply({ content: `${user} has no warns to clear.`, ephemeral: true }); } + + const amount = data.moderation.warns[user.id].length; + delete data.moderation.warns[user.id]; + persist.saveData(interaction.guild.id); + + return interaction.reply({ content: `Cleared ${amount} warns for ${user}.`, ephemeral: true }); + } } } }; From a8563d1c0e37ac5e34b8ed0fcc2bbd067de8fb4b Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Thu, 17 Nov 2022 00:43:46 +0100 Subject: [PATCH 4/6] remove useless import --- src/commands/guild/warn.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/guild/warn.ts b/src/commands/guild/warn.ts index f970bd5..d87d334 100644 --- a/src/commands/guild/warn.ts +++ b/src/commands/guild/warn.ts @@ -2,7 +2,6 @@ import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.j import { Command } from '../../types/interaction'; import { PermissionLevel } from '../../utils/permissions'; import { LogLevelColor } from '../../utils/log'; -import { formatDate } from '../../utils/utils'; import * as persist from '../../utils/persist'; import { getWarnList } from '../shared/warnlist'; From 73a7e78b52ba260a234d386a58c9ef667322a1e6 Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Thu, 17 Nov 2022 00:53:42 +0100 Subject: [PATCH 5/6] initial draft of data --- src/types/persist.ts | 6 ++++++ src/utils/scheduler.ts | 0 2 files changed, 6 insertions(+) create mode 100644 src/utils/scheduler.ts diff --git a/src/types/persist.ts b/src/types/persist.ts index 16d7bb4..6374816 100644 --- a/src/types/persist.ts +++ b/src/types/persist.ts @@ -35,6 +35,12 @@ export interface PersistentData { issuer: string, }[], }, + tempban: Array<{ + time: number, + user: string, + reason: string, + issuer: string, + }> }, reaction_roles: { [message: string]: { diff --git a/src/utils/scheduler.ts b/src/utils/scheduler.ts new file mode 100644 index 0000000..e69de29 From 79eeaa9d6e423cbaefcd52ef1d86a2ca836e8dfd Mon Sep 17 00:00:00 2001 From: ENDERZOMBI102 Date: Sat, 7 Jan 2023 16:42:19 +0100 Subject: [PATCH 6/6] scheduler --- src/commands/guild/tempban.ts | 68 ++++++++++++++++++++++++++++++++++ src/commands/shared/tempban.ts | 19 ++++++++++ src/main.ts | 9 +++++ src/types/client.ts | 4 ++ src/types/persist.ts | 16 ++++++-- src/types/scheduler.ts | 62 +++++++++++++++++++++++++++++++ src/utils/scheduler.ts | 47 +++++++++++++++++++++++ 7 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/commands/guild/tempban.ts create mode 100644 src/commands/shared/tempban.ts create mode 100644 src/types/scheduler.ts diff --git a/src/commands/guild/tempban.ts b/src/commands/guild/tempban.ts new file mode 100644 index 0000000..1f84aff --- /dev/null +++ b/src/commands/guild/tempban.ts @@ -0,0 +1,68 @@ +import { CommandInteraction, SlashCommandBuilder } from 'discord.js'; +import { Command } from '../../types/interaction'; +import { PermissionLevel } from '../../utils/permissions'; +import * as scheduler from '../../utils/scheduler'; +import {formatDate, formatUserRaw} from "../../utils/utils"; +import {unbanHandler} from "../shared/tempban"; + +const TempBan: Command = { + permissionLevel: PermissionLevel.MODERATOR, + + data: new SlashCommandBuilder() + .setName('tempban') + .setDescription('Bans the given user for X time.') + .addUserOption(option => option + .setName('user') + .setDescription('The user to ban') + .setRequired(true)) + .addIntegerOption(option => option + .setName('m') + .setDescription('Amount of minutes before unban') + .setRequired(false)) + .addIntegerOption(option => option + .setName('h') + .setDescription('Amount of hours before unban') + .setRequired(false)) + .addIntegerOption(option => option + .setName('d') + .setDescription('Amount of days before unban') + .setRequired(false)) + .addIntegerOption(option => option + .setName('w') + .setDescription('Amount of weeks before unban') + .setRequired(false)) + .addIntegerOption(option => option + .setName('M') + .setDescription('Amount of months before unban') + .setRequired(false)) + .addIntegerOption(option => option + .setName('y') + .setDescription('Amount of years before unban') + .setRequired(false)), + + async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) + return; + + if (!interaction.inGuild() || !interaction.guild) + return interaction.reply({ content: 'This command must be ran in a guild.', ephemeral: true }); + + const user = interaction.options.getUser('user', true); + const minutes = interaction.options.getInteger( 'm', false ) || 0; + const hours = interaction.options.getInteger( 'h', false ) || 0; + const days = interaction.options.getInteger( 'd', false ) || 0; + const weeks = interaction.options.getInteger( 'w', false ) || 0; + const months = interaction.options.getInteger( 'M', false ) || 0; + const years = interaction.options.getInteger( 'y', false ) || 0; + + if ( minutes + hours + days + weeks + months + years == 0 ) + return interaction.reply({ content: 'This command requires at least one non-zero parameter.', ephemeral: true }); + + const task = scheduler.schedule( interaction.guild.id, { delay: 0 }, unbanHandler ); + + await interaction.guild.bans.create(user); + + return interaction.reply({ content: `Banned ${formatUserRaw(user)} until ${formatDate(task.date)}`, ephemeral: true }); + } +}; +export default TempBan; diff --git a/src/commands/shared/tempban.ts b/src/commands/shared/tempban.ts new file mode 100644 index 0000000..5c5dac0 --- /dev/null +++ b/src/commands/shared/tempban.ts @@ -0,0 +1,19 @@ +import {MoralityCoreClient} from '../../types/client'; + +import * as persist from '../../utils/persist'; +import {resume} from '../../utils/scheduler'; +import { ok as assert } from 'assert'; +import {ScheduledTask} from '../../types/scheduler'; + +export function register( client: MoralityCoreClient ): void { + for ( const guild of client.guilds.valueOf().values() ) + for ( const { taskId } of persist.data(guild.id).moderation.tempban ) + resume( guild.id, taskId, unbanHandler ); +} + +export function unbanHandler( task: ScheduledTask, data: unknown ): void { + assert( data instanceof { guild: String }, 'unbanHandler was passed a non-string parameter!!' ); + + const client = MoralityCoreClient.get(); + client.guilds.fetch( ) +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index d7d2f8a..7a5b294 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,8 @@ import { updateCommands, updateCommandsForGuild } from './utils/update_commands' import * as config from './config.json'; import * as log from './utils/log'; import * as persist from './utils/persist'; +import * as scheduler from './utils/scheduler'; +import * as tempban from './commands/shared/tempban'; // Make console output better import consoleStamp from 'console-stamp'; @@ -344,13 +346,20 @@ async function main() { } }); + // start the scheduler + scheduler.run(); + // Log in await client.login(config.token); + // register all tempban stuff again + tempban.register( client ); + process.on('SIGINT', () => { const date = new Date(); log.writeToLog(undefined, `--- BOT END AT ${date.toDateString()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} ---`); client.destroy(); + scheduler.shutdown(); persist.saveAll(); process.exit(); }); diff --git a/src/types/client.ts b/src/types/client.ts index 514bec7..e03f56e 100644 --- a/src/types/client.ts +++ b/src/types/client.ts @@ -60,6 +60,7 @@ export class Callbacks { } export class MoralityCoreClient extends Client { + private static instance: MoralityCoreClient; commands: Collection; callbacks: Callbacks; @@ -67,5 +68,8 @@ export class MoralityCoreClient extends Client { super(options); this.commands = new Collection(); this.callbacks = new Callbacks(); + MoralityCoreClient.instance = this; } + + static get = () => MoralityCoreClient.instance; } diff --git a/src/types/persist.ts b/src/types/persist.ts index 6374816..2bcff17 100644 --- a/src/types/persist.ts +++ b/src/types/persist.ts @@ -35,12 +35,12 @@ export interface PersistentData { issuer: string, }[], }, - tempban: Array<{ - time: number, + tempban: { + taskId: string, user: string, reason: string, issuer: string, - }> + }[] }, reaction_roles: { [message: string]: { @@ -54,9 +54,17 @@ export interface PersistentData { responses: { [response_id: string]: string, }, - watched_threads: Array, + watched_threads: string[], statistics: { joins: number, leaves: number, }, + scheduler: { + [taskid: string]: { + data: unknown, + expirationTime: number, + repeat: boolean, + delay: number + } + } } diff --git a/src/types/scheduler.ts b/src/types/scheduler.ts new file mode 100644 index 0000000..c2d04e0 --- /dev/null +++ b/src/types/scheduler.ts @@ -0,0 +1,62 @@ +export interface RepeatedSchedulePlan { + + /** + * ms before this task is initially executed + */ + initialDelay: number, + + /** + * ms between task executions + */ + distance: number +} + +export interface SingleSchedulePlan { + /** + * ms before task execution + */ + delay: number +} + +export type SchedulePlan = RepeatedSchedulePlan | SingleSchedulePlan; + +export type Task = ( task: ScheduledTask, data: unknown ) => void + +/** + * Represents a task that has been submitted to the scheduler. + */ +export interface ScheduledTask { + /** + * Unique id assigned to this scheduled task. + */ + readonly id: string, + + /** + * The task that will be executed. + */ + readonly task: Task, + + /** + * The unix timestamp at which this task will run. + */ + readonly date: number, + + /** + * The guild this task was scheduled on. + readonly guild: string, + + /** + * Cancels and removes from persistence this task. + */ + cancel(): void, + + /** + * Whether this scheduled task was cancelled. + */ + cancelled(): boolean, + + /** + * Returns how many ms until this task is executed. + */ + remainingTime(): number, +} diff --git a/src/utils/scheduler.ts b/src/utils/scheduler.ts index e69de29..436a051 100644 --- a/src/utils/scheduler.ts +++ b/src/utils/scheduler.ts @@ -0,0 +1,47 @@ +import {SchedulePlan, Task, ScheduledTask} from "../types/scheduler"; +import * as persist from '../utils/persist'; + + +/** + * Resumes a task previously saved to persistence. + * @param id id of the task to get the data from. + * @param task + */ +export function resume( guild: string, id: string, task: Task ): ScheduledTask { + +} + +/** + * Schedules a task for execution. + * @param plan + * @param task + */ +export function schedule( guild: string, plan: SchedulePlan, task: Task ): ScheduledTask { + persist.data(guild).scheduler[] +} + +/** + * Cancels and removes from persistence a scheduled task. + * @param task + */ +export function cancelTask( task: ScheduledTask ): number { + +} + +export function run() { + setInterval( + () => { + + }, + 5 + ); +} + + +/** + * Stops the scheduler and saves all state. + */ +export function shutdown() { + +} +