From 38133ffb6fe7d5d1606ecd74b09e0dbc2113494b Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 04:37:24 +0100 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=8C=90=20feat(locales):=20add=20Eng?= =?UTF-8?q?lish=20localization=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced various error messages and notifications - Enhanced user feedback for permissions and bot status --- src/locales/en.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/locales/en.yml diff --git a/src/locales/en.yml b/src/locales/en.yml new file mode 100644 index 0000000..843c092 --- /dev/null +++ b/src/locales/en.yml @@ -0,0 +1,9 @@ +PERMISSIONS_ERROR: " **Unable to verify permissions.**" +MENTION_EVERYONE_PERMISSION_MISSING: " **You need the Mention Everyone permission to use this command.**" +GUILD_NOT_FOUND_ERROR: " **Guild not found.**" +BOT_TOKEN_NOT_FOUND_ERROR: " **Bot token not found in .env**" +NO_MEMBERS_ERROR: " **No members found.**" +NO_MEMBERS_MATCH_FILTER_ERROR: " **No members match the filter (all results were bots or yourself).**" +USER_ID_NOT_FOUND_ERROR: " **User ID not found.**" +YOU_HAVE_BEEN_CHOSEN: "** {{USER_ID}}, you have been chosen!**" +PING: "** Pong!** \n-# Latency: {{LATENCY}}ms" From f476cf02040c1ffaf8a648d8c0aa6fd5e9011215 Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:00:44 +0100 Subject: [PATCH 02/10] =?UTF-8?q?=E2=9E=95=20feat:=20add=20ky=20and=20yaml?= =?UTF-8?q?=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adds `ky` for making HTTP requests with improved syntax. - Adds `yaml` for parsing YAML configuration files. - Refactors member fetching to use `ky` for improved readability and conciseness. --- package-lock.json | 31 +++++++++++++++++++++++++++++- package.json | 4 +++- src/index.ts | 6 +++++- src/utils/getLanguageFromServer.ts | 21 ++++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/utils/getLanguageFromServer.ts diff --git a/package-lock.json b/package-lock.json index 25c863e..5e1c78b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "discord-hono": "^0.20.1", "eslint": "^9.39.2", "jiti": "^2.6.1", + "ky": "^1.14.2", "prettier": "^3.7.4", - "typescript-eslint": "^8.51.0" + "typescript-eslint": "^8.51.0", + "yaml": "^2.8.2" }, "devDependencies": { "@cloudflare/vitest-pool-workers": "^0.11.1", @@ -3337,6 +3339,18 @@ "node": ">=6" } }, + "node_modules/ky": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.2.tgz", + "integrity": "sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4815,6 +4829,21 @@ } } }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f8e4792..50dca63 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "discord-hono": "^0.20.1", "eslint": "^9.39.2", "jiti": "^2.6.1", + "ky": "^1.14.2", "prettier": "^3.7.4", - "typescript-eslint": "^8.51.0" + "typescript-eslint": "^8.51.0", + "yaml": "^2.8.2" } } diff --git a/src/index.ts b/src/index.ts index 8524be8..4bc43f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,7 @@ import { DiscordHono } from 'discord-hono'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { parse } from 'yaml'; +import ky from 'ky'; const app = new DiscordHono() .command('help', async (c) => { @@ -31,7 +34,8 @@ To use a command, type \`/\` followed by the command name. For example, to ping if (!token) return c.res(' **Bot token not found in env.**'); // fetch members, limit to 1000 due to discord api limitation - const resp = await fetch(`https://discord.com/api/v10/guilds/${guildId}/members?limit=1000`, { + const resp = await ky.get(`https://discord.com/api/v10/guilds/${guildId}/members`, { + searchParams: { limit: '1000' }, headers: { Authorization: `Bot ${token}` }, }); if (!resp.ok) return c.res(`Failed to fetch members: ${resp.status} ${resp.statusText}`); diff --git a/src/utils/getLanguageFromServer.ts b/src/utils/getLanguageFromServer.ts new file mode 100644 index 0000000..75ea03a --- /dev/null +++ b/src/utils/getLanguageFromServer.ts @@ -0,0 +1,21 @@ +import type { CommandContext } from 'discord-hono'; +import ky from 'ky'; + +export default async function getLanguageFromServer(serverId: string, c: CommandContext): Promise { + // use ky as a workaround for custom endpoints not supported by client.rest + const guid = await ky.get(`https://discord.com/api/v9/guilds/templates/${serverId}`, { + headers: { + Authorization: `Bot ${c.env.DISCORD_TOKEN || process.env.DISCORD_TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + + if (!guid.ok) { + // get serialized_source_guild.preferred_locale from response + const data = (await guid.json()) as { serialized_source_guild?: { preferred_locale?: string } }; + return data?.serialized_source_guild?.preferred_locale || 'en'; + } + + const data = (await guid.json()) as { language?: string }; + return data.language || 'en'; +} From 136d0652903c1bc2d20159fde949b5f6f674f86a Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:03:40 +0100 Subject: [PATCH 03/10] =?UTF-8?q?=E2=9C=A8=20feat(locales):=20add=20getFil?= =?UTF-8?q?eFromLanguage=20utility=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduces a utility function to retrieve localization files based on the specified language. - Supports French and defaults to English localization. --- src/utils/getFileFromLanguage.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/utils/getFileFromLanguage.ts diff --git a/src/utils/getFileFromLanguage.ts b/src/utils/getFileFromLanguage.ts new file mode 100644 index 0000000..30a0a1d --- /dev/null +++ b/src/utils/getFileFromLanguage.ts @@ -0,0 +1,17 @@ +import yaml from 'yaml'; +import fs from 'fs'; +import path from 'path'; + +export default function getFileFromLanguage(language: string): unknown { + let fileName: string; + switch (language) { + case 'fr': + fileName = 'fr.yaml'; + break; + default: + fileName = 'en.yaml'; + } + const filePath = path.resolve(__dirname, fileName); + const fileContents = fs.readFileSync(filePath, 'utf8'); + return yaml.parse(fileContents); +} From 4aa5af4d40f4c3c7c775fdcfe4df5048e2b96c42 Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:07:11 +0100 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=8C=90=20fix(i18n):=20normalizes=20?= =?UTF-8?q?quotes=20in=20locale=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaces double quotes with single quotes in the English locale file for consistency. - Corrects the file path for locale files and updates the type definition. --- src/locales/en.yml | 16 ++++++++-------- src/types/fileLanguage.ts | 11 +++++++++++ src/utils/getFileFromLanguage.ts | 9 +++++---- 3 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 src/types/fileLanguage.ts diff --git a/src/locales/en.yml b/src/locales/en.yml index 843c092..f25433d 100644 --- a/src/locales/en.yml +++ b/src/locales/en.yml @@ -1,9 +1,9 @@ -PERMISSIONS_ERROR: " **Unable to verify permissions.**" -MENTION_EVERYONE_PERMISSION_MISSING: " **You need the Mention Everyone permission to use this command.**" -GUILD_NOT_FOUND_ERROR: " **Guild not found.**" -BOT_TOKEN_NOT_FOUND_ERROR: " **Bot token not found in .env**" -NO_MEMBERS_ERROR: " **No members found.**" -NO_MEMBERS_MATCH_FILTER_ERROR: " **No members match the filter (all results were bots or yourself).**" -USER_ID_NOT_FOUND_ERROR: " **User ID not found.**" -YOU_HAVE_BEEN_CHOSEN: "** {{USER_ID}}, you have been chosen!**" +PERMISSIONS_ERROR: ' **Unable to verify permissions.**' +MENTION_EVERYONE_PERMISSION_MISSING: ' **You need the Mention Everyone permission to use this command.**' +GUILD_NOT_FOUND_ERROR: ' **Guild not found.**' +BOT_TOKEN_NOT_FOUND_ERROR: ' **Bot token not found in .env**' +NO_MEMBERS_ERROR: ' **No members found.**' +NO_MEMBERS_MATCH_FILTER_ERROR: ' **No members match the filter (all results were bots or yourself).**' +USER_ID_NOT_FOUND_ERROR: ' **User ID not found.**' +YOU_HAVE_BEEN_CHOSEN: '** {{USER_ID}}, you have been chosen!**' PING: "** Pong!** \n-# Latency: {{LATENCY}}ms" diff --git a/src/types/fileLanguage.ts b/src/types/fileLanguage.ts new file mode 100644 index 0000000..d0feebd --- /dev/null +++ b/src/types/fileLanguage.ts @@ -0,0 +1,11 @@ +export interface FileLanguage { + PERMISSIONS_ERROR: string; + MENTION_EVERYONE_PERMISSION_MISSING: string; + GUILD_NOT_FOUND_ERROR: string; + BOT_TOKEN_NOT_FOUND_ERROR: string; + NO_MEMBERS_ERROR: string; + NO_MEMBERS_MATCH_FILTER_ERROR: string; + USER_ID_NOT_FOUND_ERROR: string; + YOU_HAVE_BEEN_CHOSEN: string; + PING: string; +} diff --git a/src/utils/getFileFromLanguage.ts b/src/utils/getFileFromLanguage.ts index 30a0a1d..a59663e 100644 --- a/src/utils/getFileFromLanguage.ts +++ b/src/utils/getFileFromLanguage.ts @@ -1,17 +1,18 @@ import yaml from 'yaml'; import fs from 'fs'; import path from 'path'; +import type { FileLanguage } from '../types/fileLanguage'; export default function getFileFromLanguage(language: string): unknown { let fileName: string; switch (language) { case 'fr': - fileName = 'fr.yaml'; + fileName = 'fr.yml'; break; default: - fileName = 'en.yaml'; + fileName = 'en.yml'; } - const filePath = path.resolve(__dirname, fileName); + const filePath = path.resolve(__dirname, 'locales', fileName); const fileContents = fs.readFileSync(filePath, 'utf8'); - return yaml.parse(fileContents); + return yaml.parse(fileContents) as FileLanguage; } From e74c8f2ee9d4d10bb4bc9dc746726f6db83d6851 Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:21:46 +0100 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=8C=90=20feat:=20i18n=20for=20bot?= =?UTF-8?q?=20responses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implements internationalization (i18n) for bot responses. - Uses YAML files to store translations. - Retrieves server language and uses it to display messages. - Adds `en.yml` with default messages. --- src/index.ts | 47 ++++++++++++++++++++++++--------------- src/locales/en.yml | 9 ++++++++ src/locales/fr.yml | 18 +++++++++++++++ src/types/fileLanguage.ts | 2 ++ 4 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 src/locales/fr.yml diff --git a/src/index.ts b/src/index.ts index 4bc43f9..08f617e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,36 +2,43 @@ import { DiscordHono } from 'discord-hono'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { parse } from 'yaml'; import ky from 'ky'; +import getLanguageFromServer from './utils/getLanguageFromServer'; +import getFileFromLanguage from './utils/getFileFromLanguage'; +import type { FileLanguage } from './types/fileLanguage'; const app = new DiscordHono() .command('help', async (c) => { - const helpMessage = `**Available Commands:** -- \`/someone [ignore-bots]\`: Ping a random member from the server. Optionally ignore bot users. -- \`/ping\`: Replies with the current ping. -- \`/help\`: Provides help information for available commands. - -To use a command, type \`/\` followed by the command name. For example, to ping a random member, type \`/someone\`. You can add the optional parameter \`ignore-bots\` to exclude bot users from being selected. --# Source code is available on [GitHub](https://github.com/notthebestdev/someoneback).`; + const guildId = c.interaction.guild?.id as string; + let lang: FileLanguage = getFileFromLanguage('en') as FileLanguage; + await getLanguageFromServer(guildId, c).then((language) => { + lang = getFileFromLanguage(language) as FileLanguage; + }); + const helpMessage = lang.HELP_MESSAGE; return c.res(helpMessage); }) .command('someone', async (c) => { // check if user has permission to mention everyone + const guildId = c.interaction.guild?.id; const memberPermissions = c.interaction.member?.permissions; - if (!memberPermissions) return c.res(' **Unable to verify permissions.**'); + if (!guildId) return c.res(' **Guild not found.**'); + let lang: FileLanguage = getFileFromLanguage('en') as FileLanguage; + await getLanguageFromServer(guildId, c).then((language) => { + lang = getFileFromLanguage(language) as FileLanguage; + }); + if (!memberPermissions) return c.res(lang.PERMISSIONS_ERROR); const hasMentionEveryonePermission = BigInt(memberPermissions) & BigInt(0x20000); if (!hasMentionEveryonePermission) { - return c.res(' **You need the Mention Everyone permission to use this command.**'); + return c.res(lang.MENTION_EVERYONE_PERMISSION_MISSING); } // get guild id - const guildId = c.interaction.guild?.id; - if (!guildId) return c.res(' **Guild not found.**'); + if (!guildId) return c.res(lang.GUILD_NOT_FOUND); const env = c.env as { DISCORD_TOKEN?: string }; const token = env.DISCORD_TOKEN || process.env.BOT_TOKEN; - if (!token) return c.res(' **Bot token not found in env.**'); + if (!token) return c.res(lang.BOT_TOKEN_NOT_FOUND_ERROR); // fetch members, limit to 1000 due to discord api limitation const resp = await ky.get(`https://discord.com/api/v10/guilds/${guildId}/members`, { @@ -49,7 +56,7 @@ To use a command, type \`/\` followed by the command name. For example, to ping } } const allMembers = (await resp.json()) as Array<{ user?: { id?: string; username?: string; bot?: boolean } }>; - if (allMembers.length === 0) return c.res(' **No members found.**'); + if (allMembers.length === 0) return c.res(lang.NO_MEMBERS_ERROR); // apply bot filter if requested and always exclude self const selfId = c.interaction.member?.user?.id; @@ -61,22 +68,26 @@ To use a command, type \`/\` followed by the command name. For example, to ping return true; }); - if (filtered.length === 0) - return c.res(' **No members match the filter (all results were bots or yourself).**'); + if (filtered.length === 0) return c.res(lang.NO_MEMBERS_MATCH_FILTER_ERROR); // pick a random member const randomMember = filtered[Math.floor(Math.random() * filtered.length)]; const userId = randomMember.user?.id; - if (!userId) return c.res(' **User ID not found.**'); + if (!userId) return c.res(lang.USER_ID_NOT_FOUND_ERROR); - return c.res(`** <@${userId}>, you have been chosen!**`); + return c.res(lang.YOU_HAVE_BEEN_CHOSEN.replace('{{USER_ID}}', `<@${userId}>`)); }) .command('ping', async (c) => { + let lang: FileLanguage = getFileFromLanguage('en') as FileLanguage; + const guildId = c.interaction.guild?.id as string; + await getLanguageFromServer(guildId, c).then((language) => { + lang = getFileFromLanguage(language) as FileLanguage; + }); const start = Date.now(); await fetch('https://discord.com/api/v10/users/@me'); // yes, that was the only way I found to get a measurable latency, dont judge me please :D const end = Date.now(); const latency = end - start; - return c.res(`** Pong!** \n-# Latency: ${latency}ms`); + return c.res(lang.PING.replace('{{LATENCY}}', latency.toString())); }); export default app; diff --git a/src/locales/en.yml b/src/locales/en.yml index f25433d..2dc9c04 100644 --- a/src/locales/en.yml +++ b/src/locales/en.yml @@ -7,3 +7,12 @@ NO_MEMBERS_MATCH_FILTER_ERROR: ' **No members m USER_ID_NOT_FOUND_ERROR: ' **User ID not found.**' YOU_HAVE_BEEN_CHOSEN: '** {{USER_ID}}, you have been chosen!**' PING: "** Pong!** \n-# Latency: {{LATENCY}}ms" +GUILD_NOT_FOUND: ' **Guild not found.**' +HELP_MESSAGE: | + **Available Commands:** + - `/someone [ignore-bots]`: Ping a random member from the server. Optionally ignore bot users. + - `/ping`: Replies with the current ping. + - `/help`: Provides help information for available commands. + + To use a command, type `/` followed by the command name. For example, to ping a random member, type `/someone`. You can add the optional parameter `ignore-bots` to exclude bot users from being selected. + - Source code is available on [GitHub](https://github.com/notthebestdev/someoneback). diff --git a/src/locales/fr.yml b/src/locales/fr.yml new file mode 100644 index 0000000..2b4b3e0 --- /dev/null +++ b/src/locales/fr.yml @@ -0,0 +1,18 @@ +PERMISSIONS_ERROR: ' **Impossible de vérifier les permissions.**' +MENTION_EVERYONE_PERMISSION_MISSING: ' **Vous devez avoir la permission Mentionner tout le monde pour utiliser cette commande.**' +GUILD_NOT_FOUND_ERROR: ' **Serveur introuvable.**' +BOT_TOKEN_NOT_FOUND_ERROR: ' **Jeton du bot introuvable dans .env**' +NO_MEMBERS_ERROR: ' **Aucun membre trouvé.**' +NO_MEMBERS_MATCH_FILTER_ERROR: ' **Aucun membre ne correspond au filtre (tous les résultats étaient des bots ou vous-même).**' +USER_ID_NOT_FOUND_ERROR: ' **ID utilisateur introuvable.**' +YOU_HAVE_BEEN_CHOSEN: '** {{USER_ID}}, vous avez été choisi !**' +PING: "** Pong !** \n-# Latence : {{LATENCY}}ms" +GUILD_NOT_FOUND: ' **Serveur introuvable.**' +HELP_MESSAGE: | + **Commandes disponibles :** + - `/someone [ignore-bots]` : Mentionne un membre aléatoire du serveur. Optionnellement, ignore les utilisateurs bots. + - `/ping` : Répond avec la latence actuelle. + - `/help` : Fournit des informations d'aide sur les commandes disponibles. + + Pour utiliser une commande, tapez `/` suivi du nom de la commande. Par exemple, pour mentionner un membre aléatoire, tapez `/someone`. Vous pouvez ajouter le paramètre optionnel `ignore-bots` pour exclure les bots de la sélection. + - Le code source est disponible sur [GitHub](https://github.com/notthebestdev/someoneback). diff --git a/src/types/fileLanguage.ts b/src/types/fileLanguage.ts index d0feebd..8460e8b 100644 --- a/src/types/fileLanguage.ts +++ b/src/types/fileLanguage.ts @@ -8,4 +8,6 @@ export interface FileLanguage { USER_ID_NOT_FOUND_ERROR: string; YOU_HAVE_BEEN_CHOSEN: string; PING: string; + GUILD_NOT_FOUND: string; + HELP_MESSAGE: string; } From a29ecd07106f0c5c4587e42e1769daf230547d57 Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:23:00 +0100 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=94=A5=20chore:=20remove=20unused?= =?UTF-8?q?=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removes an unused import to improve code cleanliness. - Eliminates `parse` from `yaml` as it is no longer needed. --- src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 08f617e..07eeea2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,4 @@ import { DiscordHono } from 'discord-hono'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { parse } from 'yaml'; import ky from 'ky'; import getLanguageFromServer from './utils/getLanguageFromServer'; import getFileFromLanguage from './utils/getFileFromLanguage'; From fd9a5bbdffbf98250584f7b8641f753ac37d42f5 Mon Sep 17 00:00:00 2001 From: TheBestDeveloper Date: Thu, 1 Jan 2026 17:26:41 +0100 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8=20feat:=20Enables=20NodeJS=20co?= =?UTF-8?q?mpatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adds the `nodejs_compat` compatibility flag to the Wrangler configuration. - Updates the generated types to include NodeJS process environment variables. - This allows the worker to access NodeJS-compatible APIs. --- worker-configuration.d.ts | 10 ++++++++-- wrangler.jsonc | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index fa368a3..7fc1bcc 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: 7941bebd71489c99a416797df3953c9e) -// Runtime types generated with workerd@1.20251217.0 2025-10-08 +// Generated by Wrangler by running `wrangler types` (hash: 381a3acd1c0f34f3a378298d677f7c3e) +// Runtime types generated with workerd@1.20251217.0 2025-10-08 nodejs_compat declare namespace Cloudflare { interface GlobalProps { mainModule: typeof import('./src/index'); @@ -12,6 +12,12 @@ declare namespace Cloudflare { } } interface Env extends Cloudflare.Env {} +type StringifyValues> = { + [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; +}; +declare namespace NodeJS { + interface ProcessEnv extends StringifyValues> {} +} // Begin runtime types /*! ***************************************************************************** diff --git a/wrangler.jsonc b/wrangler.jsonc index 3371096..72b1a7f 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -10,6 +10,7 @@ "observability": { "enabled": true, }, + "compatibility_flags": ["nodejs_compat"], /** * Smart Placement * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement From 8e1b0f3318c61156650485b958edcc42e69d559d Mon Sep 17 00:00:00 2001 From: "thebestdev." Date: Thu, 1 Jan 2026 17:33:41 +0100 Subject: [PATCH 08/10] fix: use guild instead of guid Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/utils/getLanguageFromServer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/getLanguageFromServer.ts b/src/utils/getLanguageFromServer.ts index 75ea03a..3c34427 100644 --- a/src/utils/getLanguageFromServer.ts +++ b/src/utils/getLanguageFromServer.ts @@ -3,19 +3,19 @@ import ky from 'ky'; export default async function getLanguageFromServer(serverId: string, c: CommandContext): Promise { // use ky as a workaround for custom endpoints not supported by client.rest - const guid = await ky.get(`https://discord.com/api/v9/guilds/templates/${serverId}`, { + const guild = await ky.get(`https://discord.com/api/v9/guilds/templates/${serverId}`, { headers: { Authorization: `Bot ${c.env.DISCORD_TOKEN || process.env.DISCORD_TOKEN}`, 'Content-Type': 'application/json', }, }); - if (!guid.ok) { + if (!guild.ok) { // get serialized_source_guild.preferred_locale from response - const data = (await guid.json()) as { serialized_source_guild?: { preferred_locale?: string } }; + const data = (await guild.json()) as { serialized_source_guild?: { preferred_locale?: string } }; return data?.serialized_source_guild?.preferred_locale || 'en'; } - const data = (await guid.json()) as { language?: string }; + const data = (await guild.json()) as { language?: string }; return data.language || 'en'; } From 42608f549b42e94f1127448e3efcf1b49462ab39 Mon Sep 17 00:00:00 2001 From: "thebestdev." Date: Thu, 1 Jan 2026 17:35:45 +0100 Subject: [PATCH 09/10] fix: parse the data only if the request succeds Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/utils/getLanguageFromServer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/getLanguageFromServer.ts b/src/utils/getLanguageFromServer.ts index 3c34427..0a3f140 100644 --- a/src/utils/getLanguageFromServer.ts +++ b/src/utils/getLanguageFromServer.ts @@ -11,11 +11,11 @@ export default async function getLanguageFromServer(serverId: string, c: Command }); if (!guild.ok) { - // get serialized_source_guild.preferred_locale from response - const data = (await guild.json()) as { serialized_source_guild?: { preferred_locale?: string } }; - return data?.serialized_source_guild?.preferred_locale || 'en'; + // request failed, fall back to default language + return 'en'; } - const data = (await guild.json()) as { language?: string }; - return data.language || 'en'; + // get serialized_source_guild.preferred_locale from successful response + const data = (await guid.json()) as { serialized_source_guild?: { preferred_locale?: string } }; + return data?.serialized_source_guild?.preferred_locale || 'en'; } From 9e3cbfb801bf3e1c44a324c09ca27d78f3c3ab73 Mon Sep 17 00:00:00 2001 From: "thebestdev." Date: Thu, 1 Jan 2026 17:40:24 +0100 Subject: [PATCH 10/10] fix: use type FileLanguage Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/utils/getFileFromLanguage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/getFileFromLanguage.ts b/src/utils/getFileFromLanguage.ts index a59663e..88bc720 100644 --- a/src/utils/getFileFromLanguage.ts +++ b/src/utils/getFileFromLanguage.ts @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; import type { FileLanguage } from '../types/fileLanguage'; -export default function getFileFromLanguage(language: string): unknown { +export default function getFileFromLanguage(language: string): FileLanguage { let fileName: string; switch (language) { case 'fr':