Refactor message prefix handling, lessen reliance on member caching, fix adding guilds in postgresql

This commit is contained in:
Essem 2022-10-23 20:27:32 -05:00
parent a78df6fce7
commit 3662a2e51d
No known key found for this signature in database
GPG Key ID: 7D497397CC3A2A8C
21 changed files with 87 additions and 103 deletions

View File

@ -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
};

View File

@ -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,

View File

@ -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"
};

View File

@ -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(/(?<!\\),/).map(elem => 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"
};
}

View File

@ -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(/(?<!\\),/).map(elem => 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"
};
}

View File

@ -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
};
}

View File

@ -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(/(?<!\\),/).map(elem => 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"
};

View File

@ -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)
};
}

View File

@ -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(" ");

View File

@ -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;

View File

@ -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!";

View File

@ -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!";

View File

@ -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]);

View File

@ -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;

View File

@ -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;

View File

@ -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")) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -113,34 +113,28 @@ export function getServers(bot) {
}
// copied from eris
export function cleanMessage(message) {
let cleanContent = message.content && message.content.replace(/<a?(:\w+:)[0-9]+>/g, "$1") || "";
export function cleanMessage(message, content) {
let cleanContent = content && content.replace(/<a?(:\w+:)[0-9]+>/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);
}