From bfbf386eeb97f7cda6d6fb88c67b6dfe64f41092 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Fri, 25 Oct 2024 17:38:22 +0100 Subject: [PATCH 01/10] Fix link only mode not checking the message id --- src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts index 5778764..0c9e53f 100644 --- a/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts +++ b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts @@ -6,11 +6,7 @@ export default async function LinkOnlyMode(message: Message) { const gifOnlyMode = await SettingsHelper.GetSetting("channel.linkonly", message.guild.id); - if (!gifOnlyMode) return; - - const channel = message.guild.channels.cache.find(x => x.id == gifOnlyMode) || message.guild.channels.fetch(gifOnlyMode); - - if (!channel) return; + if (gifOnlyMode != message.channel.id) return; if (message.content.startsWith("https://") || message.content.startsWith("http://")) return; From cdf689f1c503c7f3fa549fe83bf1e09d2dfa1f1d Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Fri, 3 Jan 2025 17:47:16 +0000 Subject: [PATCH 02/10] Add auto kick functionality (#502) - Add command to configure the auto kick function - Added ability to run functions on a cron job - Added a cron job every hour to check if a user has had a role for a configured amount of time and kick them if they have - The function also optionally sends a notice embed at a configured time before the kick #485 Reviewed-on: https://git.vylpes.xyz/RabbitLabs/vylbot-app/pulls/502 Reviewed-by: VylpesTester Co-authored-by: Ethan Lane Co-committed-by: Ethan Lane --- .env.example | 6 +- .../Down/01-AutoKickConfig.sql | 1 + .../Up/01-AutoKickConfig-Table.sql | 10 ++ .../Up/02-AutoKickConfig-Key.sql | 2 + package.json | 1 + src/client/client.ts | 20 +++- src/commands/autokick.ts | 113 ++++++++++++++++++ src/constants/EmbedColours.ts | 2 + src/database/entities/AutoKickConfig.ts | 61 ++++++++++ .../1732973911304-createAutoKickConfig.ts | 19 +++ src/helpers/AutoKickHelper.ts | 37 ++++++ src/helpers/TimerHelper.ts | 81 +++++++++++++ src/registry.ts | 2 + src/timers/AutoKick.ts | 97 +++++++++++++++ src/type/primitive.ts | 1 + yarn.lock | 18 +++ 16 files changed, 467 insertions(+), 4 deletions(-) create mode 100644 database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql create mode 100644 database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql create mode 100644 database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql create mode 100644 src/commands/autokick.ts create mode 100644 src/database/entities/AutoKickConfig.ts create mode 100644 src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts create mode 100644 src/helpers/AutoKickHelper.ts create mode 100644 src/helpers/TimerHelper.ts create mode 100644 src/timers/AutoKick.ts create mode 100644 src/type/primitive.ts diff --git a/.env.example b/.env.example index 44b071b..e2591d0 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ # any secret values. BOT_TOKEN= -BOT_VER=3.2.3 +BOT_VER=3.2.4 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=682942374040961060 @@ -23,4 +23,6 @@ DB_NAME=vylbot DB_AUTH_USER=dev DB_AUTH_PASS=dev DB_SYNC=true -DB_LOGGING=true \ No newline at end of file +DB_LOGGING=true +DB_DATA_LOCATION=./.temp/database +DB_ROOT_HOST=0.0.0.0 \ No newline at end of file diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql new file mode 100644 index 0000000..8412a02 --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..e7d377a --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql @@ -0,0 +1,10 @@ +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 new file mode 100644 index 0000000..e3fec43 --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql @@ -0,0 +1,2 @@ +ALTER TABLE auto_kick_config + ADD PRIMARY KEY (Id); diff --git a/package.json b/package.json index c5af404..27b06a3 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@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 c1ce5da..86b34bd 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,6 +1,5 @@ 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"; @@ -12,14 +11,18 @@ 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; @@ -33,6 +36,10 @@ 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(); @@ -43,6 +50,7 @@ export class CoreClient extends Client { this._events = new Events(); this._util = new Util(); + this._timerHelper = new TimerHelper(); } public async start() { @@ -51,8 +59,16 @@ export class CoreClient extends Client { return; } + CoreClient._baseClient = this; + await AppDataSource.initialize() - .then(() => console.log("Data Source Initialized")) + .then(() => { + console.log("Data Source Initialized"); + + this._timerHelper.AddTimer("0 * * * *", "Europe/London", AutoKick, false); + + this._timerHelper.StartAllTimers(); + }) .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 new file mode 100644 index 0000000..760441e --- /dev/null +++ b/src/commands/autokick.ts @@ -0,0 +1,113 @@ +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 023c77a..dd84b94 100644 --- a/src/constants/EmbedColours.ts +++ b/src/constants/EmbedColours.ts @@ -1,3 +1,5 @@ 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 new file mode 100644 index 0000000..6a82ba5 --- /dev/null +++ b/src/database/entities/AutoKickConfig.ts @@ -0,0 +1,61 @@ +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 new file mode 100644 index 0000000..c4e9a74 --- /dev/null +++ b/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts @@ -0,0 +1,19 @@ +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/helpers/AutoKickHelper.ts b/src/helpers/AutoKickHelper.ts new file mode 100644 index 0000000..a02a934 --- /dev/null +++ b/src/helpers/AutoKickHelper.ts @@ -0,0 +1,37 @@ +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 new file mode 100644 index 0000000..3cc4246 --- /dev/null +++ b/src/helpers/TimerHelper.ts @@ -0,0 +1,81 @@ +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 a2d7391..36dd021 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -4,6 +4,7 @@ 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"; @@ -45,6 +46,7 @@ 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 new file mode 100644 index 0000000..61f743a --- /dev/null +++ b/src/timers/AutoKick.ts @@ -0,0 +1,97 @@ +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) { + continue; + } + + await guild.members.fetch(); + + const role = guild.roles.cache.find(x => x.id == config.RoleId); + + if (!role) { + continue; + } + + for (let memberEntity of role.members) { + const member = memberEntity[1]; + + if (!member.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()) { + 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); + + const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId); + + if (!channel?.isSendable()) { + continue; + } + + if (now.getMonth() == whenToNotice.getMonth() + && now.getDate() == whenToNotice.getDate() + && now.getHours() == whenToNotice.getHours()) { + 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 new file mode 100644 index 0000000..9093737 --- /dev/null +++ b/src/type/primitive.ts @@ -0,0 +1 @@ +export type primitive = string | number | boolean; diff --git a/yarn.lock b/yarn.lock index ced241e..24feeb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -788,6 +788,11 @@ 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" @@ -1458,6 +1463,14 @@ 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" @@ -3076,6 +3089,11 @@ 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" From 2ac2737bc0430f15ac1d4afecd5ac9d0bca740f3 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Fri, 10 Jan 2025 18:36:36 +0000 Subject: [PATCH 03/10] Add hotfix and release ci pipelines (#503) # Description Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. #492, #493 ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update # How Has This Been Tested? Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration. # Checklist - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that provide my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules Reviewed-on: https://git.vylpes.xyz/RabbitLabs/vylbot-app/pulls/503 Reviewed-by: VylpesTester Co-authored-by: Ethan Lane Co-committed-by: Ethan Lane --- .forgejo/workflows/hotfix.yml | 71 ++++++++++++++++++++++++++++++++++ .forgejo/workflows/release.yml | 71 ++++++++++++++++++++++++++++++++++ .forgejo/workflows/stage.yml | 4 ++ .forgejo/workflows/test.yml | 1 - 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 .forgejo/workflows/hotfix.yml create mode 100644 .forgejo/workflows/release.yml diff --git a/.forgejo/workflows/hotfix.yml b/.forgejo/workflows/hotfix.yml new file mode 100644 index 0000000..ea16e2c --- /dev/null +++ b/.forgejo/workflows/hotfix.yml @@ -0,0 +1,71 @@ +name: Deploy Hotfix To Stage + +on: + push: + branches: + - hotfix/* + +jobs: + build: + environment: prod + + runs-on: node + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 18.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test + + - name: "Copy files over to location" + run: cp -r . ${{ secrets.HOTFIX_REPO_PATH }} + + deploy: + environment: prod + needs: build + runs-on: node + steps: + - uses: https://github.com/appleboy/ssh-action@v1.0.0 + env: + DB_NAME: ${{ secrets.HOTFIX_DB_NAME }} + DB_AUTH_USER: ${{ secrets.HOTFIX_DB_AUTH_USER }} + DB_AUTH_PASS: ${{ secrets.HOTFIX_DB_AUTH_PASS }} + DB_HOST: ${{ secrets.HOTFIX_DB_HOST }} + DB_PORT: ${{ secrets.HOTFIX_DB_PORT }} + DB_ROOT_HOST: ${{ secrets.HOTFIX_DB_ROOT_HOST }} + DB_SYNC: ${{ secrets.HOTFIX_DB_SYNC }} + DB_LOGGING: ${{ secrets.HOTFIX_DB_LOGGING }} + DB_DATA_LOCATION: ${{ secrets.HOTFIX_DB_DATA_LOCATION }} + SERVER_PATH: ${{ secrets.HOTFIX_SSH_SERVER_PATH }} + BOT_TOKEN: ${{ secrets.HOTFIX_BOT_TOKEN }} + BOT_VER: ${{ vars.HOTFIX_BOT_VER }} + BOT_AUTHOR: ${{ vars.HOTFIX_BOT_AUTHOR }} + BOT_OWNERID: ${{ vars.HOTFIX_BOT_OWNERID }} + BOT_CLIENTID: ${{ vars.HOTFIX_BOT_CLIENTID }} + ABOUT_FUNDING: ${{ vars.HOTFIX_ABOUT_FUNDING }} + ABOUT_REPO: ${{ vars.HOTFIX_ABOUT_REPO }} + CACHE_INTERVAL: ${{ vars.HOTFIX_CACHE_INTERVAL }} + with: + host: ${{ secrets.HOTFIX_SSH_HOST }} + username: ${{ secrets.HOTFIX_SSH_USER }} + key: ${{ secrets.HOTFIX_SSH_KEY }} + port: ${{ secrets.HOTFIX_SSH_PORT }} + envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL + script: | + source .sshrc \ + && cd /home/vylpes/apps/vylbot/vylbot_hotfix \ + && docker compose down \ + && (pm2 stop vylbot_hotfix || true) \ + && (pm2 delete vylbot_hotfix || true) \ + && (pm2 stop vylbot_stage || true) \ + && (pm2 delete vylbot_stage || true) \ + && (pm2 stop vylbot_release || true) \ + && (pm2 delete vylbot_release || true) \ + && docker compose up -d \ + && sleep 10 \ + && yarn db:up \ + && pm2 start --name vylbot_hotfix dist/vylbot.js \ No newline at end of file diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml new file mode 100644 index 0000000..00af61f --- /dev/null +++ b/.forgejo/workflows/release.yml @@ -0,0 +1,71 @@ +name: Deploy Release To Stage + +on: + push: + branches: + - release/* + +jobs: + build: + environment: prod + + runs-on: node + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 18.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test + + - name: "Copy files over to location" + run: cp -r . ${{ secrets.RELEASE_REPO_PATH }} + + deploy: + environment: prod + needs: build + runs-on: node + steps: + - uses: https://github.com/appleboy/ssh-action@v1.0.0 + env: + DB_NAME: ${{ secrets.RELEASE_DB_NAME }} + DB_AUTH_USER: ${{ secrets.RELEASE_DB_AUTH_USER }} + DB_AUTH_PASS: ${{ secrets.RELEASE_DB_AUTH_PASS }} + DB_HOST: ${{ secrets.RELEASE_DB_HOST }} + DB_PORT: ${{ secrets.RELEASE_DB_PORT }} + DB_ROOT_HOST: ${{ secrets.RELEASE_DB_ROOT_HOST }} + DB_SYNC: ${{ secrets.RELEASE_DB_SYNC }} + DB_LOGGING: ${{ secrets.RELEASE_DB_LOGGING }} + DB_DATA_LOCATION: ${{ secrets.RELEASE_DB_DATA_LOCATION }} + SERVER_PATH: ${{ secrets.RELEASE_SSH_SERVER_PATH }} + BOT_TOKEN: ${{ secrets.RELEASE_BOT_TOKEN }} + BOT_VER: ${{ vars.RELEASE_BOT_VER }} + BOT_AUTHOR: ${{ vars.RELEASE_BOT_AUTHOR }} + BOT_OWNERID: ${{ vars.RELEASE_BOT_OWNERID }} + BOT_CLIENTID: ${{ vars.RELEASE_BOT_CLIENTID }} + ABOUT_FUNDING: ${{ vars.RELEASE_ABOUT_FUNDING }} + ABOUT_REPO: ${{ vars.RELEASE_ABOUT_REPO }} + CACHE_INTERVAL: ${{ vars.RELEASE_CACHE_INTERVAL }} + with: + host: ${{ secrets.RELEASE_SSH_HOST }} + username: ${{ secrets.RELEASE_SSH_USER }} + key: ${{ secrets.RELEASE_SSH_KEY }} + port: ${{ secrets.RELEASE_SSH_PORT }} + envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL + script: | + source .sshrc \ + && cd /home/vylpes/apps/vylbot/vylbot_release \ + && docker compose down \ + && (pm2 stop vylbot_release || true) \ + && (pm2 delete vylbot_release || true) \ + && (pm2 stop vylbot_hotfix || true) \ + && (pm2 delete vylbot_hotfix || true) \ + && (pm2 stop vylbot_stage || true) \ + && (pm2 delete vylbot_stage || true) \ + && docker compose up -d \ + && sleep 10 \ + && yarn db:up \ + && pm2 start --name vylbot_release dist/vylbot.js \ No newline at end of file diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index e8157b8..372a3d6 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -61,6 +61,10 @@ jobs: && docker compose down \ && (pm2 stop vylbot_stage || true) \ && (pm2 delete vylbot_stage || true) \ + && (pm2 stop vylbot_hotfix || true) \ + && (pm2 delete vylbot_hotfix || true) \ + && (pm2 stop vylbot_release || true) \ + && (pm2 delete vylbot_release || true) \ && docker compose up -d \ && sleep 10 \ && yarn db:up \ diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index cf4c550..d4d6f74 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -4,7 +4,6 @@ on: push: branches: - feature/* - - hotfix/* - renovate/* jobs: From d3a0c418beaa5a2bf45aff329682b135105d5297 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Fri, 10 Jan 2025 18:41:11 +0000 Subject: [PATCH 04/10] Use rsync --- .forgejo/workflows/hotfix.yml | 2 +- .forgejo/workflows/production.yml | 2 +- .forgejo/workflows/release.yml | 2 +- .forgejo/workflows/stage.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/hotfix.yml b/.forgejo/workflows/hotfix.yml index ea16e2c..3ce09a3 100644 --- a/.forgejo/workflows/hotfix.yml +++ b/.forgejo/workflows/hotfix.yml @@ -22,7 +22,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: cp -r . ${{ secrets.HOTFIX_REPO_PATH }} + run: rsync -rvzP . ${{ secrets.HOTFIX_REPO_PATH }} deploy: environment: prod diff --git a/.forgejo/workflows/production.yml b/.forgejo/workflows/production.yml index fa080ee..584bd56 100644 --- a/.forgejo/workflows/production.yml +++ b/.forgejo/workflows/production.yml @@ -22,7 +22,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: cp -r . ${{ secrets.PROD_REPO_PATH }} + run: rsync -rvzP . ${{ secrets.PROD_REPO_PATH }} deploy: environment: prod diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 00af61f..d816ada 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -22,7 +22,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: cp -r . ${{ secrets.RELEASE_REPO_PATH }} + run: rsync -rvzP . ${{ secrets.RELEASE_REPO_PATH }} deploy: environment: prod diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index 372a3d6..e81ef91 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -22,7 +22,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: cp -r . ${{ secrets.STAGE_REPO_PATH }} + run: rsync -rvzP . ${{ secrets.STAGE_REPO_PATH }} deploy: environment: prod From ccac9861a9189584defbde9193858bd15d10da0a Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 11 Jan 2025 16:07:04 +0000 Subject: [PATCH 05/10] Use just stage.yml --- .forgejo/workflows/hotfix.yml | 71 ---------------------------------- .forgejo/workflows/release.yml | 71 ---------------------------------- .forgejo/workflows/stage.yml | 4 +- 3 files changed, 3 insertions(+), 143 deletions(-) delete mode 100644 .forgejo/workflows/hotfix.yml delete mode 100644 .forgejo/workflows/release.yml diff --git a/.forgejo/workflows/hotfix.yml b/.forgejo/workflows/hotfix.yml deleted file mode 100644 index 3ce09a3..0000000 --- a/.forgejo/workflows/hotfix.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Deploy Hotfix To Stage - -on: - push: - branches: - - hotfix/* - -jobs: - build: - environment: prod - - runs-on: node - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js - uses: actions/setup-node@v1 - with: - node-version: 18.x - - run: yarn install --frozen-lockfile - - run: yarn build - - run: yarn test - - - name: "Copy files over to location" - run: rsync -rvzP . ${{ secrets.HOTFIX_REPO_PATH }} - - deploy: - environment: prod - needs: build - runs-on: node - steps: - - uses: https://github.com/appleboy/ssh-action@v1.0.0 - env: - DB_NAME: ${{ secrets.HOTFIX_DB_NAME }} - DB_AUTH_USER: ${{ secrets.HOTFIX_DB_AUTH_USER }} - DB_AUTH_PASS: ${{ secrets.HOTFIX_DB_AUTH_PASS }} - DB_HOST: ${{ secrets.HOTFIX_DB_HOST }} - DB_PORT: ${{ secrets.HOTFIX_DB_PORT }} - DB_ROOT_HOST: ${{ secrets.HOTFIX_DB_ROOT_HOST }} - DB_SYNC: ${{ secrets.HOTFIX_DB_SYNC }} - DB_LOGGING: ${{ secrets.HOTFIX_DB_LOGGING }} - DB_DATA_LOCATION: ${{ secrets.HOTFIX_DB_DATA_LOCATION }} - SERVER_PATH: ${{ secrets.HOTFIX_SSH_SERVER_PATH }} - BOT_TOKEN: ${{ secrets.HOTFIX_BOT_TOKEN }} - BOT_VER: ${{ vars.HOTFIX_BOT_VER }} - BOT_AUTHOR: ${{ vars.HOTFIX_BOT_AUTHOR }} - BOT_OWNERID: ${{ vars.HOTFIX_BOT_OWNERID }} - BOT_CLIENTID: ${{ vars.HOTFIX_BOT_CLIENTID }} - ABOUT_FUNDING: ${{ vars.HOTFIX_ABOUT_FUNDING }} - ABOUT_REPO: ${{ vars.HOTFIX_ABOUT_REPO }} - CACHE_INTERVAL: ${{ vars.HOTFIX_CACHE_INTERVAL }} - with: - host: ${{ secrets.HOTFIX_SSH_HOST }} - username: ${{ secrets.HOTFIX_SSH_USER }} - key: ${{ secrets.HOTFIX_SSH_KEY }} - port: ${{ secrets.HOTFIX_SSH_PORT }} - envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL - script: | - source .sshrc \ - && cd /home/vylpes/apps/vylbot/vylbot_hotfix \ - && docker compose down \ - && (pm2 stop vylbot_hotfix || true) \ - && (pm2 delete vylbot_hotfix || true) \ - && (pm2 stop vylbot_stage || true) \ - && (pm2 delete vylbot_stage || true) \ - && (pm2 stop vylbot_release || true) \ - && (pm2 delete vylbot_release || true) \ - && docker compose up -d \ - && sleep 10 \ - && yarn db:up \ - && pm2 start --name vylbot_hotfix dist/vylbot.js \ No newline at end of file diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml deleted file mode 100644 index d816ada..0000000 --- a/.forgejo/workflows/release.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Deploy Release To Stage - -on: - push: - branches: - - release/* - -jobs: - build: - environment: prod - - runs-on: node - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js - uses: actions/setup-node@v1 - with: - node-version: 18.x - - run: yarn install --frozen-lockfile - - run: yarn build - - run: yarn test - - - name: "Copy files over to location" - run: rsync -rvzP . ${{ secrets.RELEASE_REPO_PATH }} - - deploy: - environment: prod - needs: build - runs-on: node - steps: - - uses: https://github.com/appleboy/ssh-action@v1.0.0 - env: - DB_NAME: ${{ secrets.RELEASE_DB_NAME }} - DB_AUTH_USER: ${{ secrets.RELEASE_DB_AUTH_USER }} - DB_AUTH_PASS: ${{ secrets.RELEASE_DB_AUTH_PASS }} - DB_HOST: ${{ secrets.RELEASE_DB_HOST }} - DB_PORT: ${{ secrets.RELEASE_DB_PORT }} - DB_ROOT_HOST: ${{ secrets.RELEASE_DB_ROOT_HOST }} - DB_SYNC: ${{ secrets.RELEASE_DB_SYNC }} - DB_LOGGING: ${{ secrets.RELEASE_DB_LOGGING }} - DB_DATA_LOCATION: ${{ secrets.RELEASE_DB_DATA_LOCATION }} - SERVER_PATH: ${{ secrets.RELEASE_SSH_SERVER_PATH }} - BOT_TOKEN: ${{ secrets.RELEASE_BOT_TOKEN }} - BOT_VER: ${{ vars.RELEASE_BOT_VER }} - BOT_AUTHOR: ${{ vars.RELEASE_BOT_AUTHOR }} - BOT_OWNERID: ${{ vars.RELEASE_BOT_OWNERID }} - BOT_CLIENTID: ${{ vars.RELEASE_BOT_CLIENTID }} - ABOUT_FUNDING: ${{ vars.RELEASE_ABOUT_FUNDING }} - ABOUT_REPO: ${{ vars.RELEASE_ABOUT_REPO }} - CACHE_INTERVAL: ${{ vars.RELEASE_CACHE_INTERVAL }} - with: - host: ${{ secrets.RELEASE_SSH_HOST }} - username: ${{ secrets.RELEASE_SSH_USER }} - key: ${{ secrets.RELEASE_SSH_KEY }} - port: ${{ secrets.RELEASE_SSH_PORT }} - envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL - script: | - source .sshrc \ - && cd /home/vylpes/apps/vylbot/vylbot_release \ - && docker compose down \ - && (pm2 stop vylbot_release || true) \ - && (pm2 delete vylbot_release || true) \ - && (pm2 stop vylbot_hotfix || true) \ - && (pm2 delete vylbot_hotfix || true) \ - && (pm2 stop vylbot_stage || true) \ - && (pm2 delete vylbot_stage || true) \ - && docker compose up -d \ - && sleep 10 \ - && yarn db:up \ - && pm2 start --name vylbot_release dist/vylbot.js \ No newline at end of file diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index e81ef91..7db6827 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -4,6 +4,8 @@ on: push: branches: - develop + - hotfix/* + - release/* jobs: build: @@ -42,7 +44,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: ${{ vars.STAGE_BOT_VER }} + BOT_VER: ${{ vars.STAGE_BOT_VER }}-${{ github.ref_name }} BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }} BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }} BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }} From 9dd72a074bf4579d4a8efe754ec8374e815f819e Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 11 Jan 2025 16:10:00 +0000 Subject: [PATCH 06/10] Remove no longer used pm2 stop scripts --- .forgejo/workflows/stage.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index 7db6827..6fefed1 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -63,10 +63,6 @@ jobs: && docker compose down \ && (pm2 stop vylbot_stage || true) \ && (pm2 delete vylbot_stage || true) \ - && (pm2 stop vylbot_hotfix || true) \ - && (pm2 delete vylbot_hotfix || true) \ - && (pm2 stop vylbot_release || true) \ - && (pm2 delete vylbot_release || true) \ && docker compose up -d \ && sleep 10 \ && yarn db:up \ From 0fad587ae9db268ec9d8e79ff6f493677b409844 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 11 Jan 2025 16:11:13 +0000 Subject: [PATCH 07/10] Delete files not in the destination --- .forgejo/workflows/stage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index 6fefed1..d8425bd 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -24,7 +24,7 @@ jobs: - run: yarn test - name: "Copy files over to location" - run: rsync -rvzP . ${{ secrets.STAGE_REPO_PATH }} + run: rsync -rvzP --delete . ${{ secrets.STAGE_REPO_PATH }} deploy: environment: prod From 784eb5e6c5123b91357436d01da4241d51c49942 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 11 Jan 2025 16:25:35 +0000 Subject: [PATCH 08/10] Use branch name as version string --- .forgejo/workflows/stage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml index d8425bd..664cf11 100644 --- a/.forgejo/workflows/stage.yml +++ b/.forgejo/workflows/stage.yml @@ -44,7 +44,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: ${{ vars.STAGE_BOT_VER }}-${{ github.ref_name }} + BOT_VER: ${{ github.ref_name }} BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }} BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }} BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }} From e8de5e9931e46dcbc0a56525488e9b6049b99c9b Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sun, 12 Jan 2025 12:48:59 +0000 Subject: [PATCH 09/10] Update the When To Kick time to match the cron time --- src/timers/AutoKick.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/timers/AutoKick.ts b/src/timers/AutoKick.ts index 61f743a..67ffe55 100644 --- a/src/timers/AutoKick.ts +++ b/src/timers/AutoKick.ts @@ -70,6 +70,11 @@ export default async function AutoKick() { if (now.getMonth() == whenToNotice.getMonth() && now.getDate() == whenToNotice.getDate() && now.getHours() == whenToNotice.getHours()) { + + const nextHour = new Date(whenToNotice); + nextHour.setMinutes(0, 0, 0); + nextHour.setHours(whenToNotice.getHours() + 1); + const embed = new EmbedBuilder() .setTitle("Auto Kick Notice") .setColor(EmbedColours.Warning) @@ -82,7 +87,7 @@ export default async function AutoKick() { }, { name: "When To Kick", - value: ``, + value: ``, inline: true, }, ]); From c22dbcf00537820506c8b759af083b4d60c2f7a7 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sun, 12 Jan 2025 15:17:11 +0000 Subject: [PATCH 10/10] Fix auto kick notice time --- src/timers/AutoKick.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/timers/AutoKick.ts b/src/timers/AutoKick.ts index 67ffe55..fcaac56 100644 --- a/src/timers/AutoKick.ts +++ b/src/timers/AutoKick.ts @@ -11,6 +11,8 @@ export default async function AutoKick() { 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; } @@ -19,6 +21,8 @@ export default async function AutoKick() { const role = guild.roles.cache.find(x => x.id == config.RoleId); if (!role) { + console.error("Role not found in guild"); + continue; } @@ -26,6 +30,8 @@ export default async function AutoKick() { const member = memberEntity[1]; if (!member.kickable) { + console.error("Member not kickable"); + continue; } @@ -39,6 +45,8 @@ export default async function AutoKick() { 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; } @@ -60,10 +68,14 @@ export default async function AutoKick() { } } 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; } @@ -71,9 +83,9 @@ export default async function AutoKick() { && now.getDate() == whenToNotice.getDate() && now.getHours() == whenToNotice.getHours()) { - const nextHour = new Date(whenToNotice); + const nextHour = new Date(whenToKick); nextHour.setMinutes(0, 0, 0); - nextHour.setHours(whenToNotice.getHours() + 1); + nextHour.setHours(whenToKick.getHours() + 1); const embed = new EmbedBuilder() .setTitle("Auto Kick Notice")