Merge pull request #7 from r-Overwatch2/feature/4-add-data-delete-on-guild-remove
Feature/4 add data delete on guild remove
This commit was merged in pull request #7.
	This commit is contained in:
		@@ -18,6 +18,12 @@ Removes the word from the server specific blocklist
 | 
				
			|||||||
### /info
 | 
					### /info
 | 
				
			||||||
Returns general information about the bot and the servers stats
 | 
					Returns general information about the bot and the servers stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### /preservesettings
 | 
				
			||||||
 | 
					Changes the behaviour when the bot leaves the server.
 | 
				
			||||||
 | 
					Options are:
 | 
				
			||||||
 | 
					- Keep settings persistent even if the bot leaves
 | 
				
			||||||
 | 
					- Delete setting when the bot leaves the server [default]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Environment variables
 | 
					## Environment variables
 | 
				
			||||||
| Name             | Description                                                                     | Required | Example             |
 | 
					| Name             | Description                                                                     | Required | Example             |
 | 
				
			||||||
| :--------------- | :------------------------------------------------------------------------------ | :------: | :------------------ |
 | 
					| :--------------- | :------------------------------------------------------------------------------ | :------: | :------------------ |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "eu.astrogd.white-leopard",
 | 
					  "name": "eu.astrogd.white-leopard",
 | 
				
			||||||
  "version": "1.0.0-beta.2",
 | 
					  "version": "1.0.0-alpha.6",
 | 
				
			||||||
  "description": "A Discord bot that checks channel names for blacklisted words and reverts the changes if necessary",
 | 
					  "description": "A Discord bot that checks channel names for blacklisted words and reverts the changes if necessary",
 | 
				
			||||||
  "main": "build/index.js",
 | 
					  "main": "build/index.js",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ export default async function execute(args: string[]) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            console.log(`Guild ${args[1]}:
 | 
					            console.log(`Guild ${args[1]}:
 | 
				
			||||||
    - Premium: ${isPremium ? `ACTIVE for ${moment(settings.isPremiumUntil).fromNow(true)}` : "INACTIVE"}
 | 
					    - Premium: ${isPremium ? `ACTIVE for ${moment(settings.isPremiumUntil).fromNow(true)}` : "INACTIVE"}
 | 
				
			||||||
 | 
					    - Preserve Settings: ${settings.preserveDataOnGuildLeave ? "ENABLED" : "DISABLED"}
 | 
				
			||||||
    - Logchannel: ${settings.notificationChannelID ? `ENABLED (${settings.notificationChannelID})` : "DISABLED"}
 | 
					    - Logchannel: ${settings.notificationChannelID ? `ENABLED (${settings.notificationChannelID})` : "DISABLED"}
 | 
				
			||||||
    - blocked Words: ${wordCount}`);
 | 
					    - blocked Words: ${wordCount}`);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import { runCleanup } from "../service";
 | 
				
			||||||
import client from "./index";
 | 
					import client from "./index";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const token = process.env["TOKEN"];
 | 
					const token = process.env["TOKEN"];
 | 
				
			||||||
@@ -7,4 +8,6 @@ client.login(token);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
client.on("ready", () => {
 | 
					client.on("ready", () => {
 | 
				
			||||||
    console.log(`Connected to Discord API. Bot account is ${client.user?.tag} (${client.user?.id})`);
 | 
					    console.log(`Connected to Discord API. Bot account is ${client.user?.tag} (${client.user?.id})`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runCleanup();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
import * as notification from "./notification";
 | 
					import * as notification from "./notification";
 | 
				
			||||||
import * as blocklist from "./blocklist";
 | 
					import * as blocklist from "./blocklist";
 | 
				
			||||||
import * as info from "./info";
 | 
					import * as info from "./info";
 | 
				
			||||||
 | 
					import * as preserveSettings from "./preserveSettings";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const array = [notification.builder.toJSON(), blocklist.builder.toJSON(), info.builder.toJSON()];
 | 
					const array = [notification.builder.toJSON(), blocklist.builder.toJSON(), info.builder.toJSON(), preserveSettings.builder.toJSON()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
    array
 | 
					    array
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import { ChatInputCommandInteraction, Collection, Events, SlashCommandBuilder }
 | 
				
			|||||||
import * as notification from "./notification";
 | 
					import * as notification from "./notification";
 | 
				
			||||||
import * as blocklist from "./blocklist";
 | 
					import * as blocklist from "./blocklist";
 | 
				
			||||||
import * as info from "./info";
 | 
					import * as info from "./info";
 | 
				
			||||||
 | 
					import * as preserveSettings from "./preserveSettings";
 | 
				
			||||||
import client from "../client";
 | 
					import client from "../client";
 | 
				
			||||||
import getDefaultEmbed from "../tools/defaultEmbeds";
 | 
					import getDefaultEmbed from "../tools/defaultEmbeds";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9,6 +10,7 @@ const commands = new Collection<string, { builder: SlashCommandBuilder, execute:
 | 
				
			|||||||
commands.set(notification.builder.name, notification);
 | 
					commands.set(notification.builder.name, notification);
 | 
				
			||||||
commands.set(blocklist.builder.name, blocklist);
 | 
					commands.set(blocklist.builder.name, blocklist);
 | 
				
			||||||
commands.set(info.builder.name, info);
 | 
					commands.set(info.builder.name, info);
 | 
				
			||||||
 | 
					commands.set(preserveSettings.builder.name, preserveSettings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
client.on(Events.InteractionCreate, async (interaction) => {
 | 
					client.on(Events.InteractionCreate, async (interaction) => {
 | 
				
			||||||
    if (!interaction.isChatInputCommand()) return;
 | 
					    if (!interaction.isChatInputCommand()) return;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								src/commands/preserveSettings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/commands/preserveSettings.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					import { SlashCommandBuilder, ChatInputCommandInteraction, PermissionFlagsBits } from "discord.js";
 | 
				
			||||||
 | 
					import { database, GuildSetting } from "../data";
 | 
				
			||||||
 | 
					import { getGuildSetting } from "../tools/data";
 | 
				
			||||||
 | 
					import getDefaultEmbed, { getSuccessEmbed } from "../tools/defaultEmbeds";
 | 
				
			||||||
 | 
					import { Emoji } from "../tools/design";
 | 
				
			||||||
 | 
					import { getGuildChannel } from "../tools/discord";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const builder = new SlashCommandBuilder();
 | 
				
			||||||
 | 
					builder.setName("preservesettings");
 | 
				
			||||||
 | 
					builder.setDescription("Sets if the bot should save the server settings and blocklist if it leaves the server or delete it");
 | 
				
			||||||
 | 
					builder.addStringOption((option) => {
 | 
				
			||||||
 | 
					    option.addChoices({
 | 
				
			||||||
 | 
					        name: "Keep data when bot leaves the server",
 | 
				
			||||||
 | 
					        value: "keep"
 | 
				
			||||||
 | 
					    }, {
 | 
				
			||||||
 | 
					        name: "Delete data when bot leaves the server",
 | 
				
			||||||
 | 
					        value: "delete"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    option.setName("behaviour");
 | 
				
			||||||
 | 
					    option.setDescription("How the bot behaves when leaving the server");
 | 
				
			||||||
 | 
					    option.setRequired(true);
 | 
				
			||||||
 | 
					    return option;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					builder.setDMPermission(false);
 | 
				
			||||||
 | 
					builder.setDefaultMemberPermissions(PermissionFlagsBits.Administrator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function execute(interaction: ChatInputCommandInteraction): Promise<void> {
 | 
				
			||||||
 | 
					    if (!interaction.inGuild()) throw new Error("Command was executed outside guild context");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const settings = await getGuildSetting(interaction.guildId);
 | 
				
			||||||
 | 
					    const option = interaction.options.getString("behaviour", true).toLowerCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (option !== "keep" && option !== "delete") throw new TypeError(`option "behaviour" expected to be of type "keep" | "delete" but was "${option}"`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    settings.preserveDataOnGuildLeave = option === "keep";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await database.getRepository(GuildSetting).save(settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const embed = getSuccessEmbed();
 | 
				
			||||||
 | 
					    embed.setDescription(`Preserve settings on server leave is now ${settings.preserveDataOnGuildLeave ? "ENABLED" : "DISABLED"}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    interaction.reply({
 | 
				
			||||||
 | 
					        embeds: [embed],
 | 
				
			||||||
 | 
					        ephemeral: true
 | 
				
			||||||
 | 
					    }).catch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!settings.notificationChannelID) return;
 | 
				
			||||||
 | 
					    const logChannel = await getGuildChannel(interaction.guildId, settings.notificationChannelID);
 | 
				
			||||||
 | 
					    if (!logChannel || !logChannel.isTextBased()) return;
 | 
				
			||||||
 | 
					    const logEmbed = getDefaultEmbed();
 | 
				
			||||||
 | 
					    logEmbed.setTitle(`${Emoji.SETTINGS} Settings changed`);
 | 
				
			||||||
 | 
					    logEmbed.setDescription(`Preserve settings on server leave is now ${settings.preserveDataOnGuildLeave ? "ENABLED" : "DISABLED"}`);
 | 
				
			||||||
 | 
					    logEmbed.addFields({
 | 
				
			||||||
 | 
					        name: "This action was performed by",
 | 
				
			||||||
 | 
					        value: `${interaction.user.tag} (${interaction.user.id})`
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logChannel.send({
 | 
				
			||||||
 | 
					        embeds: [logEmbed]
 | 
				
			||||||
 | 
					    }).catch();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    builder,
 | 
				
			||||||
 | 
					    execute
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/data/migrations/1669392941776-data.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/data/migrations/1669392941776-data.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { MigrationInterface, QueryRunner } from "typeorm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class data1669392941776 implements MigrationInterface {
 | 
				
			||||||
 | 
					    name = 'data1669392941776'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async up(queryRunner: QueryRunner): Promise<void> {
 | 
				
			||||||
 | 
					        await queryRunner.query(`
 | 
				
			||||||
 | 
					            ALTER TABLE "guild_setting"
 | 
				
			||||||
 | 
					            ADD "preserveDataOnGuildLeave" boolean NOT NULL DEFAULT false
 | 
				
			||||||
 | 
					        `);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async down(queryRunner: QueryRunner): Promise<void> {
 | 
				
			||||||
 | 
					        await queryRunner.query(`
 | 
				
			||||||
 | 
					            ALTER TABLE "guild_setting" DROP COLUMN "preserveDataOnGuildLeave"
 | 
				
			||||||
 | 
					        `);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,4 +10,7 @@ export class GuildSetting {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Column("timestamp", { nullable: true, default: null })
 | 
					    @Column("timestamp", { nullable: true, default: null })
 | 
				
			||||||
    isPremiumUntil!: Date | null;
 | 
					    isPremiumUntil!: Date | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Column("boolean", { default: false })
 | 
				
			||||||
 | 
					    preserveDataOnGuildLeave!: boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/events/guildDelete.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/events/guildDelete.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import client from "../client";
 | 
				
			||||||
 | 
					import { Events } from "discord.js";
 | 
				
			||||||
 | 
					import { getGuildSetting } from "../tools/data";
 | 
				
			||||||
 | 
					import { Badword, database, GuildSetting } from "../data";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client.on(Events.GuildDelete, async (guild) => {
 | 
				
			||||||
 | 
					    const settings = await getGuildSetting(guild.id);
 | 
				
			||||||
 | 
					    if (settings.preserveDataOnGuildLeave) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await database.getRepository(GuildSetting).delete({
 | 
				
			||||||
 | 
					        id: guild.id
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await database.getRepository(Badword).delete({
 | 
				
			||||||
 | 
					        guildID: guild.id
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -1 +1,2 @@
 | 
				
			|||||||
import "./channelUpdate";
 | 
					import "./channelUpdate";
 | 
				
			||||||
 | 
					import "./guildDelete";
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/service/cleanup.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/service/cleanup.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					import { Badword, database, GuildSetting } from "../data";
 | 
				
			||||||
 | 
					import client from "../client";
 | 
				
			||||||
 | 
					import { DiscordAPIError } from "discord.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default async function execute(): Promise<void> {
 | 
				
			||||||
 | 
					    const guilds = await database.getRepository(GuildSetting).find({
 | 
				
			||||||
 | 
					        where: {
 | 
				
			||||||
 | 
					            preserveDataOnGuildLeave: false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const guild of guilds) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await client.guilds.fetch(guild.id);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (!(error instanceof DiscordAPIError)) {
 | 
				
			||||||
 | 
					                console.error(`service.cleanup failed: ${error}`);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const id = guild.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 5001 = Missing access
 | 
				
			||||||
 | 
					            if (error.code.toString() !== "50001") {
 | 
				
			||||||
 | 
					                console.warn(`Guild ${guild.id} is unavailable but not because of error 5001:\n${error}`);
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await database.getRepository(Badword).delete({
 | 
				
			||||||
 | 
					                guildID: guild.id
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await database.getRepository(GuildSetting).remove(guild);
 | 
				
			||||||
 | 
					            console.log(`Removed data for guild ${id}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log("Cleanup completed");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1 +1,6 @@
 | 
				
			|||||||
import "./uptime";
 | 
					import "./uptime";
 | 
				
			||||||
 | 
					import runCleanup from "./cleanup";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					    runCleanup
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -12,6 +12,7 @@ export async function getGuildSetting(guildID: string): Promise<GuildSetting> {
 | 
				
			|||||||
        guildSetting.id = guildID;
 | 
					        guildSetting.id = guildID;
 | 
				
			||||||
        guildSetting.isPremiumUntil = null;
 | 
					        guildSetting.isPremiumUntil = null;
 | 
				
			||||||
        guildSetting.notificationChannelID = null;
 | 
					        guildSetting.notificationChannelID = null;
 | 
				
			||||||
 | 
					        guildSetting.preserveDataOnGuildLeave = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return guildSetting;
 | 
					    return guildSetting;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user