diff --git a/.env.example b/.env.example index e2591d0..44b071b 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ # any secret values. BOT_TOKEN= -BOT_VER=3.2.4 +BOT_VER=3.2.3 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=682942374040961060 @@ -23,6 +23,4 @@ DB_NAME=vylbot DB_AUTH_USER=dev DB_AUTH_PASS=dev DB_SYNC=true -DB_LOGGING=true -DB_DATA_LOCATION=./.temp/database -DB_ROOT_HOST=0.0.0.0 \ No newline at end of file +DB_LOGGING=true \ No newline at end of file diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index 664cf11..fbf46d6 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -4,8 +4,6 @@ on: push: branches: - develop - - hotfix/* - - release/* jobs: build: @@ -24,7 +22,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: rsync -rvzP --delete . ${{ secrets.STAGE_REPO_PATH }} + run: rsync -rvzP . ${{ secrets.STAGE_REPO_PATH }} deploy: environment: prod @@ -44,7 +42,7 @@ jobs: DB_DATA_LOCATION: ${{ secrets.STAGE_DB_DATA_LOCATION }} SERVER_PATH: ${{ secrets.STAGE_SSH_SERVER_PATH }} BOT_TOKEN: ${{ secrets.STAGE_BOT_TOKEN }} - BOT_VER: ${{ github.ref_name }} + BOT_VER: ${{ vars.STAGE_BOT_VER }} BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }} BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }} BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }} diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index d4d6f74..cf4c550 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -4,6 +4,7 @@ on: push: branches: - feature/* + - hotfix/* - renovate/* jobs: diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql deleted file mode 100644 index 8412a02..0000000 --- a/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE auto_kick_config; diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql deleted file mode 100644 index e7d377a..0000000 --- a/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE auto_kick_config ( - Id varchar(255) NOT NULL, - WhenCreated datetime NOT NULL, - WhenUpdated datetime NOT NULL, - ServerId varchar(255) NOT NULL, - RoleId varchar(255) NOT NULL, - KickTime int NOT NULL, - NoticeTime int NULL, - NoticeChannelId varchar(255) NULL -); diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql deleted file mode 100644 index e3fec43..0000000 --- a/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE auto_kick_config - ADD PRIMARY KEY (Id); diff --git a/package.json b/package.json index 2cf6510..c5af404 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vylbot-app", - "version": "3.2.4", + "version": "3.2.3", "description": "A discord bot made for Vylpes' Den", "main": "./dist/vylbot", "typings": "./dist", @@ -30,7 +30,6 @@ "@discordjs/rest": "^2.0.0", "@types/jest": "^29.0.0", "@types/uuid": "^9.0.0", - "cron": "^3.3.1", "discord.js": "^14.3.0", "dotenv": "^16.0.0", "emoji-regex": "^10.0.0", diff --git a/src/client/client.ts b/src/client/client.ts index 86b34bd..c1ce5da 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,5 +1,6 @@ import { Client, Partials } from "discord.js"; import * as dotenv from "dotenv"; +import { createConnection } from "typeorm"; import { EventType } from "../constants/EventType"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; @@ -11,18 +12,14 @@ import AppDataSource from "../database/dataSources/appDataSource"; import ButtonEventItem from "../contracts/ButtonEventItem"; import { ButtonEvent } from "../type/buttonEvent"; import CacheHelper from "../helpers/CacheHelper"; -import TimerHelper from "../helpers/TimerHelper"; -import AutoKick from "../timers/AutoKick"; export class CoreClient extends Client { private static _commandItems: ICommandItem[]; private static _eventItems: IEventItem[]; private static _buttonEvents: ButtonEventItem[]; - private static _baseClient: Client; private _events: Events; private _util: Util; - private _timerHelper: TimerHelper; public static get commandItems(): ICommandItem[] { return this._commandItems; @@ -36,10 +33,6 @@ export class CoreClient extends Client { return this._buttonEvents; } - public static get baseClient(): Client { - return this._baseClient; - } - constructor(intents: number[], partials: Partials[]) { super({ intents: intents, partials: partials }); dotenv.config(); @@ -50,7 +43,6 @@ export class CoreClient extends Client { this._events = new Events(); this._util = new Util(); - this._timerHelper = new TimerHelper(); } public async start() { @@ -59,16 +51,8 @@ export class CoreClient extends Client { return; } - CoreClient._baseClient = this; - await AppDataSource.initialize() - .then(() => { - console.log("Data Source Initialized"); - - this._timerHelper.AddTimer("0 * * * *", "Europe/London", AutoKick, false); - - this._timerHelper.StartAllTimers(); - }) + .then(() => console.log("Data Source Initialized")) .catch((err) => console.error("Error Initialising Data Source", err)); super.on("interactionCreate", this._events.onInteractionCreate); diff --git a/src/commands/autokick.ts b/src/commands/autokick.ts deleted file mode 100644 index 760441e..0000000 --- a/src/commands/autokick.ts +++ /dev/null @@ -1,113 +0,0 @@ -import {ChatInputCommandInteraction, CommandInteraction, EmbedBuilder, PermissionFlagsBits, SlashCommandBuilder} from "discord.js"; -import {Command} from "../type/command"; -import TimeLengthInput from "../helpers/TimeLengthInput"; -import AutoKickHelper from "../helpers/AutoKickHelper"; - -export default class Autokick extends Command { - constructor() { - super(); - - this.CommandBuilder = new SlashCommandBuilder() - .setName("autokick") - .setDescription("Configure the auto kick functionality") - .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers) - .addSubcommand(x => x - .setName("set") - .setDescription("Set the configuration") - .addRoleOption(y => y - .setName("role") - .setDescription("The role the user needs to be auto kicked") - .setRequired(true)) - .addStringOption(y => y - .setName("kicktime") - .setDescription("The time with the role before being kicked (Ex: 2h 30m)") - .setRequired(true)) - .addStringOption(y => y - .setName("noticetime") - .setDescription("The time before being kicked when a notification is sent (Ex: 2h 30m)")) - .addChannelOption(y => y - .setName("noticechannel") - .setDescription("The channel to send the notification to"))) - .addSubcommand(x => x - .setName("unset") - .setDescription("Unset the current configuration")); - } - - public override async execute(interaction: CommandInteraction) { - if (!interaction.isChatInputCommand()) return; - - const subcommand = interaction.options.getSubcommand(); - - switch (subcommand) { - case "set": - await this.set(interaction); - break; - case "unset": - await this.unset(interaction); - break; - } - } - - private async set(interaction: ChatInputCommandInteraction) { - if (!interaction.guildId) return; - - const roleOption = interaction.options.getRole("role", true); - const kickTimeOption = interaction.options.getString("kicktime", true); - const noticeTimeOption = interaction.options.getString("noticetime"); - const noticeChannelOption = interaction.options.getChannel("noticechannel"); - - const roleId = roleOption.id; - const kickTimeInput = new TimeLengthInput(kickTimeOption); - const noticeTimeInput = noticeTimeOption ? new TimeLengthInput(noticeTimeOption) : undefined; - const noticeChannelId = noticeChannelOption?.id; - - if ((noticeTimeInput && !noticeTimeOption) || (!noticeTimeInput && noticeChannelOption)) { - await interaction.reply("Both `noticetime` and `noticechannel` must be set if you want a notification embed"); - return; - } - - await AutoKickHelper.SetSetting(interaction.guildId, roleId, kickTimeInput.GetMilliseconds(), noticeTimeInput?.GetMilliseconds(), noticeChannelId); - - const embed = new EmbedBuilder() - .setTitle("Auto Kick") - .setDescription("Configured auto kick for this server") - .addFields([ - { - name: "Role", - value: roleOption.name, - inline: true, - }, - { - name: "Kick Time", - value: kickTimeInput.GetLengthShort(), - inline: true, - }, - ]); - - if (noticeTimeInput) { - embed.addFields([ - { - name: "Notice Time", - value: noticeTimeInput.GetLengthShort(), - }, - { - name: "Notice Channel", - value: noticeChannelOption!.name!, - inline: true, - }, - ]); - } - - await interaction.reply({ - embeds: [ embed ], - }); - } - - private async unset(interaction: ChatInputCommandInteraction) { - if (!interaction.guildId) return; - - await AutoKickHelper.UnsetSetting(interaction.guildId); - - await interaction.reply("Unset the auto kick configuration for this server"); - } -} diff --git a/src/constants/EmbedColours.ts b/src/constants/EmbedColours.ts index dd84b94..023c77a 100644 --- a/src/constants/EmbedColours.ts +++ b/src/constants/EmbedColours.ts @@ -1,5 +1,3 @@ export default class EmbedColours { public static readonly Ok = 0x3050ba; - public static readonly Warning = 0xffbf00; - public static readonly Danger = 0xd2042d; } \ No newline at end of file diff --git a/src/database/entities/AutoKickConfig.ts b/src/database/entities/AutoKickConfig.ts deleted file mode 100644 index 6a82ba5..0000000 --- a/src/database/entities/AutoKickConfig.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {Column, Entity} from "typeorm"; -import AppDataSource from "../dataSources/appDataSource"; -import BaseEntity from "../../contracts/BaseEntity"; - -@Entity() -export default class AutoKickConfig extends BaseEntity { - constructor(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { - super(); - - this.ServerId = serverId; - this.RoleId = roleId; - this.KickTime = kickTime; - this.NoticeTime = noticeTime; - this.NoticeChannelId = noticeChannelId; - } - - @Column() - ServerId: string; - - @Column() - RoleId: string; - - @Column({ type: "int" }) - KickTime: number; - - @Column({ type: "int", nullable: true }) - NoticeTime?: number; - - @Column({ nullable: true }) - NoticeChannelId?: string; - - public UpdateBasicDetails(roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { - this.RoleId = roleId; - this.KickTime = kickTime; - this.NoticeTime = noticeTime; - this.NoticeChannelId = noticeChannelId; - } - - public static async FetchOneByServerIdAndRoleId(serverId: string, roleId: string): Promise { - const repository = AppDataSource.getRepository(AutoKickConfig); - - const query = repository - .createQueryBuilder("config") - .where("config.serverId = :serverId", { serverId }) - .andWhere("config.roleId = :roleId", { roleId }) - .getOne(); - - return query; - } - - public static async FetchAllByServerId(serverId: string): Promise { - const repository = AppDataSource.getRepository(AutoKickConfig); - - const query = repository - .createQueryBuilder("config") - .where("config.serverId = :serverId", { serverId }) - .getMany(); - - return query; - } -} diff --git a/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts b/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts deleted file mode 100644 index c4e9a74..0000000 --- a/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import MigrationHelper from "../../../helpers/MigrationHelper"; - -export class CreateAutoKickConfig1732973911304 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - MigrationHelper.Up("1732973911304-createAutoKickConfig", "3.2.4", [ - "01-AutoKickConfig-Table", - "02-AutoKickConfig-Key", - ], queryRunner) - } - - public async down(queryRunner: QueryRunner): Promise { - MigrationHelper.Down("1732973911304-createAutoKickConfig", "3.2.4", [ - "01-AutoKickConfig", - ], queryRunner) - } - -} diff --git a/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts index 0c9e53f..5778764 100644 --- a/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts +++ b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts @@ -6,7 +6,11 @@ export default async function LinkOnlyMode(message: Message) { const gifOnlyMode = await SettingsHelper.GetSetting("channel.linkonly", message.guild.id); - if (gifOnlyMode != message.channel.id) return; + if (!gifOnlyMode) return; + + const channel = message.guild.channels.cache.find(x => x.id == gifOnlyMode) || message.guild.channels.fetch(gifOnlyMode); + + if (!channel) return; if (message.content.startsWith("https://") || message.content.startsWith("http://")) return; diff --git a/src/helpers/AutoKickHelper.ts b/src/helpers/AutoKickHelper.ts deleted file mode 100644 index a02a934..0000000 --- a/src/helpers/AutoKickHelper.ts +++ /dev/null @@ -1,37 +0,0 @@ -import AutoKickConfig from "../database/entities/AutoKickConfig"; - -export default class AutoKickHelper { - public static async GetSetting(serverId: string): Promise { - const configs = await AutoKickConfig.FetchAllByServerId(serverId); - - if (configs.length != 1) { - return null; - } - - return configs[0]; - } - - public static async SetSetting(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { - const configs = await AutoKickConfig.FetchAllByServerId(serverId); - - if (configs.length == 0) { - const config = new AutoKickConfig(serverId, roleId, kickTime, noticeTime, noticeChannelId); - await config.Save(AutoKickConfig, config); - - return; - } - - const config = configs[0]; - - config.UpdateBasicDetails(roleId, kickTime, noticeTime, noticeChannelId); - await config.Save(AutoKickConfig, config); - } - - public static async UnsetSetting(serverId: string) { - const configs = await AutoKickConfig.FetchAllByServerId(serverId); - - for (let config of configs) { - await AutoKickConfig.Remove(AutoKickConfig, config); - } - } -} diff --git a/src/helpers/TimerHelper.ts b/src/helpers/TimerHelper.ts deleted file mode 100644 index 3cc4246..0000000 --- a/src/helpers/TimerHelper.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {CronJob} from "cron"; -import {primitive} from "../type/primitive"; -import {v4} from "uuid"; - -interface Timer { - id: string; - job: CronJob; - context: Map; - onTick: ((context: Map) => void) | ((context: Map) => Promise); - runOnStart: boolean; -} - -export default class TimerHelper { - private _timers: Timer[]; - - constructor() { - this._timers = []; - } - - public AddTimer( - cronTime: string, - timeZone: string, - onTick: ((context: Map) => void) | ((context: Map) => Promise), - runOnStart: boolean = false): string { - const context = new Map(); - - const job = new CronJob( - cronTime, - () => { - onTick(context); - }, - null, - false, - timeZone, - ); - - const id = v4(); - - this._timers.push({ - id, - job, - context, - onTick, - runOnStart, - }); - - return id; - } - - public StartAllTimers() { - this._timers.forEach(timer => this.StartJob(timer)); - } - - public StopAllTimers() { - this._timers.forEach(timer => timer.job.stop()); - } - - public StartTimer(id: string) { - const timer = this._timers.find(x => x.id == id); - - if (!timer) return; - - this.StartJob(timer); - } - - public StopTimer(id: string) { - const timer = this._timers.find(x => x.id == id); - - if (!timer) return; - - timer.job.stop(); - } - - private StartJob(timer: Timer) { - timer.job.start(); - - if (timer.runOnStart) { - timer.onTick(timer.context); - } - } -} diff --git a/src/registry.ts b/src/registry.ts index 36dd021..a2d7391 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -4,7 +4,6 @@ import { EventType } from "./constants/EventType"; // Command Imports import About from "./commands/about"; import Audits from "./commands/audits"; -import Autokick from "./commands/autokick"; import Ban from "./commands/ban"; import Bunny from "./commands/bunny"; import Clear from "./commands/clear"; @@ -46,7 +45,6 @@ export default class Registry { public static RegisterCommands() { CoreClient.RegisterCommand("about", new About()); CoreClient.RegisterCommand("audits", new Audits()); - CoreClient.RegisterCommand("autokick", new Autokick()); CoreClient.RegisterCommand("ban", new Ban()); CoreClient.RegisterCommand("bunny", new Bunny()); CoreClient.RegisterCommand("clear", new Clear()); diff --git a/src/timers/AutoKick.ts b/src/timers/AutoKick.ts deleted file mode 100644 index fcaac56..0000000 --- a/src/timers/AutoKick.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { EmbedBuilder } from "discord.js"; -import {CoreClient} from "../client/client"; -import AutoKickConfig from "../database/entities/AutoKickConfig"; -import EmbedColours from "../constants/EmbedColours"; - -export default async function AutoKick() { - const client = CoreClient.baseClient; - const autoKickConfigs = await AutoKickConfig.FetchAll(AutoKickConfig); - - for (let config of autoKickConfigs) { - const guild = client.guilds.cache.find(x => x.id == config.ServerId) || await client.guilds.fetch(config.ServerId); - - if (!guild) { - console.error("Guild not found"); - - continue; - } - - await guild.members.fetch(); - - const role = guild.roles.cache.find(x => x.id == config.RoleId); - - if (!role) { - console.error("Role not found in guild"); - - continue; - } - - for (let memberEntity of role.members) { - const member = memberEntity[1]; - - if (!member.kickable) { - console.error("Member not kickable"); - - continue; - } - - const whenToKick = new Date(member.joinedTimestamp! + config.KickTime); - const now = new Date(); - - if (whenToKick < now) { - await member.kick("Auto Kicked"); - - if (config.NoticeChannelId) { - const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId); - - if (!channel?.isSendable()) { - console.log("Channel not sendable"); - - continue; - } - - const embed = new EmbedBuilder() - .setTitle("Auto Kicked User") - .setColor(EmbedColours.Danger) - .setThumbnail(member.user.avatarURL()) - .addFields([ - { - name: "User", - value: `<@${member.user.id}> \`${member.user.username}\``, - inline: true, - }, - ]); - - await channel.send({ - embeds: [ embed ], - }); - } - } else if (config.NoticeChannelId && config.NoticeTime) { - const whenToNotice = new Date(whenToKick.getTime() - config.NoticeTime); - whenToNotice.setMinutes(0, 0, 0); - whenToNotice.setHours(whenToNotice.getHours() + 1); - - const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId); - - if (!channel?.isSendable()) { - console.error("Channel not sendable"); - - continue; - } - - if (now.getMonth() == whenToNotice.getMonth() - && now.getDate() == whenToNotice.getDate() - && now.getHours() == whenToNotice.getHours()) { - - const nextHour = new Date(whenToKick); - nextHour.setMinutes(0, 0, 0); - nextHour.setHours(whenToKick.getHours() + 1); - - const embed = new EmbedBuilder() - .setTitle("Auto Kick Notice") - .setColor(EmbedColours.Warning) - .setThumbnail(member.user.avatarURL()) - .addFields([ - { - name: "User", - value: `<@${member.user.id}> \`${member.user.username}\``, - inline: true, - }, - { - name: "When To Kick", - value: ``, - inline: true, - }, - ]); - - await channel.send({ - embeds: [ embed ], - }); - } - } - } - } -} diff --git a/src/type/primitive.ts b/src/type/primitive.ts deleted file mode 100644 index 9093737..0000000 --- a/src/type/primitive.ts +++ /dev/null @@ -1 +0,0 @@ -export type primitive = string | number | boolean; diff --git a/yarn.lock b/yarn.lock index 24feeb5..ced241e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -788,11 +788,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/luxon@~3.4.0": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" - integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== - "@types/node@*": version "22.7.5" resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" @@ -1463,14 +1458,6 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" -cron@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/cron/-/cron-3.3.1.tgz#03c56b4a3ad52606160adfba1fab932c53838807" - integrity sha512-KpvuzJEbeTMTfLsXhUuDfsFYr8s5roUlLKb4fa68GszWrA4783C7q6m9yj4vyc6neyD/V9e0YiADSX2c+yRDXg== - dependencies: - "@types/luxon" "~3.4.0" - luxon "~3.5.0" - cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -3089,11 +3076,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -luxon@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" - integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== - magic-bytes.js@^1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz"