diff --git a/commands/fun/sonic.js b/commands/fun/sonic.js index 6ecdaea..d8b361c 100644 --- a/commands/fun/sonic.js +++ b/commands/fun/sonic.js @@ -1,10 +1,10 @@ //import wrap from "../../utils/wrap.js"; import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class SonicCommand extends ImageCommand { params() { - const cleanedMessage = textEncode(this.options.text ?? this.args.join(" ")); + const cleanedMessage = cleanMessage(this.message, this.options.text ?? this.args.join(" ")); return { text: cleanedMessage }; diff --git a/commands/image-editing/caption.js b/commands/image-editing/caption.js index 844a6f6..9f36c0e 100644 --- a/commands/image-editing/caption.js +++ b/commands/image-editing/caption.js @@ -1,10 +1,10 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class CaptionCommand extends ImageCommand { params(url) { const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); - let newCaption = textEncode(newArgs); + let newCaption = cleanMessage(this.message, newArgs); if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.options.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`; return { caption: newCaption, diff --git a/commands/image-editing/caption2.js b/commands/image-editing/caption2.js index 04a9b36..c848680 100644 --- a/commands/image-editing/caption2.js +++ b/commands/image-editing/caption2.js @@ -1,12 +1,12 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; const words = ["me irl", "dank", "follow my second account @esmBot_", "2016", "meme", "wholesome", "reddit", "instagram", "twitter", "facebook", "fortnite", "minecraft", "relatable", "gold", "funny", "template", "hilarious", "memes", "deep fried", "2020", "leafy", "pewdiepie"]; class CaptionTwoCommand extends ImageCommand { params(url) { const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); return { - caption: newArgs && newArgs.trim() ? textEncode(newArgs) : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), + caption: newArgs && newArgs.trim() ? cleanMessage(this.message, newArgs) : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), top: !!this.options.top, font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica" }; diff --git a/commands/image-editing/meme.js b/commands/image-editing/meme.js index b1563ab..67dc8c7 100644 --- a/commands/image-editing/meme.js +++ b/commands/image-editing/meme.js @@ -1,5 +1,5 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class MemeCommand extends ImageCommand { async criteria(text, url) { @@ -15,8 +15,8 @@ class MemeCommand extends ImageCommand { const newArgs = this.options.text ?? this.args.join(" "); const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(? elem.trim()); return { - top: textEncode(this.options.case ? topText : topText.toUpperCase()), - bottom: bottomText ? textEncode(this.options.case ? bottomText : bottomText.toUpperCase()) : "", + top: cleanMessage(this.message, this.options.case ? topText : topText.toUpperCase()), + bottom: bottomText ? cleanMessage(this.message, this.options.case ? bottomText : bottomText.toUpperCase()) : "", font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "impact" }; } diff --git a/commands/image-editing/motivate.js b/commands/image-editing/motivate.js index 2bd0b15..8f6fe4b 100644 --- a/commands/image-editing/motivate.js +++ b/commands/image-editing/motivate.js @@ -1,5 +1,5 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class MotivateCommand extends ImageCommand { async criteria(text, url) { @@ -15,8 +15,8 @@ class MotivateCommand extends ImageCommand { const newArgs = this.options.text ?? this.args.join(" "); const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(? elem.trim()); return { - top: textEncode(topText), - bottom: bottomText ? textEncode(bottomText) : "", + top: cleanMessage(this.message, topText), + bottom: bottomText ? cleanMessage(this.message, bottomText) : "", font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "times" }; } diff --git a/commands/image-editing/snapchat.js b/commands/image-editing/snapchat.js index 12f6d36..72a9832 100644 --- a/commands/image-editing/snapchat.js +++ b/commands/image-editing/snapchat.js @@ -1,12 +1,12 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class SnapchatCommand extends ImageCommand { params(url) { const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); const position = parseFloat(this.options.position); return { - caption: textEncode(newArgs), + caption: cleanMessage(this.message, newArgs), pos: isNaN(position) ? 0.5 : position }; } diff --git a/commands/image-editing/uncanny.js b/commands/image-editing/uncanny.js index 49b2fe3..79a5a16 100644 --- a/commands/image-editing/uncanny.js +++ b/commands/image-editing/uncanny.js @@ -1,5 +1,5 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { random, textEncode } from "../../utils/misc.js"; +import { random, cleanMessage } from "../../utils/misc.js"; import { readdirSync } from "fs"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; @@ -17,8 +17,8 @@ class UncannyCommand extends ImageCommand { let [text1, text2] = newArgs.replaceAll(url, "").split(/(? elem.trim()); if (!text2?.trim()) text2 = name; return { - caption: text1?.trim() ? textEncode(text1) : random(prompts), - caption2: textEncode(text2), + caption: text1?.trim() ? cleanMessage(this.message, text1) : random(prompts), + caption2: cleanMessage(this.message, text2), path: `assets/images/uncanny/${typeof this.options.phase === "string" && names.includes(this.options.phase.toLowerCase()) ? this.options.phase.toLowerCase() : random(names.filter((val) => val !== "goated"))}.png`, font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica" }; diff --git a/commands/image-editing/whisper.js b/commands/image-editing/whisper.js index 5bb1527..aa75320 100644 --- a/commands/image-editing/whisper.js +++ b/commands/image-editing/whisper.js @@ -1,11 +1,11 @@ import ImageCommand from "../../classes/imageCommand.js"; -import { textEncode } from "../../utils/misc.js"; +import { cleanMessage } from "../../utils/misc.js"; class WhisperCommand extends ImageCommand { params(url) { const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); return { - caption: textEncode(newArgs) + caption: cleanMessage(this.message, newArgs) }; } diff --git a/commands/music/host.js b/commands/music/host.js index d6fd84f..0f8af67 100644 --- a/commands/music/host.js +++ b/commands/music/host.js @@ -6,7 +6,7 @@ class HostCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can choose another host!"; const input = this.options.user ?? this.args.join(" "); diff --git a/commands/music/loop.js b/commands/music/loop.js index 03caa82..8d5331e 100644 --- a/commands/music/loop.js +++ b/commands/music/loop.js @@ -6,7 +6,7 @@ class LoopCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id && !this.member.permissions.has("MANAGE_CHANNELS")) return "Only the current voice session host can loop the music!"; const object = this.connection; diff --git a/commands/music/nowplaying.js b/commands/music/nowplaying.js index 64b0a09..b72eaad 100644 --- a/commands/music/nowplaying.js +++ b/commands/music/nowplaying.js @@ -6,7 +6,7 @@ class NowPlayingCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; const player = this.connection.player; if (!player) return "I'm not playing anything!"; diff --git a/commands/music/queue.js b/commands/music/queue.js index 07a73b1..321aefc 100644 --- a/commands/music/queue.js +++ b/commands/music/queue.js @@ -9,7 +9,7 @@ class QueueCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) return "I don't have the `Embed Links` permission!"; const player = this.connection; if (!player) return "I haven't completely connected yet!"; diff --git a/commands/music/remove.js b/commands/music/remove.js index 1db4b2b..1140651 100644 --- a/commands/music/remove.js +++ b/commands/music/remove.js @@ -6,7 +6,7 @@ class RemoveCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can remove songs from the queue!"; const pos = parseInt(this.options.position ?? this.args[0]); diff --git a/commands/music/seek.js b/commands/music/seek.js index a3273fa..89b64e4 100644 --- a/commands/music/seek.js +++ b/commands/music/seek.js @@ -5,7 +5,7 @@ class SeekCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id) return "Only the current voice session host can seek the music!"; const player = this.connection.player; diff --git a/commands/music/shuffle.js b/commands/music/shuffle.js index 1f7bf6d..661c54b 100644 --- a/commands/music/shuffle.js +++ b/commands/music/shuffle.js @@ -6,7 +6,7 @@ class ShuffleCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id) return "Only the current voice session host can shuffle the music!"; const object = this.connection; diff --git a/commands/music/skip.js b/commands/music/skip.js index 16d6a74..a5e8be0 100644 --- a/commands/music/skip.js +++ b/commands/music/skip.js @@ -6,7 +6,7 @@ class SkipCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; const player = this.connection; if (!player) return "I haven't completely connected yet!"; if (player.host !== this.author.id && !this.member.permissions.has("MANAGE_CHANNELS")) { diff --git a/commands/music/stop.js b/commands/music/stop.js index 8860137..98e5f81 100644 --- a/commands/music/stop.js +++ b/commands/music/stop.js @@ -6,7 +6,7 @@ class StopCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) { await manager.getNode().leaveChannel(this.guild.id); this.success = true; diff --git a/commands/music/toggle.js b/commands/music/toggle.js index 527d42c..2949f8e 100644 --- a/commands/music/toggle.js +++ b/commands/music/toggle.js @@ -5,7 +5,7 @@ class ToggleCommand extends MusicCommand { this.success = false; if (!this.guild) return "This command only works in servers!"; if (!this.member.voiceState) return "You need to be in a voice channel first!"; - if (!this.guild.members.get(this.client.user.id).voiceState) return "I'm not in a voice channel!"; + if (!this.guild.voiceStates.has(this.client.user.id)) return "I'm not in a voice channel!"; if (!this.connection) return "I haven't completely connected yet!"; if (this.connection.host !== this.author.id && !this.member.permissions.has("MANAGE_CHANNELS")) return "Only the current voice session host can pause/resume the music!"; const player = this.connection.player; diff --git a/events/messageCreate.js b/events/messageCreate.js index da79613..a7f6cc0 100644 --- a/events/messageCreate.js +++ b/events/messageCreate.js @@ -2,10 +2,12 @@ import database from "../utils/database.js"; import { log, error as _error } from "../utils/logger.js"; import { prefixCache, aliases, disabledCache, disabledCmdCache, commands } from "../utils/collections.js"; import parseCommand from "../utils/parseCommand.js"; -import { clean, cleanMessage } from "../utils/misc.js"; +import { clean } from "../utils/misc.js"; import { upload } from "../utils/tempimages.js"; import { ThreadChannel } from "oceanic.js"; +let mentionRegex; + // run when someone sends a message export default async (client, message) => { // ignore other bots @@ -22,77 +24,63 @@ export default async (client, message) => { } if (message.guildID && !permChannel.permissionsOf(client.user.id.toString()).has("SEND_MESSAGES")) return; - let prefixCandidate; + if (!mentionRegex) mentionRegex = new RegExp(`^<@!?${client.user.id}> `); + let guildDB; - if (message.guildID) { + let text; + const mentionResult = message.content.match(mentionRegex); + if (mentionResult) { + text = message.content.substring(mentionResult[0].length).trim(); + } else if (message.guildID) { const cachedPrefix = prefixCache.get(message.guildID); - if (cachedPrefix) { - prefixCandidate = cachedPrefix; + if (cachedPrefix && message.content.startsWith(cachedPrefix)) { + text = message.content.substring(cachedPrefix.length).trim(); } else { guildDB = await database.getGuild(message.guildID); if (!guildDB) { guildDB = await database.fixGuild(message.guildID); } - prefixCandidate = guildDB.prefix; - prefixCache.set(message.guildID, guildDB.prefix); - } - } - - let prefix; - let isMention = false; - if (message.guildID) { - const user = message.guild.members.get(client.user.id); - if (message.content.startsWith(user.mention)) { - prefix = `${user.mention} `; - isMention = true; - } else if (message.content.startsWith(`<@${client.user.id}>`)) { // workaround for member.mention not accounting for both mention types - prefix = `<@${client.user.id}> `; - isMention = true; - } else { - prefix = prefixCandidate; + if (message.content.startsWith(guildDB.prefix)) { + text = message.content.substring(guildDB.prefix.length).trim(); + prefixCache.set(message.guildID, guildDB.prefix); + } else { + return; + } } + } else if (message.content.startsWith(process.env.PREFIX)) { + text = message.content.substring(process.env.PREFIX.length).trim(); } else { - prefix = process.env.PREFIX; + return; } - // ignore other stuff - if (!message.content.startsWith(prefix)) return; - // separate commands and args - const replace = isMention ? `@${(message.guild ? message.guild.members.get(client.user.id).nick : client.user.username) ?? client.user.username} ` : prefix; - const content = cleanMessage(message).substring(replace.length).trim(); - const rawContent = message.content.substring(prefix.length).trim(); - const preArgs = content.split(/\s+/g); - preArgs.shift(); - const command = rawContent.split(/\s+/g).shift().toLowerCase(); - const parsed = parseCommand(preArgs); + const preArgs = text.split(/\s+/g); + const command = preArgs.shift().toLowerCase(); const aliased = aliases.get(command); - // don't run if message is in a disabled channel - if (message.guildID) { - const disabled = disabledCache.get(message.guildID); - if (disabled) { - if (disabled.includes(message.channelID) && command != "channel") return; - } else { - guildDB = await database.getGuild(message.guildID); - disabledCache.set(message.guildID, guildDB.disabled); - if (guildDB.disabled.includes(message.channelID) && command !== "channel") return; - } - - const disabledCmds = disabledCmdCache.get(message.guildID); - if (disabledCmds) { - if (disabledCmds.includes(aliased ?? command)) return; - } else { - guildDB = await database.getGuild(message.guildID); - disabledCmdCache.set(message.guildID, guildDB.disabled_commands ?? guildDB.disabledCommands); - if ((guildDB.disabled_commands ?? guildDB.disabledCommands).includes(aliased ?? command)) return; - } - } - // check if command exists and if it's enabled const cmd = commands.get(aliased ?? command); if (!cmd) return; + // don't run if message is in a disabled channel + if (message.guildID) { + let disabled = disabledCache.get(message.guildID); + if (!disabled) { + if (!guildDB) guildDB = await database.getGuild(message.guildID); + disabledCache.set(message.guildID, guildDB.disabled); + disabled = guildDB.disabled; + } + if (disabled.includes(message.channelID) && command != "channel") return; + + let disabledCmds = disabledCmdCache.get(message.guildID); + if (!disabledCmds) { + if (!guildDB) guildDB = await database.getGuild(message.guildID); + disabledCmdCache.set(message.guildID, guildDB.disabled_commands ?? guildDB.disabledCommands); + disabledCmds = guildDB.disabled_commands ?? guildDB.disabledCommands; + } + if (disabledCmds.includes(aliased ?? command)) return; + } + // block certain commands from running in DMs if (!cmd.directAllowed && !message.guildID) return; @@ -110,10 +98,12 @@ export default async (client, message) => { } }; try { + // parse args + const parsed = parseCommand(preArgs); await database.addCount(aliases.get(command) ?? command); const startTime = new Date(); // eslint-disable-next-line no-unused-vars - const commandClass = new cmd(client, { type: "classic", message, args: parsed._, content: message.content.substring(prefix.length).trim().replace(command, "").trim(), specialArgs: (({ _, ...o }) => o)(parsed) }); // we also provide the message content as a parameter for cases where we need more accuracy + const commandClass = new cmd(client, { type: "classic", message, args: parsed._, content: text.replace(command, "").trim(), specialArgs: (({ _, ...o }) => o)(parsed) }); // we also provide the message content as a parameter for cases where we need more accuracy const result = await commandClass.run(); const endTime = new Date(); if ((endTime - startTime) >= 180000) reference.allowedMentions.repliedUser = true; diff --git a/utils/database/postgresql.js b/utils/database/postgresql.js index 47aa918..afccb03 100644 --- a/utils/database/postgresql.js +++ b/utils/database/postgresql.js @@ -147,11 +147,11 @@ export async function addGuild(guild) { const query = await this.getGuild(guild); if (query) return query; try { - await sql`INSERT INTO guilds ${sql({ guild_id: guild.id, prefix: process.env.PREFIX, disabled: [], disabled_commands: [] })}`; + await sql`INSERT INTO guilds ${sql({ guild_id: guild, prefix: process.env.PREFIX, disabled: [], disabled_commands: [] })}`; } catch (e) { - logger.error(`Failed to register guild ${guild.id}: ${e}`); + logger.error(`Failed to register guild ${guild}: ${e}`); } - return await this.getGuild(guild.id); + return await this.getGuild(guild); } export async function fixGuild(guild) { diff --git a/utils/misc.js b/utils/misc.js index 7ce83a2..08fe77b 100644 --- a/utils/misc.js +++ b/utils/misc.js @@ -113,34 +113,28 @@ export function getServers(bot) { } // copied from eris -export function cleanMessage(message) { - let cleanContent = message.content && message.content.replace(//g, "$1") || ""; +export function cleanMessage(message, content) { + let cleanContent = content && content.replace(//g, "$1") || ""; let authorName = message.author.username; - if (message.guildID) { - const member = message.guild.members.get(message.author.id); - if (member && member.nick) { - authorName = member.nick; - } + if (message.member?.nick) { + authorName = message.member.nick; } - cleanContent = cleanContent.replace(new RegExp(`<@!?${message.author.id}>`, "g"), `@\u200b${authorName}`); + cleanContent = cleanContent.replace(new RegExp(`<@!?${message.author.id}>`, "g"), `@${authorName}`); if (message.mentions) { for (const mention of message.mentions.members) { - if (message.guildID) { - const member = message.guild.members.get(mention.id); - if (member && member.nick) { - cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@\u200b${member.nick}`); - } + if (mention.nick) { + cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@${mention.nick}`); } - cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@\u200b${mention.username}`); + cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@${mention.username}`); } if (message.guildID && message.mentions.roles) { for (const roleID of message.mentions.roles) { const role = message.guild.roles.get(roleID); const roleName = role ? role.name : "deleted-role"; - cleanContent = cleanContent.replace(new RegExp(`<@&${roleID}>`, "g"), `@\u200b${roleName}`); + cleanContent = cleanContent.replace(new RegExp(`<@&${roleID}>`, "g"), `@${roleName}`); } } @@ -152,5 +146,5 @@ export function cleanMessage(message) { } } - return cleanContent.replace(/@everyone/g, "@\u200beveryone").replace(/@here/g, "@\u200bhere"); + return textEncode(cleanContent); } \ No newline at end of file