From 839a146b36a1bac2a1d1e5e286927a0a02e4acdc Mon Sep 17 00:00:00 2001 From: Zaka Date: Mon, 27 Jan 2025 17:34:55 +0100 Subject: [PATCH 01/14] fix deprecation --- src/commands/games/rps.ts | 3 ++- src/commands/news/add.ts | 3 ++- src/commands/news/edit.ts | 3 ++- src/commands/news/remove.ts | 3 ++- src/utils/embeds/errorEmbed.ts | 6 +++--- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/commands/games/rps.ts b/src/commands/games/rps.ts index 96aecefe..e5c14bb8 100644 --- a/src/commands/games/rps.ts +++ b/src/commands/games/rps.ts @@ -4,6 +4,7 @@ import { ButtonInteraction, ButtonStyle, EmbedBuilder, + MessageFlags, SlashCommandSubcommandBuilder, type ChatInputCommandInteraction } from "discord.js"; @@ -74,7 +75,7 @@ export async function run(interaction: ChatInputCommandInteraction) { collector.on("collect", async (i: ButtonInteraction) => { playerChoices.set(i.user.id, i.customId.split("_")[1] as RPSChoice); - await i.reply({ content: "Choice recorded!", ephemeral: true }); + await i.reply({ content: "Choice recorded!", flags: MessageFlags.Ephemeral }); if (playerChoices.size == 2) collector.stop("game-complete"); }); diff --git a/src/commands/news/add.ts b/src/commands/news/add.ts index 33a3cbd2..bb72e054 100644 --- a/src/commands/news/add.ts +++ b/src/commands/news/add.ts @@ -1,6 +1,7 @@ import { ActionRowBuilder, EmbedBuilder, + MessageFlags, ModalBuilder, SlashCommandSubcommandBuilder, TextInputBuilder, @@ -68,7 +69,7 @@ export async function run(interaction: ChatInputCommandInteraction) { await sendChannelNews(guild, id, interaction).catch(err => console.error(err)); await i.reply({ embeds: [new EmbedBuilder().setTitle("News added.").setColor(genColor(100))], - ephemeral: true + flags: MessageFlags.Ephemeral }); }); } diff --git a/src/commands/news/edit.ts b/src/commands/news/edit.ts index 32f75b81..298fbbb0 100644 --- a/src/commands/news/edit.ts +++ b/src/commands/news/edit.ts @@ -1,6 +1,7 @@ import { ActionRowBuilder, EmbedBuilder, + MessageFlags, ModalBuilder, SlashCommandSubcommandBuilder, TextInputBuilder, @@ -93,7 +94,7 @@ export async function run(interaction: ChatInputCommandInteraction) { updateNews(guild.id, id, title, body); await i.reply({ embeds: [new EmbedBuilder().setTitle("News edited.").setColor(genColor(100))], - ephemeral: true + flags: MessageFlags.Ephemeral }); }); } diff --git a/src/commands/news/remove.ts b/src/commands/news/remove.ts index cdf8b842..eb46b7cf 100644 --- a/src/commands/news/remove.ts +++ b/src/commands/news/remove.ts @@ -1,5 +1,6 @@ import { EmbedBuilder, + MessageFlags, SlashCommandSubcommandBuilder, TextChannel, type ChatInputCommandInteraction @@ -40,6 +41,6 @@ export async function run(interaction: ChatInputCommandInteraction) { deleteNews(guild.id, id); await interaction.reply({ embeds: [new EmbedBuilder().setTitle("News removed.").setColor(genColor(100))], - ephemeral: true + flags: MessageFlags.Ephemeral }); } diff --git a/src/utils/embeds/errorEmbed.ts b/src/utils/embeds/errorEmbed.ts index b4d60efa..513eb486 100644 --- a/src/utils/embeds/errorEmbed.ts +++ b/src/utils/embeds/errorEmbed.ts @@ -1,4 +1,4 @@ -import { EmbedBuilder, type ButtonInteraction, type ChatInputCommandInteraction } from "discord.js"; +import { EmbedBuilder, MessageFlags, type ButtonInteraction, type ChatInputCommandInteraction } from "discord.js"; import { genColor } from "../colorGen"; /** @@ -21,6 +21,6 @@ export async function errorEmbed( .setDescription(content.join("\n")) .setColor(genColor(0)); - if (interaction.replied) return await interaction.followUp({ embeds: [embed], ephemeral: true }); - return await interaction.reply({ embeds: [embed], ephemeral: true }); + if (interaction.replied) return await interaction.followUp({ embeds: [embed], flags: MessageFlags.Ephemeral }); + return await interaction.reply({ embeds: [embed], flags: MessageFlags.Ephemeral }); } From b22e0c661c9fe8d9db10c08d1c3f214c29ae8b88 Mon Sep 17 00:00:00 2001 From: Zaka Date: Mon, 27 Jan 2025 17:35:18 +0100 Subject: [PATCH 02/14] very slightly restyle math command --- src/commands/math/graph.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/commands/math/graph.ts b/src/commands/math/graph.ts index 3414713e..89e70ca7 100644 --- a/src/commands/math/graph.ts +++ b/src/commands/math/graph.ts @@ -7,6 +7,7 @@ import { } from "discord.js"; import * as math from "mathjs"; import { errorEmbed } from "../../utils/embeds/errorEmbed"; +import { genColor, genRGBColor } from "../../utils/colorGen"; export const data = new SlashCommandSubcommandBuilder() .setName("graph") @@ -58,7 +59,7 @@ export async function run(interaction: ChatInputCommandInteraction) { ctx.stroke(); ctx.strokeStyle = "#ff0000"; - ctx.lineWidth = 2; + ctx.lineWidth = 3; ctx.beginPath(); const points = 1000; @@ -89,16 +90,16 @@ export async function run(interaction: ChatInputCommandInteraction) { const attachment = new AttachmentBuilder(canvas.toBuffer(), { name: "graph.png" }); const embed = new EmbedBuilder() .setTitle("Function Graph") - .setDescription(`f(x) = ${func}`) + .setDescription(`\`f(x) = ${func}\``) .setImage("attachment://graph.png") - .setColor("#00ff00"); + .setColor(genColor(100)); await interaction.reply({ embeds: [embed], files: [attachment] }); } catch (error) { return await errorEmbed( interaction, "Invalid function", - "Please provide a valid mathematical function. Examples: 'x^2', 'sin(x)', '2*x + 1'" + "Please provide a valid mathematical function. Examples: 'x^2', 'sin(x)', '2*x + 1'." ); } } From 46760b3ee0830b2a959e4d85603307e866bd76c6 Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 17:12:54 +0100 Subject: [PATCH 03/14] Add support for server invites in serverboard --- src/commands/serverboard.ts | 22 ++++++++++++++++---- src/utils/database/settings.ts | 18 ++++++++++++---- src/utils/embeds/serverEmbed.ts | 37 +++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/commands/serverboard.ts b/src/commands/serverboard.ts index e84222a4..0e0615bd 100644 --- a/src/commands/serverboard.ts +++ b/src/commands/serverboard.ts @@ -3,6 +3,7 @@ import { ButtonBuilder, ButtonInteraction, ButtonStyle, + Guild, SlashCommandBuilder, type ChatInputCommandInteraction } from "discord.js"; @@ -16,9 +17,16 @@ export const data = new SlashCommandBuilder() .addNumberOption(number => number.setName("page").setDescription("The page you want to see.")); export async function run(interaction: ChatInputCommandInteraction) { - const guildList = ( - await Promise.all(listPublicServers().map(id => interaction.client.guilds.fetch(id))) - ).sort((a, b) => b.memberCount - a.memberCount); + const guildList: { guild: Guild; showInvite: boolean }[] = ( + await Promise.all( + listPublicServers().map(async entry => { + return { + guild: await interaction.client.guilds.fetch(entry.guildID), + showInvite: entry.showInvite + }; + }) + ) + ).sort((a, b) => b.guild.memberCount - a.guild.memberCount); const pages = guildList.length; if (!pages) @@ -32,7 +40,13 @@ export async function run(interaction: ChatInputCommandInteraction) { let page = (argPage - 1 <= 0 ? 0 : argPage - 1 > pages ? pages - 1 : argPage - 1) || 0; async function getEmbed() { - return await serverEmbed({ guild: guildList[page], page: page + 1, pages }); + return await serverEmbed({ + guild: guildList[page].guild, + showInvite: guildList[page].showInvite, + page: page + 1, + pages, + roles: true + }); } const row = new ActionRowBuilder().addComponents( diff --git a/src/utils/database/settings.ts b/src/utils/database/settings.ts index 61da872f..f5c36a52 100644 --- a/src/utils/database/settings.ts +++ b/src/utils/database/settings.ts @@ -314,6 +314,9 @@ const getQuery = database.query("SELECT * FROM settings WHERE guildID = $1 AND k const listPublicQuery = database.query( "SELECT * FROM settings WHERE key = 'serverboard.shown' AND value = '1';" ); +const listPublicWithInvitesEnabledQuery = database.query( + "SELECT * FROM settings WHERE key = 'serverboard.server_invite' AND value = '1';" +) const deleteQuery = database.query("DELETE FROM settings WHERE guildID = $1 AND key = $2;"); const insertQuery = database.query( "INSERT INTO settings (guildID, key, value) VALUES (?1, ?2, ?3);" @@ -369,8 +372,15 @@ export function setSetting( insertQuery.run(JSON.stringify(guildID), `${key}.${setting}`, value); } -export function listPublicServers() { - return (listPublicQuery.all() as TypeOfDefinition[]).map(entry => - JSON.parse(entry.guildID) - ); +export function listPublicServers(): { guildID: string, showInvite: boolean }[] { + const publicGuildSet = new Set((listPublicQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); + // you know that time-complexity thingy? idk much but uh an array has O(n) and a JS Set() has O(1) which should mean using a Set is more performant + const inviteGuildsSet = new Set((listPublicWithInvitesEnabledQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); + + return (Array.from(publicGuildSet)).map(entry => { + return { + guildID: entry, + showInvite: inviteGuildsSet.has(entry), + } + }); } diff --git a/src/utils/embeds/serverEmbed.ts b/src/utils/embeds/serverEmbed.ts index 2af9256b..86096521 100644 --- a/src/utils/embeds/serverEmbed.ts +++ b/src/utils/embeds/serverEmbed.ts @@ -4,13 +4,14 @@ * @returns Embed that contains the guild info. */ -import { EmbedBuilder, type Guild } from "discord.js"; +import { EmbedBuilder, Invite, type Guild } from "discord.js"; import { genColor } from "../colorGen"; import { imageColor } from "../imageColor"; import { pluralOrNot } from "../pluralOrNot"; type Options = { guild: Guild; + showInvite: boolean; roles?: boolean; page?: number; pages?: number; @@ -66,6 +67,8 @@ export async function serverEmbed(options: Options) { .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}` }); + const channelCount = channelSizes.text + channelSizes.voice; + embed.addFields( { name: `👥 • ${guild.memberCount?.toLocaleString("en-US")} members`, @@ -76,7 +79,7 @@ export async function serverEmbed(options: Options) { inline: true }, { - name: `🗨️ • ${channelSizes.text + channelSizes.voice} ${pluralOrNot("channel", channelSizes.text + channelSizes.voice)}`, + name: `🗨️ • ${channelCount} ${pluralOrNot("channel", channelCount)}`, value: [ `**${channelSizes.text}** text • **${channelSizes.voice}** voice`, `**${channelSizes.categories}** ${pluralOrNot("category", channelSizes.categories)}` @@ -95,5 +98,35 @@ export async function serverEmbed(options: Options) { } ); + if (options.showInvite) { + const previousInvite: Invite | undefined = (await options.guild.invites.fetch()).find( + invite => + invite.inviter?.id === "873918300726394960" && + invite.maxUses === null && + invite.expiresAt === null + ); + + if (!(options.guild.rulesChannel)) return embed; + // TODO - either + // 1. show an error to the server owner + // 2. use the 1st channel of the server if no rules channel exists) + + const inviteUrl = previousInvite + ? previousInvite.url + : await options.guild.rulesChannel.createInvite({ + maxAge: undefined, + maxUses: undefined, + reason: "Serverboard", + temporary: false, + unique: true + }); + + embed.addFields({ + name: `🚪 • Join in!`, + value: `This server allows you to join from here. ${inviteUrl}`, + inline: true + }); + } + return embed; } From 051d5940bdc63cd0d0e4fc4a1677e5377116c76f Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 17:16:35 +0100 Subject: [PATCH 04/14] Replace underscore with whitespace when showing settings --- src/commands/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/settings.ts b/src/commands/settings.ts index 493b87cf..210388f4 100644 --- a/src/commands/settings.ts +++ b/src/commands/settings.ts @@ -147,7 +147,7 @@ export async function run(interaction: ChatInputCommandInteraction) { ); setSetting(guild.id, key, option.name, option.value as string); - description += `**${capitalize(option.name)}:** ${settingText(option.name.toString()!)}\n` + description += `**${capitalize(option.name)?.replace("_", " ")}:** ${settingText(option.name.toString()!)}\n` } embed.setDescription(description) From be86be943561a560ef04d243b39ee5b4d33d1566 Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 17:25:50 +0100 Subject: [PATCH 05/14] Fix a few typos --- .gitignore | 1 + bun.lock | 2 +- src/utils/database/settings.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 200aebbe..08986023 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ .DS_Store data.db .idea +.vscode/ diff --git a/bun.lock b/bun.lock index f3941dd4..32809283 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,5 @@ { - // Note: this file must be committed for package version consisency for everyone who uses bun install + // Note: this file must be committed for package version consistency for everyone who uses bun install "lockfileVersion": 1, "workspaces": { "": { diff --git a/src/utils/database/settings.ts b/src/utils/database/settings.ts index f5c36a52..3e7feecd 100644 --- a/src/utils/database/settings.ts +++ b/src/utils/database/settings.ts @@ -19,7 +19,7 @@ export const settingsDefinition: Record< } > = { leveling: { - description: "Customise the behaviour of the leveling system.", + description: "Customize the behavior of the leveling system.", settings: { enabled: { type: "BOOL", From 6086226eb23b3f890c8f4bfa9d99e942fa15652b Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 20:09:20 +0100 Subject: [PATCH 06/14] Remove Instagram link (unused) --- src/commands/about.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/about.ts b/src/commands/about.ts index 43be4ba7..32f2f2ef 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -42,7 +42,7 @@ export async function run(interaction: ChatInputCommandInteraction) { { name: "🔗 • Links", value: [ - "[Discord](https://discord.gg/c6C25P4BuY) • [GitHub](https://www.github.com/SokoraDesu) • [YouTube](https://www.youtube.com/@SokoraDesu) • [Instagram](https://instagram.com/NebulaTheBot) • [Mastodon](https://mastodon.online/@NebulaTheBot@mastodon.social) • [Matrix](https://matrix.to/#/#sokora:matrix.org) • [Revolt](https://rvlt.gg/28TS9aXy)", + "[Discord](https://discord.gg/c6C25P4BuY) • [GitHub](https://www.github.com/SokoraDesu) • [YouTube](https://www.youtube.com/@SokoraDesu) • [Mastodon](https://mastodon.online/@NebulaTheBot@mastodon.social) • [Matrix](https://matrix.to/#/#sokora:matrix.org) • [Revolt](https://rvlt.gg/28TS9aXy)", "Also, please read the [ToS](https://sokora.org/terms) and the [privacy policy](https://sokora.org/privacy)." ].join("\n") } From a71ef2adec4e7752a1b4792dad7f8a9fa775d774 Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 20:42:36 +0100 Subject: [PATCH 07/14] Humanize settings display + capitalize() don't return undefined --- src/commands/settings.ts | 9 +++++---- src/utils/capitalize.ts | 3 +-- src/utils/humanizeSettings.ts | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/utils/humanizeSettings.ts diff --git a/src/commands/settings.ts b/src/commands/settings.ts index 210388f4..8dd09270 100644 --- a/src/commands/settings.ts +++ b/src/commands/settings.ts @@ -16,6 +16,7 @@ import { } from "../utils/database/settings"; import { errorEmbed } from "../utils/embeds/errorEmbed"; import { capitalize } from "../utils/capitalize"; +import { humanizeSettings } from "../utils/humanizeSettings"; export let data = new SlashCommandBuilder() .setName("settings") @@ -94,7 +95,7 @@ export async function run(interaction: ChatInputCommandInteraction) { const key = interaction.options.getSubcommand() as keyof typeof settingsDefinition; const values = interaction.options.data[0].options!; const settingsDef = settingsDefinition[key]; - const settingText = (name: string) => { + const settingText = (name: string): string => { const setting = getSetting(guild.id, key, name)?.toString(); let text; switch (settingsDef.settings[name].type) { @@ -108,7 +109,7 @@ export async function run(interaction: ChatInputCommandInteraction) { text = setting ? `<@&${setting}>` : "Not set"; break; default: - text = setting || "Not set"; + text = setting || "*Not set*"; break; } return text; @@ -118,7 +119,7 @@ export async function run(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setAuthor({ name: `${capitalize(key)} settings` }) .setDescription( - Object.keys(settingsDef.settings).map(setting => `${settingsDef.settings[setting].emoji} **• ${capitalize(setting.replaceAll("_", " "))}**: ${settingText(setting)}`).join("\n") + Object.keys(settingsDef.settings).map(setting => `${settingsDef.settings[setting].emoji} **• ${humanizeSettings(capitalize(setting))}**: ${humanizeSettings(settingText(setting))}`).join("\n") ) .setColor(genColor(100)); @@ -147,7 +148,7 @@ export async function run(interaction: ChatInputCommandInteraction) { ); setSetting(guild.id, key, option.name, option.value as string); - description += `**${capitalize(option.name)?.replace("_", " ")}:** ${settingText(option.name.toString()!)}\n` + description += `**${humanizeSettings(capitalize(option.name))}:** ${humanizeSettings(settingText(option.name.toString()))}\n` } embed.setDescription(description) diff --git a/src/utils/capitalize.ts b/src/utils/capitalize.ts index 9c2d7cfe..8ae43a81 100644 --- a/src/utils/capitalize.ts +++ b/src/utils/capitalize.ts @@ -3,7 +3,6 @@ * @param string String, the first letter of which should be capitalized. */ -export function capitalize(string: string) { - if (!string) return; +export function capitalize(string: string): string { return `${string.charAt(0).toUpperCase()}${string.slice(1)}`; } diff --git a/src/utils/humanizeSettings.ts b/src/utils/humanizeSettings.ts new file mode 100644 index 00000000..aea524b0 --- /dev/null +++ b/src/utils/humanizeSettings.ts @@ -0,0 +1,18 @@ + +/** + * Outputs the given settings_string + * @param {string} string Settings string, either a key or a value. + */ +export function humanizeSettings(string: string) { + if (!string) return; + const humanized = string + .trim() + .replaceAll("_", " ") + .replaceAll("true", "***Enabled***") + .replaceAll("false", "***Disabled***") + .replaceAll("(name)", "\`(name)\`") + .replaceAll("(servername)", "\`(servername)\`") + .replaceAll("(count)", "\`(count)\`") + + return humanized +} \ No newline at end of file From 7983cd816b08d035fd5c368c291ca0bdec8131b1 Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 20:59:16 +0100 Subject: [PATCH 08/14] Format markdown + Add code style guidelines --- CONTRIBUTING.md | 24 ++++++++++++++++++++++-- README.md | 2 ++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa5e502e..36c3a998 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,25 +1,45 @@ # Sokora Contributing Guide ## Prerequisites + - Basic knowledge of [TypeScript](https://typescriptlang.org/) and [discord.js](https://discord.js.org/). - [Bun](https://bun.sh) installed. ## Get started with contributing + ### Getting the code + - Make a fork of this repository. - Clone your fork. ### Creating your bot + - Head over to the [Discord Developer Portal](https://discord.com/developers/applications) and make a new application. - Invite your bot to your server. - Reset and then copy your bot's token. ### Setting up .env -- Run `bun run setup` and our cli tool will install dependencies and write .env for you + +- Run `bun run setup` and our CLI tool will install dependencies and write .env for you. It'll ask you to paste in your bot's token. ### Running + - Run `bun dev`. Be sure to open a pull request when you're ready to push your changes. Be descriptive of the changes you've made. -![](https://user-images.githubusercontent.com/51555391/176925763-cdfd57ba-ae1e-4bf3-85e9-b3ebd30b1d59.png) +## Code style guidelines + +A few, simple guidelines onto how code contributed to Sokora should look like. + +- Keep a consistent indentation of two spaces. Don't use tabs. +- Use `K&R` style for bracket placement (`function() {}` instead of `function() \n {}`). +- Use `camelCase` for both variables and function names. +- Keep lines reasonably short, don't fear linebreaks. Of course, longer lines are valid where needed. +- Use early returns to avoid nesting. +- Avoid curly braces in one-line `if` statements. +- Non-nullish assertions are valid when needed. +- Prefer strict comparisons (`===` / `!==`). +- Use the cache instead of a new `fetch()` call where possible, to avoid unnecessary usage (e.g., if possible, prefer `guild.members.cache` over `await guild.members.fetch()`). + +![PLEASE SUBMIT A PR, NO DIRECT COMMITS](https://user-images.githubusercontent.com/51555391/176925763-cdfd57ba-ae1e-4bf3-85e9-b3ebd30b1d59.png) diff --git a/README.md b/README.md index d0ce2114..94d1b059 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,10 @@ # About + Sokora is a multipurpose Discord bot that lets you manage your servers easily. **Please note that Sokora is currently unstable so it might have issues.** # Contributing + While we're developing the bot, you can [help us](CONTRIBUTING.MD) if you find any bugs. From 24413b737fba7e795734aeca2240097a1026ec74 Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 21:22:39 +0100 Subject: [PATCH 09/14] Add timestamp and See profile to edit/remove mod embeds --- src/events/messageDelete.ts | 5 ++++- src/events/messageUpdate.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/events/messageDelete.ts b/src/events/messageDelete.ts index 90516813..b00391d1 100644 --- a/src/events/messageDelete.ts +++ b/src/events/messageDelete.ts @@ -16,7 +16,10 @@ export default (async function run(message) { name: `• ${author.displayName}'s message has been deleted.`, iconURL: author.displayAvatarURL() }) - .setDescription(`[Jump to message](${message.url})`) + .setDescription( + `[Jump to message](${message.url}) • [See ${author.displayName}'s profile](https://discord.com/users/${author.id})` + ) + .setTimestamp(new Date()) .addFields({ name: "🗑️ • Deleted message", value: message.content! diff --git a/src/events/messageUpdate.ts b/src/events/messageUpdate.ts index e900a52d..6d75dfc5 100644 --- a/src/events/messageUpdate.ts +++ b/src/events/messageUpdate.ts @@ -20,7 +20,10 @@ export default (async function run(oldMessage, newMessage) { name: `• ${author.displayName} edited a message.`, iconURL: author.displayAvatarURL() }) - .setDescription(`[Jump to message](${oldMessage.url})`) + .setDescription( + `[Jump to message](${oldMessage.url}) • [See ${author.displayName}'s profile](https://discord.com/users/${author.id})` + ) + .setTimestamp(new Date()) .addFields( { name: "🖋️ • Old message", From 53071d854ca1f48f324ba761d26a477e275def0e Mon Sep 17 00:00:00 2001 From: Zaka Date: Wed, 29 Jan 2025 21:28:37 +0100 Subject: [PATCH 10/14] i think this makes SQL more performant? --- src/utils/database/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/database/settings.ts b/src/utils/database/settings.ts index 3e7feecd..7a21dd27 100644 --- a/src/utils/database/settings.ts +++ b/src/utils/database/settings.ts @@ -315,7 +315,7 @@ const listPublicQuery = database.query( "SELECT * FROM settings WHERE key = 'serverboard.shown' AND value = '1';" ); const listPublicWithInvitesEnabledQuery = database.query( - "SELECT * FROM settings WHERE key = 'serverboard.server_invite' AND value = '1';" + "SELECT * FROM settings WHERE EXISTS (SELECT 1 FROM settings WHERE key = 'serverboard.server_invite' AND value = '1') AND EXISTS (SELECT 1 FROM settings WHERE key = 'serverboard.shown' AND value = '1');" ) const deleteQuery = database.query("DELETE FROM settings WHERE guildID = $1 AND key = $2;"); const insertQuery = database.query( From 7f08ac8fa471e76e08e78c446e4149b62829bf1c Mon Sep 17 00:00:00 2001 From: Zaka Date: Fri, 31 Jan 2025 18:52:19 +0100 Subject: [PATCH 11/14] Allow to set serverboard invite channel --- .prettierrc.json | 2 +- src/commands/server.ts | 2 +- src/commands/serverboard.ts | 13 +++++---- src/utils/database/settings.ts | 18 ++++++++++-- src/utils/embeds/serverEmbed.ts | 52 ++++++++++++++++++++------------- 5 files changed, 56 insertions(+), 31 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 9d5a120e..0f4a0472 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,6 +2,6 @@ "tabWidth": 2, "useTabs": false, "arrowParens": "avoid", - "trailingComma": "none", + "trailingComma": "es5", "printWidth": 100 } diff --git a/src/commands/server.ts b/src/commands/server.ts index 769e0171..594e6ce7 100644 --- a/src/commands/server.ts +++ b/src/commands/server.ts @@ -6,6 +6,6 @@ export const data = new SlashCommandBuilder() .setDescription("Shows this server's info."); export async function run(interaction: ChatInputCommandInteraction) { - const embed = await serverEmbed({ guild: interaction.guild!, roles: true }); + const embed = await serverEmbed({ guild: interaction.guild! }); await interaction.reply({ embeds: [embed] }); } diff --git a/src/commands/serverboard.ts b/src/commands/serverboard.ts index 0e0615bd..05229fcf 100644 --- a/src/commands/serverboard.ts +++ b/src/commands/serverboard.ts @@ -17,12 +17,13 @@ export const data = new SlashCommandBuilder() .addNumberOption(number => number.setName("page").setDescription("The page you want to see.")); export async function run(interaction: ChatInputCommandInteraction) { - const guildList: { guild: Guild; showInvite: boolean }[] = ( + const guildList: { guild: Guild; showInvite: boolean, inviteChannelId: string | null }[] = ( await Promise.all( listPublicServers().map(async entry => { return { guild: await interaction.client.guilds.fetch(entry.guildID), - showInvite: entry.showInvite + showInvite: entry.showInvite, + inviteChannelId: entry.inviteChannelId }; }) ) @@ -42,10 +43,12 @@ export async function run(interaction: ChatInputCommandInteraction) { async function getEmbed() { return await serverEmbed({ guild: guildList[page].guild, - showInvite: guildList[page].showInvite, + invite: { + show: guildList[page].showInvite, + channel: guildList[page].inviteChannelId + }, page: page + 1, - pages, - roles: true + pages }); } diff --git a/src/utils/database/settings.ts b/src/utils/database/settings.ts index 7a21dd27..c50cfad5 100644 --- a/src/utils/database/settings.ts +++ b/src/utils/database/settings.ts @@ -15,7 +15,7 @@ export const settingsDefinition: Record< string, { description: string; - settings: Record; + settings: Record; } > = { leveling: { @@ -209,6 +209,11 @@ export const settingsDefinition: Record< desc: "Whether to show server invite on the serverboard.", val: false, emoji: "🍍", + }, + invite_channel: { + type: "CHANNEL", + desc: "Channel for the invite. If unset, if a rules channel exists uses it, hides the invite otherwise.", + emoji: "🍍", } } }, @@ -316,7 +321,7 @@ const listPublicQuery = database.query( ); const listPublicWithInvitesEnabledQuery = database.query( "SELECT * FROM settings WHERE EXISTS (SELECT 1 FROM settings WHERE key = 'serverboard.server_invite' AND value = '1') AND EXISTS (SELECT 1 FROM settings WHERE key = 'serverboard.shown' AND value = '1');" -) +); const deleteQuery = database.query("DELETE FROM settings WHERE guildID = $1 AND key = $2;"); const insertQuery = database.query( "INSERT INTO settings (guildID, key, value) VALUES (?1, ?2, ?3);" @@ -372,15 +377,22 @@ export function setSetting( insertQuery.run(JSON.stringify(guildID), `${key}.${setting}`, value); } -export function listPublicServers(): { guildID: string, showInvite: boolean }[] { +export function listPublicServers(): { + guildID: string; + showInvite: boolean; + inviteChannelId: string | null; +}[] { const publicGuildSet = new Set((listPublicQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); // you know that time-complexity thingy? idk much but uh an array has O(n) and a JS Set() has O(1) which should mean using a Set is more performant const inviteGuildsSet = new Set((listPublicWithInvitesEnabledQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); return (Array.from(publicGuildSet)).map(entry => { + const inviteChannel = getSetting(entry, "serverboard", "invite_channel"); + return { guildID: entry, showInvite: inviteGuildsSet.has(entry), + inviteChannelId: inviteChannel ? inviteChannel.toString() : null, } }); } diff --git a/src/utils/embeds/serverEmbed.ts b/src/utils/embeds/serverEmbed.ts index 86096521..f6be4a70 100644 --- a/src/utils/embeds/serverEmbed.ts +++ b/src/utils/embeds/serverEmbed.ts @@ -4,15 +4,17 @@ * @returns Embed that contains the guild info. */ -import { EmbedBuilder, Invite, type Guild } from "discord.js"; +import { ChannelType, EmbedBuilder, Invite, type Guild } from "discord.js"; import { genColor } from "../colorGen"; import { imageColor } from "../imageColor"; import { pluralOrNot } from "../pluralOrNot"; type Options = { guild: Guild; - showInvite: boolean; - roles?: boolean; + invite?: { + show: boolean; + channel: string | null; + }; page?: number; pages?: number; }; @@ -55,21 +57,19 @@ export async function serverEmbed(options: Options) { .setThumbnail(icon) .setColor((await imageColor(icon)) ?? genColor(200)); - if (options.roles) - embed.addFields({ - name: `🎭 • ${roles.size - 1} ${pluralOrNot("role", roles.size - 1)}`, - value: - roles.size == 1 - ? "*None*" - : `${sortedRoles - .slice(0, 5) - .map(role => `<@&${role[0]}>`) - .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}` - }); - const channelCount = channelSizes.text + channelSizes.voice; embed.addFields( + { + name: `🎭 • ${roles.size - 1} ${pluralOrNot("role", roles.size - 1)}`, + value: + roles.size == 1 + ? "*None*" + : `${sortedRoles + .slice(0, 5) + .map(role => `<@&${role[0]}>`) + .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}` + }, { name: `👥 • ${guild.memberCount?.toLocaleString("en-US")} members`, value: [ @@ -98,7 +98,7 @@ export async function serverEmbed(options: Options) { } ); - if (options.showInvite) { + if (options.invite?.show) { const previousInvite: Invite | undefined = (await options.guild.invites.fetch()).find( invite => invite.inviter?.id === "873918300726394960" && @@ -106,14 +106,24 @@ export async function serverEmbed(options: Options) { invite.expiresAt === null ); - if (!(options.guild.rulesChannel)) return embed; - // TODO - either - // 1. show an error to the server owner - // 2. use the 1st channel of the server if no rules channel exists) + if (!options.guild.rulesChannel) return embed; + + const possiblyFetchedInviteChannel = await options.guild.channels.fetch( + options.invite.channel ?? "hi" + ); + + const inviteChannel = + possiblyFetchedInviteChannel && + possiblyFetchedInviteChannel.isTextBased() && + !possiblyFetchedInviteChannel.isThread() + ? possiblyFetchedInviteChannel + : options.guild.rulesChannel; + + if (!inviteChannel) return embed; const inviteUrl = previousInvite ? previousInvite.url - : await options.guild.rulesChannel.createInvite({ + : await inviteChannel.createInvite({ maxAge: undefined, maxUses: undefined, reason: "Serverboard", From 9951ffdae371fa878fa5941ac30316d5ee7cd884 Mon Sep 17 00:00:00 2001 From: Zaka Date: Fri, 31 Jan 2025 18:53:21 +0100 Subject: [PATCH 12/14] Prettify code --- src/commands/serverboard.ts | 12 +++---- src/commands/settings.ts | 25 +++++++++----- src/utils/database/settings.ts | 58 +++++++++++++++++++-------------- src/utils/embeds/modEmbed.ts | 16 +++++---- src/utils/embeds/serverEmbed.ts | 34 +++++++++---------- src/utils/humanizeSettings.ts | 25 +++++++------- 6 files changed, 95 insertions(+), 75 deletions(-) diff --git a/src/commands/serverboard.ts b/src/commands/serverboard.ts index 05229fcf..afb4fc43 100644 --- a/src/commands/serverboard.ts +++ b/src/commands/serverboard.ts @@ -5,7 +5,7 @@ import { ButtonStyle, Guild, SlashCommandBuilder, - type ChatInputCommandInteraction + type ChatInputCommandInteraction, } from "discord.js"; import { listPublicServers } from "../utils/database/settings"; import { errorEmbed } from "../utils/embeds/errorEmbed"; @@ -17,13 +17,13 @@ export const data = new SlashCommandBuilder() .addNumberOption(number => number.setName("page").setDescription("The page you want to see.")); export async function run(interaction: ChatInputCommandInteraction) { - const guildList: { guild: Guild; showInvite: boolean, inviteChannelId: string | null }[] = ( + const guildList: { guild: Guild; showInvite: boolean; inviteChannelId: string | null }[] = ( await Promise.all( listPublicServers().map(async entry => { return { guild: await interaction.client.guilds.fetch(entry.guildID), showInvite: entry.showInvite, - inviteChannelId: entry.inviteChannelId + inviteChannelId: entry.inviteChannelId, }; }) ) @@ -45,10 +45,10 @@ export async function run(interaction: ChatInputCommandInteraction) { guild: guildList[page].guild, invite: { show: guildList[page].showInvite, - channel: guildList[page].inviteChannelId + channel: guildList[page].inviteChannelId, }, page: page + 1, - pages + pages, }); } @@ -65,7 +65,7 @@ export async function run(interaction: ChatInputCommandInteraction) { const reply = await interaction.reply({ embeds: [await getEmbed()], - components: pages != 1 ? [row] : [] + components: pages != 1 ? [row] : [], }); if (pages == 1) return; diff --git a/src/commands/settings.ts b/src/commands/settings.ts index 8dd09270..823a442e 100644 --- a/src/commands/settings.ts +++ b/src/commands/settings.ts @@ -5,14 +5,14 @@ import { PermissionsBitField, SlashCommandBuilder, SlashCommandSubcommandBuilder, - type ChatInputCommandInteraction + type ChatInputCommandInteraction, } from "discord.js"; import { genColor } from "../utils/colorGen"; import { getSetting, setSetting, settingsDefinition, - settingsKeys + settingsKeys, } from "../utils/database/settings"; import { errorEmbed } from "../utils/embeds/errorEmbed"; import { capitalize } from "../utils/capitalize"; @@ -119,7 +119,14 @@ export async function run(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setAuthor({ name: `${capitalize(key)} settings` }) .setDescription( - Object.keys(settingsDef.settings).map(setting => `${settingsDef.settings[setting].emoji} **• ${humanizeSettings(capitalize(setting))}**: ${humanizeSettings(settingText(setting))}`).join("\n") + Object.keys(settingsDef.settings) + .map( + setting => + `${settingsDef.settings[setting].emoji} **• ${humanizeSettings( + capitalize(setting) + )}**: ${humanizeSettings(settingText(setting))}` + ) + .join("\n") ) .setColor(genColor(100)); @@ -128,9 +135,9 @@ export async function run(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setColor(genColor(100)) - .setAuthor({ name: `✅ • ${capitalize(key)} settings changed` }) + .setAuthor({ name: `✅ • ${capitalize(key)} settings changed` }); - let description = "" + let description = ""; for (let i = 0; i < values.length; i++) { const option = values[i]; @@ -148,9 +155,11 @@ export async function run(interaction: ChatInputCommandInteraction) { ); setSetting(guild.id, key, option.name, option.value as string); - description += `**${humanizeSettings(capitalize(option.name))}:** ${humanizeSettings(settingText(option.name.toString()))}\n` + description += `**${humanizeSettings(capitalize(option.name))}:** ${humanizeSettings( + settingText(option.name.toString()) + )}\n`; } - embed.setDescription(description) + embed.setDescription(description); await interaction.reply({ embeds: [embed] }); } @@ -162,7 +171,7 @@ export async function autocomplete(interaction: AutocompleteInteraction) { await interaction.respond( ["true", "false"].map(choice => ({ name: choice, - value: choice + value: choice, })) ); break; diff --git a/src/utils/database/settings.ts b/src/utils/database/settings.ts index c50cfad5..74076d9b 100644 --- a/src/utils/database/settings.ts +++ b/src/utils/database/settings.ts @@ -7,8 +7,8 @@ const tableDefinition = { definition: { guildID: "TEXT", key: "TEXT", - value: "TEXT" - } + value: "TEXT", + }, } satisfies TableDefinition; export const settingsDefinition: Record< @@ -131,8 +131,8 @@ export const settingsDefinition: Record< desc: "Delay before autokicking is triggered", val: false, emoji: "🍍", - } - } + }, + }, }, news: { description: "Configure news for your server.", @@ -164,8 +164,8 @@ export const settingsDefinition: Record< desc: "Allow users to receive news in DMs.", val: false, emoji: "🍍", - } - } + }, + }, }, starboard: { description: "Configure the starboard system.", @@ -192,8 +192,8 @@ export const settingsDefinition: Record< desc: "Reactions needed for a message to be starred.", val: 3, emoji: "🍍", - } - } + }, + }, }, serverboard: { description: "Configure your server's appearance on the serverboard.", @@ -214,8 +214,8 @@ export const settingsDefinition: Record< type: "CHANNEL", desc: "Channel for the invite. If unset, if a rules channel exists uses it, hides the invite otherwise.", emoji: "🍍", - } - } + }, + }, }, welcome: { description: "Change how Sokora welcomes your new users.", @@ -259,8 +259,8 @@ export const settingsDefinition: Record< type: "TEXT", desc: "Roles to exclude from retention (comma-separated IDs)", emoji: "🍍", - } - } + }, + }, }, easter: { description: "Enable/disable easter eggs.", @@ -275,8 +275,8 @@ export const settingsDefinition: Record< type: "TEXT", desc: "Channel IDs where easter eggs are allowed (comma-separated).", emoji: "🍍", - } - } + }, + }, }, commands: { description: "Configure command availability.", @@ -285,8 +285,8 @@ export const settingsDefinition: Record< type: "TEXT", desc: "Disabled commands (comma-separated names).", emoji: "🍍", - } - } + }, + }, }, currency: { description: "Configure the multi-currency system.", @@ -308,9 +308,9 @@ export const settingsDefinition: Record< desc: "Name of the secondary currency.", val: "gems", emoji: "🍍", - } - } - } + }, + }, + }, }; export const settingsKeys = Object.keys(settingsDefinition) as (keyof typeof settingsDefinition)[]; @@ -382,17 +382,25 @@ export function listPublicServers(): { showInvite: boolean; inviteChannelId: string | null; }[] { - const publicGuildSet = new Set((listPublicQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); + const publicGuildSet = new Set( + (listPublicQuery.all() as TypeOfDefinition[]).map(entry => + JSON.parse(entry.guildID) + ) + ); // you know that time-complexity thingy? idk much but uh an array has O(n) and a JS Set() has O(1) which should mean using a Set is more performant - const inviteGuildsSet = new Set((listPublicWithInvitesEnabledQuery.all() as TypeOfDefinition[]).map(entry => JSON.parse(entry.guildID))); + const inviteGuildsSet = new Set( + (listPublicWithInvitesEnabledQuery.all() as TypeOfDefinition[]).map( + entry => JSON.parse(entry.guildID) + ) + ); - return (Array.from(publicGuildSet)).map(entry => { + return Array.from(publicGuildSet).map(entry => { const inviteChannel = getSetting(entry, "serverboard", "invite_channel"); return { - guildID: entry, - showInvite: inviteGuildsSet.has(entry), + guildID: entry, + showInvite: inviteGuildsSet.has(entry), inviteChannelId: inviteChannel ? inviteChannel.toString() : null, - } + }; }); } diff --git a/src/utils/embeds/modEmbed.ts b/src/utils/embeds/modEmbed.ts index a8cb8201..dd398467 100644 --- a/src/utils/embeds/modEmbed.ts +++ b/src/utils/embeds/modEmbed.ts @@ -2,7 +2,7 @@ import { EmbedBuilder, type PermissionResolvable, type ChatInputCommandInteraction, - type User + type User, } from "discord.js"; import ms from "ms"; import { genColor } from "../colorGen"; @@ -89,7 +89,9 @@ export async function errorCheck( return await errorEmbed( interaction, `You can't ${action.toLowerCase()} ${name}.`, - `The member has ${highestModPos == highestTargetPos ? "the same" : "a higher"} role position ${highestModPos == highestTargetPos ? "as" : "than"} you.` + `The member has ${ + highestModPos == highestTargetPos ? "the same" : "a higher" + } role position ${highestModPos == highestTargetPos ? "as" : "than"} you.` ); if (ownerError) { @@ -124,7 +126,9 @@ export async function modEmbed( const guild = interaction.guild!; const name = user.displayName; const generalValues = [`**Moderator**: ${interaction.user.displayName}`]; - let author = `• ${previousID ? "Edited a " : ""}${previousID ? dbAction?.toLowerCase() : action}${previousID ? " on" : ""} ${name}`; + let author = `• ${previousID ? "Edited a " : ""}${ + previousID ? dbAction?.toLowerCase() : action + }${previousID ? " on" : ""} ${name}`; reason ? generalValues.push(`**Reason**: ${reason}`) : generalValues.push("*No reason provided*"); if (duration) generalValues.push(`**Duration**: ${ms(ms(duration), { long: true })}`); if (previousID) { @@ -181,11 +185,11 @@ export async function modEmbed( embed .setAuthor({ name: `• You got ${action.toLowerCase()}.`, - iconURL: user.displayAvatarURL() + iconURL: user.displayAvatarURL(), }) .setDescription(generalValues.slice(+!showModerator, generalValues.length).join("\n")) - .setColor(genColor(0)) - ] + .setColor(genColor(0)), + ], }) .catch(() => null); } catch (e) { diff --git a/src/utils/embeds/serverEmbed.ts b/src/utils/embeds/serverEmbed.ts index f6be4a70..30b8fe65 100644 --- a/src/utils/embeds/serverEmbed.ts +++ b/src/utils/embeds/serverEmbed.ts @@ -38,18 +38,18 @@ export async function serverEmbed(options: Options) { text: channels.filter(channel => channel.type == 0 || channel.type == 15 || channel.type == 5) .size, voice: channels.filter(channel => channel.type == 2 || channel.type == 13).size, - categories: channels.filter(channel => channel.type == 4).size + categories: channels.filter(channel => channel.type == 4).size, }; const generalValues = [ `Owned by **${(await guild.fetchOwner()).user.displayName}**`, - `Created on ****` + `Created on ****`, ]; const embed = new EmbedBuilder() .setAuthor({ name: `${pages ? `#${page} • ` : icon ? "• " : ""}${guild.name}`, - iconURL: icon + iconURL: icon, }) .setDescription(guild.description ? guild.description : null) .setFields({ name: "📃 • General", value: generalValues.join("\n") }) @@ -63,28 +63,28 @@ export async function serverEmbed(options: Options) { { name: `🎭 • ${roles.size - 1} ${pluralOrNot("role", roles.size - 1)}`, value: - roles.size == 1 - ? "*None*" - : `${sortedRoles - .slice(0, 5) - .map(role => `<@&${role[0]}>`) - .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}` + roles.size == 1 + ? "*None*" + : `${sortedRoles + .slice(0, 5) + .map(role => `<@&${role[0]}>`) + .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}`, }, { name: `👥 • ${guild.memberCount?.toLocaleString("en-US")} members`, value: [ `**${formattedUserCount}** ${pluralOrNot("user", guild.memberCount - bots.size)}`, - `**${bots.size?.toLocaleString("en-US")}** ${pluralOrNot("bot", bots.size)}` + `**${bots.size?.toLocaleString("en-US")}** ${pluralOrNot("bot", bots.size)}`, ].join("\n"), - inline: true + inline: true, }, { name: `🗨️ • ${channelCount} ${pluralOrNot("channel", channelCount)}`, value: [ `**${channelSizes.text}** text • **${channelSizes.voice}** voice`, - `**${channelSizes.categories}** ${pluralOrNot("category", channelSizes.categories)}` + `**${channelSizes.categories}** ${pluralOrNot("category", channelSizes.categories)}`, ].join("\n"), - inline: true + inline: true, }, { name: `🌟 • ${!boostTier ? "No level" : `Level ${boostTier}`}`, @@ -92,9 +92,9 @@ export async function serverEmbed(options: Options) { `**${boostCount}**${ !boostTier ? "/2" : boostTier == 1 ? "/7" : boostTier == 2 ? "/14" : "" } ${pluralOrNot("boost", boostCount!)}`, - `**${boosters.size}** ${pluralOrNot("booster", boosters.size)}` + `**${boosters.size}** ${pluralOrNot("booster", boosters.size)}`, ].join("\n"), - inline: true + inline: true, } ); @@ -128,13 +128,13 @@ export async function serverEmbed(options: Options) { maxUses: undefined, reason: "Serverboard", temporary: false, - unique: true + unique: true, }); embed.addFields({ name: `🚪 • Join in!`, value: `This server allows you to join from here. ${inviteUrl}`, - inline: true + inline: true, }); } diff --git a/src/utils/humanizeSettings.ts b/src/utils/humanizeSettings.ts index aea524b0..af704f64 100644 --- a/src/utils/humanizeSettings.ts +++ b/src/utils/humanizeSettings.ts @@ -1,18 +1,17 @@ - /** - * Outputs the given settings_string + * Outputs the given settings_string * @param {string} string Settings string, either a key or a value. */ export function humanizeSettings(string: string) { - if (!string) return; - const humanized = string - .trim() - .replaceAll("_", " ") - .replaceAll("true", "***Enabled***") - .replaceAll("false", "***Disabled***") - .replaceAll("(name)", "\`(name)\`") - .replaceAll("(servername)", "\`(servername)\`") - .replaceAll("(count)", "\`(count)\`") + if (!string) return; + const humanized = string + .trim() + .replaceAll("_", " ") + .replaceAll("true", "***Enabled***") + .replaceAll("false", "***Disabled***") + .replaceAll("(name)", "`(name)`") + .replaceAll("(servername)", "`(servername)`") + .replaceAll("(count)", "`(count)`"); - return humanized -} \ No newline at end of file + return humanized; +} From f45beb9b7d28730c05d3f178ca0a74c6395f9932 Mon Sep 17 00:00:00 2001 From: Zaka Date: Fri, 31 Jan 2025 19:02:21 +0100 Subject: [PATCH 13/14] Return of roles param + don't prefer strict comparison --- CONTRIBUTING.md | 1 - src/commands/server.ts | 2 +- src/commands/serverboard.ts | 1 + src/utils/embeds/serverEmbed.ts | 10 +++++++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 36c3a998..2ccc236c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,6 @@ A few, simple guidelines onto how code contributed to Sokora should look like. - Use early returns to avoid nesting. - Avoid curly braces in one-line `if` statements. - Non-nullish assertions are valid when needed. -- Prefer strict comparisons (`===` / `!==`). - Use the cache instead of a new `fetch()` call where possible, to avoid unnecessary usage (e.g., if possible, prefer `guild.members.cache` over `await guild.members.fetch()`). ![PLEASE SUBMIT A PR, NO DIRECT COMMITS](https://user-images.githubusercontent.com/51555391/176925763-cdfd57ba-ae1e-4bf3-85e9-b3ebd30b1d59.png) diff --git a/src/commands/server.ts b/src/commands/server.ts index 594e6ce7..769e0171 100644 --- a/src/commands/server.ts +++ b/src/commands/server.ts @@ -6,6 +6,6 @@ export const data = new SlashCommandBuilder() .setDescription("Shows this server's info."); export async function run(interaction: ChatInputCommandInteraction) { - const embed = await serverEmbed({ guild: interaction.guild! }); + const embed = await serverEmbed({ guild: interaction.guild!, roles: true }); await interaction.reply({ embeds: [embed] }); } diff --git a/src/commands/serverboard.ts b/src/commands/serverboard.ts index afb4fc43..551d1439 100644 --- a/src/commands/serverboard.ts +++ b/src/commands/serverboard.ts @@ -49,6 +49,7 @@ export async function run(interaction: ChatInputCommandInteraction) { }, page: page + 1, pages, + roles: false, }); } diff --git a/src/utils/embeds/serverEmbed.ts b/src/utils/embeds/serverEmbed.ts index 30b8fe65..366a2ce5 100644 --- a/src/utils/embeds/serverEmbed.ts +++ b/src/utils/embeds/serverEmbed.ts @@ -15,6 +15,7 @@ type Options = { show: boolean; channel: string | null; }; + roles?: boolean; page?: number; pages?: number; }; @@ -59,8 +60,8 @@ export async function serverEmbed(options: Options) { const channelCount = channelSizes.text + channelSizes.voice; - embed.addFields( - { + if (options.roles) { + embed.addFields({ name: `🎭 • ${roles.size - 1} ${pluralOrNot("role", roles.size - 1)}`, value: roles.size == 1 @@ -69,7 +70,10 @@ export async function serverEmbed(options: Options) { .slice(0, 5) .map(role => `<@&${role[0]}>`) .join(", ")}${rolesLength > 5 ? ` and **${rolesLength - 5}** more` : ""}`, - }, + }); + } + + embed.addFields( { name: `👥 • ${guild.memberCount?.toLocaleString("en-US")} members`, value: [ From 9010c798df078ebb6f63b15366c90f80937cabb3 Mon Sep 17 00:00:00 2001 From: Zaka Date: Fri, 31 Jan 2025 19:14:41 +0100 Subject: [PATCH 14/14] Simpler Enabled/Disable settings text --- src/utils/humanizeSettings.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/humanizeSettings.ts b/src/utils/humanizeSettings.ts index af704f64..4235f3a7 100644 --- a/src/utils/humanizeSettings.ts +++ b/src/utils/humanizeSettings.ts @@ -7,8 +7,8 @@ export function humanizeSettings(string: string) { const humanized = string .trim() .replaceAll("_", " ") - .replaceAll("true", "***Enabled***") - .replaceAll("false", "***Disabled***") + .replaceAll("true", "Enabled") + .replaceAll("false", "Disabled") .replaceAll("(name)", "`(name)`") .replaceAll("(servername)", "`(servername)`") .replaceAll("(count)", "`(count)`");