From 88c6740adccdfcbaccf7edf54c4753e44bd4c263 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Thu, 30 Jun 2022 12:44:01 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=9A=B8=20gdpr=20fix=20for=20removing?= =?UTF-8?q?=20guild=20when=20bot=20has=20bent=20offline=20during=20removal?= =?UTF-8?q?=20of=20bot.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/helpers/dropGuild/index.ts | 46 ++++++++----------- src/helpers/fetchGuild/index.ts | 12 ++--- src/jobs/keepDataUpToDate/index.ts | 38 +++++++++++++++ .../commands/credits/modules/work/index.ts | 2 +- src/plugins/events/guildCreate/index.ts | 11 +++-- src/plugins/events/guildDelete/index.ts | 2 +- .../messageCreate/modules/credits/index.ts | 2 +- .../messageCreate/modules/points/index.ts | 2 +- 9 files changed, 76 insertions(+), 40 deletions(-) create mode 100644 src/jobs/keepDataUpToDate/index.ts diff --git a/package.json b/package.json index ac149cc..740f844 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "tsconfig-paths": "^4.0.0", "typescript": "^4.6.3", "uuid": "^8.3.2", + "winston": "^3.8.0", "winston-daily-rotate-file": "^4.6.1" }, "devDependencies": { diff --git a/src/helpers/dropGuild/index.ts b/src/helpers/dropGuild/index.ts index 0c0e2ab..6145aa4 100644 --- a/src/helpers/dropGuild/index.ts +++ b/src/helpers/dropGuild/index.ts @@ -7,66 +7,60 @@ import timeoutSchema from "../../models/timeout"; import logger from "../../logger"; -import { Guild } from "discord.js"; +import { Snowflake } from "discord.js"; -export default async (guild: Guild) => { +export default async (id: Snowflake) => { await guildSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - return logger?.silly(`Deleted guild: ${guild.id}`); + return logger?.silly(`Deleted guild: ${id}`); }) .catch(async (error) => { - logger?.error(`Error deleting guild: ${guild.id} - ${error}`); + logger?.error(`Error deleting guild: ${id} - ${error}`); }); await userSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - logger?.silly(`Deleted users for guild: ${guild.id} from database`); + logger?.silly(`Deleted users for guild: ${id} from database`); }) .catch(async (error) => { - logger?.error(`Error deleting users for guild: ${guild.id} - ${error}`); + logger?.error(`Error deleting users for guild: ${id} - ${error}`); }); await apiSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - logger?.silly(`Deleted apis for guild: ${guild.id} from database`); + logger?.silly(`Deleted apis for guild: ${id} from database`); }) .catch(async (error) => { - logger?.error(`Error deleting apis for guild: ${guild.id} - ${error}`); + logger?.error(`Error deleting apis for guild: ${id} - ${error}`); }); await counterSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - logger?.silly(`Deleted counters for guild: ${guild.id} from database`); + logger?.silly(`Deleted counters for guild: ${id} from database`); }) .catch(async (error) => { - logger?.error( - `Error deleting counters for guild: ${guild.id} - ${error}` - ); + logger?.error(`Error deleting counters for guild: ${id} - ${error}`); }); await shopRoleSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - logger?.silly(`Deleted shop roles for guild: ${guild.id} from database`); + logger?.silly(`Deleted shop roles for guild: ${id} from database`); }) .catch(async (error) => { - logger?.error( - `Error deleting shop roles for guild: ${guild.id} - ${error}` - ); + logger?.error(`Error deleting shop roles for guild: ${id} - ${error}`); }); await timeoutSchema - .deleteMany({ guildId: guild.id }) + .deleteMany({ guildId: id }) .then(async () => { - logger?.silly(`Deleted timeouts for guild: ${guild.id} from database`); + logger?.silly(`Deleted timeouts for guild: ${id} from database`); }) .catch(async (error) => { - logger?.error( - `Error deleting timeouts for guild: ${guild.id} - ${error}` - ); + logger?.error(`Error deleting timeouts for guild: ${id} - ${error}`); }); }; diff --git a/src/helpers/fetchGuild/index.ts b/src/helpers/fetchGuild/index.ts index a89e840..264687e 100644 --- a/src/helpers/fetchGuild/index.ts +++ b/src/helpers/fetchGuild/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { Guild } from "discord.js"; +import { Snowflake } from "discord.js"; // Models import guildSchema from "../../models/guild"; @@ -8,18 +8,18 @@ import guildSchema from "../../models/guild"; import logger from "../../logger"; // Function -export default async (guild: Guild) => { - const guildObj = await guildSchema?.findOne({ guildId: guild.id }); +export default async (id: Snowflake) => { + const guildObj = await guildSchema?.findOne({ guildId: id }); if (guildObj === null) { - const newGuildObj = new guildSchema({ guildId: guild.id }); + const newGuildObj = new guildSchema({ guildId: id }); await newGuildObj .save() .then(async () => { - logger?.silly(`Created guild: ${guild.id}`); + logger?.silly(`Created guild: ${id}`); }) .catch(async (error) => { - logger?.error(`Error creating guild: ${guild.id} - ${error}`); + logger?.error(`Error creating guild: ${id} - ${error}`); }); return newGuildObj; diff --git a/src/jobs/keepDataUpToDate/index.ts b/src/jobs/keepDataUpToDate/index.ts new file mode 100644 index 0000000..9c7f307 --- /dev/null +++ b/src/jobs/keepDataUpToDate/index.ts @@ -0,0 +1,38 @@ +import { Client, Guild } from "discord.js"; +import logger from "../../logger"; +import dropGuild from "../../helpers/dropGuild"; +import fetchGuild from "../../helpers/fetchGuild"; + +import guildSchema from "../../models/guild"; + +export const options = { + schedule: "*/5 * * * * *", // https://crontab.guru/ +}; + +export const execute = async (client: Client) => { + const guildsDB = await guildSchema.find(); + const guildsDiscord = client.guilds.cache; + + const shouldNotExist = guildsDB + .filter((x) => !guildsDiscord.some((y) => y.id === x.guildId)) + .map((z) => z.guildId); + + const shouldExist = guildsDiscord + .filter((x) => !guildsDB.some((y) => y.guildId === x.id)) + .map((z) => z.id); + + logger.silly(shouldNotExist); + logger.silly(shouldExist); + + if (shouldNotExist) { + shouldNotExist.map(async (x) => { + await dropGuild(x); + }); + } + + if (shouldExist) { + shouldExist.map(async (x) => { + await fetchGuild(x); + }); + } +}; diff --git a/src/plugins/commands/credits/modules/work/index.ts b/src/plugins/commands/credits/modules/work/index.ts index ef0a550..b61d9fe 100644 --- a/src/plugins/commands/credits/modules/work/index.ts +++ b/src/plugins/commands/credits/modules/work/index.ts @@ -43,7 +43,7 @@ export default { return logger?.silly(`Guild is null`); } - const guildDB = await fetchGuild(guild); + const guildDB = await fetchGuild(guild.id); await cooldown.command(interaction, guildDB?.credits?.workTimeout); diff --git a/src/plugins/events/guildCreate/index.ts b/src/plugins/events/guildCreate/index.ts index 60d5a3c..0d775de 100644 --- a/src/plugins/events/guildCreate/index.ts +++ b/src/plugins/events/guildCreate/index.ts @@ -11,10 +11,13 @@ export const options: IEventOptions = { export const execute = async (guild: Guild) => { const { client } = guild; - logger?.silly(`Added to guild: ${guild.name} (${guild.id})`); + if (!client.user) + throw new Error("Discord API client user is not available."); - await fetchGuild(guild); + logger.silly( + `${client.user.username} joined guild: ${guild.name} (${guild.id})` + ); + + await fetchGuild(guild.id); await updatePresence(client); - - logger.silly(`guildCreate: ${guild}`); }; diff --git a/src/plugins/events/guildDelete/index.ts b/src/plugins/events/guildDelete/index.ts index 146351f..e33d341 100644 --- a/src/plugins/events/guildDelete/index.ts +++ b/src/plugins/events/guildDelete/index.ts @@ -16,7 +16,7 @@ export const execute = async (guild: Guild) => { logger?.silly(`Deleted from guild: ${guild.name} (${guild.id})`); - await dropGuild(guild); + await dropGuild(guild.id); await updatePresence(client); logger.silly(`guildDelete: ${guild}`); diff --git a/src/plugins/events/messageCreate/modules/credits/index.ts b/src/plugins/events/messageCreate/modules/credits/index.ts index 24a5ce2..8f4a54e 100644 --- a/src/plugins/events/messageCreate/modules/credits/index.ts +++ b/src/plugins/events/messageCreate/modules/credits/index.ts @@ -17,7 +17,7 @@ export default { const { id: guildId } = guild; const { id: userId } = author; - const guildData = await fetchGuild(guild); + const guildData = await fetchGuild(guild.id); const userData = await fetchUser(author, guild); if (content.length < guildData.credits.minimumLength) return; diff --git a/src/plugins/events/messageCreate/modules/points/index.ts b/src/plugins/events/messageCreate/modules/points/index.ts index ed18003..b73dbf2 100644 --- a/src/plugins/events/messageCreate/modules/points/index.ts +++ b/src/plugins/events/messageCreate/modules/points/index.ts @@ -14,7 +14,7 @@ export default { if (author.bot) return; if (channel?.type !== "GUILD_TEXT") return; - const guildData = await fetchGuild(guild); + const guildData = await fetchGuild(guild.id); const userData = await fetchUser(author, guild); if (content.length < guildData.credits.minimumLength) return; From 21365d5ecf533e9e416744bda9096e8154b58a55 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Thu, 30 Jun 2022 12:47:27 +0200 Subject: [PATCH 2/8] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Change=20keepDataUpToD?= =?UTF-8?q?ate=20to=2000:00=20on=201=20of=20month?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jobs/keepDataUpToDate/index.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/jobs/keepDataUpToDate/index.ts b/src/jobs/keepDataUpToDate/index.ts index 9c7f307..4e7262a 100644 --- a/src/jobs/keepDataUpToDate/index.ts +++ b/src/jobs/keepDataUpToDate/index.ts @@ -6,7 +6,7 @@ import fetchGuild from "../../helpers/fetchGuild"; import guildSchema from "../../models/guild"; export const options = { - schedule: "*/5 * * * * *", // https://crontab.guru/ + schedule: "0 0 1 * *", // https://crontab.guru/ }; export const execute = async (client: Client) => { @@ -21,17 +21,14 @@ export const execute = async (client: Client) => { .filter((x) => !guildsDB.some((y) => y.guildId === x.id)) .map((z) => z.id); - logger.silly(shouldNotExist); - logger.silly(shouldExist); - if (shouldNotExist) { - shouldNotExist.map(async (x) => { + shouldNotExist.forEach(async (x) => { await dropGuild(x); }); } if (shouldExist) { - shouldExist.map(async (x) => { + shouldExist.forEach(async (x) => { await fetchGuild(x); }); } From ef8b20984d97b72c9e37d9c14f6f95cc9f182bb8 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:36:16 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=A9=B9=20forgot=20to=20change=20from?= =?UTF-8?q?=20millisecs=20default=20to=20secs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/guild.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/guild.ts b/src/models/guild.ts index 0b7a78d..44b6958 100644 --- a/src/models/guild.ts +++ b/src/models/guild.ts @@ -58,7 +58,7 @@ const guildSchema = new Schema( }, timeout: { type: Number, - default: 5000, + default: 5, }, workRate: { type: Number, @@ -66,7 +66,7 @@ const guildSchema = new Schema( }, workTimeout: { type: Number, - default: 900000, + default: 900, }, }, embeds: { @@ -118,7 +118,7 @@ const guildSchema = new Schema( }, timeout: { type: Number, - default: 5000, + default: 5, }, }, welcome: { From 68bc3f6843365c6dfd8c38f9279eb9307858bd69 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:38:49 +0200 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=8E=A8=20remove=20unnecessary=20error?= =?UTF-8?q?=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/helpers/listDir/index.ts | 4 +--- src/managers/database/index.ts | 25 ++----------------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/src/helpers/listDir/index.ts b/src/helpers/listDir/index.ts index 382e41d..d70afc5 100644 --- a/src/helpers/listDir/index.ts +++ b/src/helpers/listDir/index.ts @@ -2,7 +2,5 @@ import fs from "fs"; const fsPromises = fs.promises; export default async (path: string) => { - return fsPromises.readdir(path).catch(async (err) => { - throw new Error(`Could not list directory: ${path}`, err); - }); + return fsPromises.readdir(path); }; diff --git a/src/managers/database/index.ts b/src/managers/database/index.ts index 0063c61..c58af68 100644 --- a/src/managers/database/index.ts +++ b/src/managers/database/index.ts @@ -1,27 +1,6 @@ -// 3rd party dependencies import mongoose from "mongoose"; - -// Dependencies -import logger from "../../logger"; - -// Configuration import { url } from "../../config/database"; -export const start = async () => { - await mongoose - .connect(url) - .then(async (connection) => { - logger.info(`Connected to database: ${connection.connection.name}`); - }) - .catch(async (e) => { - logger.error("Could not connect to database", e); - }); - - mongoose.connection.on("error", async (error) => { - logger.error(`${error}`); - }); - - mongoose.connection.on("warn", async (warning) => { - logger.warn(warning); - }); +export const connect = async () => { + await mongoose.connect(url); }; From fcd2d18480b9519259a1070615f5206916b46bfe Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:39:25 +0200 Subject: [PATCH 5/8] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20better=20command=20man?= =?UTF-8?q?ager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/managers/command/index.ts | 76 +++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/managers/command/index.ts b/src/managers/command/index.ts index c3c08f9..508aea6 100644 --- a/src/managers/command/index.ts +++ b/src/managers/command/index.ts @@ -5,31 +5,65 @@ import logger from "../../logger"; import { ICommand } from "../../interfaces/Command"; export const register = async (client: Client) => { - client.commands = new Collection(); - + // Get name of directories containing commands const commandNames = await listDir("plugins/commands"); + if (!commandNames) throw new Error("📦 No commands available"); - if (!commandNames) throw new Error("Could not list commands"); + const amountOfCommands = commandNames.length; + let importedCommandAmount = 0; + logger.info(`📦 Trying to load ${amountOfCommands} commands`); - logger.info(`Loading ${commandNames.length} commands`); + const importCommand = async (commandName: string) => { + // Import command from plugins/commands + const command: ICommand = await import( + `../../plugins/commands/${commandName}` + ); + if (!command) + throw new Error(`📦 No command found while importing "${commandName}"`); + if (!command.builder) + throw new Error( + `📦 No command builder found while importing "${commandName}"` + ); + if (!command.execute) + throw new Error( + `📦 No command execute found while importing "${commandName}"` + ); + if (!command.moduleData) + throw new Error( + `📦 No command moduleData found while importing "${commandName}"` + ); - await Promise.all( - commandNames.map(async (commandName) => { - const command: ICommand = await import( - `../../plugins/commands/${commandName}` - ).catch(async (e) => { - throw new Error(`Could not load command: ${commandName}`, e); + // Add command to collection + client.commands.set(command.builder.name, command); + importedCommandAmount += 1; + }; + + // Send log message when it's done loading commands + const doneImporting = async () => { + if (importedCommandAmount !== amountOfCommands) { + return logger.warn( + `📦 Failed importing ${ + amountOfCommands - importedCommandAmount + } of ${amountOfCommands} commands` + ); + } + + return logger.info(`📦 Managed to load all commands`); + }; + + // Start importing commands + commandNames.forEach(async (commandName: string, index: number) => { + await importCommand(commandName) + .then(async () => { + logger.debug(`📦 Imported the "${commandName}" command`); + }) + .catch(async (err) => { + logger.error(err); }); - client.commands.set(command.builder.name, command); - - logger.verbose(`${command.builder.name} loaded`); - }) - ) - .then(async () => { - logger.info(`Finished loading commands.`); - }) - .catch(async (err) => { - throw new Error(`Could not load commands: ${err}`); - }); + // If done importing + if (index + 1 === amountOfCommands) { + await doneImporting(); + } + }); }; From 5ae6c05aef91c1d214f6ec2be6ef1d52a8b21284 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:39:56 +0200 Subject: [PATCH 6/8] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20better=20event=20manag?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/managers/event/index.ts | 76 ++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/src/managers/event/index.ts b/src/managers/event/index.ts index 348e138..41f5484 100644 --- a/src/managers/event/index.ts +++ b/src/managers/event/index.ts @@ -6,15 +6,35 @@ import logger from "../../logger"; export const register = async (client: Client) => { const eventNames = await listDir("plugins/events"); - if (!eventNames) return; + if (!eventNames) throw new Error("📦 No events available"); - for await (const eventName of eventNames) { + const amountOfEvents = eventNames.length; + let importedEventAmount = 0; + logger.info(`📦 Trying to load ${amountOfEvents} events`); + + const importEvent = async (eventName: string) => { + // Import event from plugins/events const event: IEvent = await import(`../../plugins/events/${eventName}`); - const eventExecutor = async (...args: Promise[]) => - event.execute(...args).catch(async (err) => { + if (!event) + throw new Error(`📦 No event found while importing "${eventName}"`); + if (!event.options) + throw new Error( + `📦 No event options found while importing "${eventName}"` + ); + if (!event.execute) + throw new Error( + `📦 No event execute found while importing "${eventName}"` + ); + + // Register event + const eventExecutor = async (...args: Promise[]) => { + await event.execute(...args).catch(async (err) => { logger.error(`${err}`); }); - if (!event.options?.type) return; + }; + + if (!event.options?.type) + throw new Error(`📦 No event type found while importing "${eventName}"`); switch (event.options.type) { case "once": @@ -25,5 +45,49 @@ export const register = async (client: Client) => { client.on(eventName, eventExecutor); break; } - } + importedEventAmount += 1; + }; + + // Send log message when it's done loading events + const doneImporting = async () => { + if (importedEventAmount !== amountOfEvents) { + return logger.warn( + `📦 Failed importing ${ + amountOfEvents - importedEventAmount + } of ${amountOfEvents} events` + ); + } + + return logger.info(`📦 Managed to load all events`); + }; + + eventNames.forEach(async (eventName: string, index: number) => { + await importEvent(eventName).then(async () => { + logger.debug(`📦 Imported the "${eventName}" event`); + }); + + // If done importing + if (index + 1 === amountOfEvents) { + await doneImporting(); + } + }); + + // for await (const eventName of eventNames) { + // const event: IEvent = await import(`../../plugins/events/${eventName}`); + // const eventExecutor = async (...args: Promise[]) => + // event.execute(...args).catch(async (err) => { + // logger.error(`${err}`); + // }); + // if (!event.options?.type) return; + + // switch (event.options.type) { + // case "once": + // client.once(eventName, eventExecutor); + // break; + + // case "on": + // client.on(eventName, eventExecutor); + // break; + // } + // } }; From 77daf24f6d68c6cc714c67b32ccda340bf2cbea7 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:40:50 +0200 Subject: [PATCH 7/8] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20better=20schedule=20ma?= =?UTF-8?q?nager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/managers/index.ts | 2 +- src/managers/schedule/index.ts | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/managers/index.ts b/src/managers/index.ts index 6981d3c..a1f3dad 100644 --- a/src/managers/index.ts +++ b/src/managers/index.ts @@ -6,7 +6,7 @@ import * as event from "./event"; import * as command from "./command"; export const start = async (client: Client) => { - await database.start(); + await database.connect(); await schedule.start(client); await command.register(client); await event.register(client); diff --git a/src/managers/schedule/index.ts b/src/managers/schedule/index.ts index 9fc8aa9..753d099 100644 --- a/src/managers/schedule/index.ts +++ b/src/managers/schedule/index.ts @@ -8,22 +8,19 @@ import listDir from "../../helpers/listDir"; import schedule from "node-schedule"; export const start = async (client: Client) => { - logger.info("Starting schedule manager..."); + logger.info("⏰ Started job management"); const jobNames = await listDir("jobs"); - if (!jobNames) return logger.info("No jobs found"); + if (!jobNames) return logger.warn("No available jobs found"); await Promise.all( jobNames.map(async (jobName) => { const job: IJob = await import(`../../jobs/${jobName}`); schedule.scheduleJob(job.options.schedule, async () => { - logger.info(`Executed job ${jobName}!`); + logger.verbose(`⏰ Performed the job "${jobName}"`); await job.execute(client); }); }) - ).then(async () => { - const list = schedule.scheduledJobs; - logger.silly(list); - }); + ); }; From 1790ed09c359d2262967892dc1b2d726e3a9f630 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Tue, 5 Jul 2022 01:41:30 +0200 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=8E=A8=20move=20command=20collection?= =?UTF-8?q?=20to=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 4327353..c3661d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { token, intents } from "./config/discord"; -import { Client } from "discord.js"; // discord.js +import { Client, Collection } from "discord.js"; // discord.js import * as managers from "./managers"; @@ -11,6 +11,9 @@ const main = async () => { intents, }); + // Create command collection + client.commands = new Collection(); + await managers.start(client); // Authorize with Discord's API