Upgrade to Discord.JS v13.2.0

Co-Authored-By: Dmytro Meleshko <dmytro.meleshko@gmail.com>
This commit is contained in:
Alyxia Sother 2021-10-29 14:52:46 +02:00
parent 36bc488757
commit fbb687d3d6
No known key found for this signature in database
GPG Key ID: 355968D14144B739
41 changed files with 1491 additions and 738 deletions

1075
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,20 +12,19 @@
"dev-instance": "rimraf dist && tsc && node . dev", "dev-instance": "rimraf dist && tsc && node . dev",
"test": "jest", "test": "jest",
"format": "prettier --write **/*", "format": "prettier --write **/*",
"postinstall": "node patch.js && husky install" "postinstall": "husky install"
}, },
"dependencies": { "dependencies": {
"canvas": "^2.7.0", "canvas": "^2.7.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"discord.js": "github:discordjs/discord.js", "discord.js": "^13.2.0",
"discord.js-lavalink-lib": "^0.1.8",
"figlet": "^1.5.0", "figlet": "^1.5.0",
"glob": "^7.1.6", "glob": "^7.1.6",
"inquirer": "^7.3.3", "inquirer": "^7.3.3",
"mathjs": "^9.3.0", "mathjs": "^9.3.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"ms": "^2.1.3", "ms": "^2.1.3",
"onion-lasers": "^1.2.0-unstable.0", "onion-lasers": "npm:onion-lasers-v13@^2.0.0",
"pet-pet-gif": "^1.0.8", "pet-pet-gif": "^1.0.8",
"relevant-urban": "^2.0.0", "relevant-urban": "^2.0.0",
"translate-google": "^1.4.3", "translate-google": "^1.4.3",
@ -46,7 +45,7 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^26.4.4", "ts-jest": "^26.4.4",
"tsc-watch": "^4.2.9", "tsc-watch": "^4.2.9",
"typescript": "^4.2.4" "typescript": "^4.4.4"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "^2.1.2" "fsevents": "^2.1.2"

View File

@ -1,23 +0,0 @@
// This is a nightmarishly bad way to handle module patches... but oh well, it's on the unstable branch for a reason.
const fs = require("fs");
const DECLARATION_FILE = "node_modules/discord.js/typings/index.d.ts";
fs.readFile(DECLARATION_FILE, "utf-8", (err, data) => {
if (err) console.error(err);
else {
const declaration = data.split(/\r?\n/);
// "discord-api-types/v8" is apparently not found so just ignore it to get the typings to work.
for (let i = 0; i < declaration.length; i++) {
const line = declaration[i];
if (line.includes("@ts-ignore")) {
break;
} else if (line.includes("discord-api-types/v8")) {
declaration.splice(i, 0, "// @ts-ignore");
fs.writeFile(DECLARATION_FILE, declaration.join("\n"), () => {});
break;
}
}
}
});

View File

@ -1,5 +1,6 @@
import {NamedCommand, RestCommand} from "onion-lasers"; import {NamedCommand, RestCommand} from "onion-lasers";
import figlet from "figlet"; import figlet from "figlet";
import {Util} from "discord.js";
export default new NamedCommand({ export default new NamedCommand({
description: "Generates a figlet of your input.", description: "Generates a figlet of your input.",
@ -7,12 +8,11 @@ export default new NamedCommand({
any: new RestCommand({ any: new RestCommand({
async run({send, combined}) { async run({send, combined}) {
return send( return send(
figlet.textSync(combined, { `\`\`\`\n${Util.cleanCodeBlockContent(
horizontalLayout: "full" figlet.textSync(combined, {
}), horizontalLayout: "full"
{ })
code: true )}\n\`\`\``
}
); );
} }
}) })

View File

@ -3,12 +3,12 @@ import {NamedCommand} from "onion-lasers";
export default new NamedCommand({ export default new NamedCommand({
description: "Insult TravBot! >:D", description: "Insult TravBot! >:D",
async run({send, channel, author}) { async run({send, channel, author}) {
channel.startTyping(); channel.sendTyping();
setTimeout(() => { setTimeout(() => {
send( send(
`${author} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.` `${author} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.`
); );
channel.stopTyping(); channel.sendTyping();
}, 60000); }, 60000);
} }
}); });

View File

@ -39,9 +39,12 @@ export const BetCommand = new NamedCommand({
// handle invalid amount // handle invalid amount
if (amount <= 0) return send("You must bet at least one Mon!"); if (amount <= 0) return send("You must bet at least one Mon!");
else if (sender.money < amount) else if (sender.money < amount)
return send("You don't have enough Mons for that.", getMoneyEmbed(author)); return send({content: "You don't have enough Mons for that.", embeds: [getMoneyEmbed(author)]});
else if (receiver.money < amount) else if (receiver.money < amount)
return send("They don't have enough Mons for that.", getMoneyEmbed(target)); return send({
content: "They don't have enough Mons for that.",
embeds: [getMoneyEmbed(target)]
});
return send("How long until the bet ends?"); return send("How long until the bet ends?");
} else return; } else return;
@ -67,9 +70,15 @@ export const BetCommand = new NamedCommand({
// handle invalid amount // handle invalid amount
if (amount <= 0) return send("You must bet at least one Mon!"); if (amount <= 0) return send("You must bet at least one Mon!");
else if (sender.money < amount) else if (sender.money < amount)
return send("You don't have enough Mons for that.", getMoneyEmbed(author)); return send({
content: "You don't have enough Mons for that.",
embeds: [getMoneyEmbed(author)]
});
else if (receiver.money < amount) else if (receiver.money < amount)
return send("They don't have enough Mons for that.", getMoneyEmbed(target)); return send({
content: "They don't have enough Mons for that.",
embeds: [getMoneyEmbed(target)]
});
// handle invalid duration // handle invalid duration
if (duration <= 0) return send("Invalid bet duration"); if (duration <= 0) return send("Invalid bet duration");
@ -107,7 +116,7 @@ export const BetCommand = new NamedCommand({
); );
// Wait for the duration of the bet. // Wait for the duration of the bet.
return client.setTimeout(async () => { return setTimeout(async () => {
// In debug mode, saving the storage will break the references, so you have to redeclare sender and receiver for it to actually save. // In debug mode, saving the storage will break the references, so you have to redeclare sender and receiver for it to actually save.
const sender = Storage.getUser(author.id); const sender = Storage.getUser(author.id);
const receiver = Storage.getUser(target.id); const receiver = Storage.getUser(target.id);

View File

@ -16,27 +16,26 @@ export const DailyCommand = new NamedCommand({
user.lastReceived = now; user.lastReceived = now;
Storage.save(); Storage.save();
send({ send({
embed: { embeds: [
title: "Daily Reward", {
description: "You received 1 Mon!", title: "Daily Reward",
color: ECO_EMBED_COLOR, description: "You received 1 Mon!",
fields: [ color: ECO_EMBED_COLOR
{ }
name: "New balance:", ]
value: pluralise(user.money, "Mon", "s")
}
]
}
}); });
} else } else
send({ send({
embed: { embeds: [
title: "Daily Reward", {
description: `It's too soon to pick up your daily Mons. Try again at <t:${Math.floor( title: "Daily Reward",
(user.lastReceived + 79200000) / 1000 description: `It's too soon to pick up your daily Mons. You have about ${(
)}:t>.`, (user.lastReceived + 79200000 - now) /
color: ECO_EMBED_COLOR 3600000
} ).toFixed(1)} hours to go.`,
color: ECO_EMBED_COLOR
}
]
}); });
} }
} }
@ -55,25 +54,27 @@ export const GuildCommand = new NamedCommand({
} }
send({ send({
embed: { embeds: [
title: `The Bank of ${guild!.name}`, {
color: ECO_EMBED_COLOR, title: `The Bank of ${guild!.name}`,
fields: [ color: ECO_EMBED_COLOR,
{ fields: [
name: "Accounts", {
value: Object.keys(users).length, name: "Accounts",
inline: true value: Object.keys(users).length.toString(),
}, inline: true
{ },
name: "Total Mons", {
value: totalAmount, name: "Total Mons",
inline: true value: totalAmount.toString(),
inline: true
}
],
thumbnail: {
url: guild?.iconURL() ?? ""
} }
],
thumbnail: {
url: guild?.iconURL() ?? ""
} }
} ]
}); });
} }
} }
@ -100,14 +101,16 @@ export const LeaderboardCommand = new NamedCommand({
} }
send({ send({
embed: { embeds: [
title: "Top 10 Richest Players", {
color: ECO_EMBED_COLOR, title: "Top 10 Richest Players",
fields: fields, color: ECO_EMBED_COLOR,
thumbnail: { fields: fields,
url: guild?.iconURL() ?? "" thumbnail: {
url: guild?.iconURL() ?? ""
}
} }
} ]
}); });
} }
} }
@ -130,7 +133,7 @@ export const PayCommand = new NamedCommand({
if (amount <= 0) return send("You must send at least one Mon!"); if (amount <= 0) return send("You must send at least one Mon!");
else if (sender.money < amount) else if (sender.money < amount)
return send("You don't have enough Mons for that.", getMoneyEmbed(author)); return send({content: "You don't have enough Mons for that.", embeds: [getMoneyEmbed(author)]});
else if (target.id === author.id) return send("You can't send Mons to yourself!"); else if (target.id === author.id) return send("You can't send Mons to yourself!");
else if (target.bot && !IS_DEV_MODE) return send("You can't send Mons to a bot!"); else if (target.bot && !IS_DEV_MODE) return send("You can't send Mons to a bot!");
@ -157,7 +160,7 @@ export const PayCommand = new NamedCommand({
if (amount <= 0) return send("You must send at least one Mon!"); if (amount <= 0) return send("You must send at least one Mon!");
else if (sender.money < amount) else if (sender.money < amount)
return send("You don't have enough Mons to do that!", getMoneyEmbed(author)); return send({content: "You don't have enough Mons to do that!", embeds: [getMoneyEmbed(author)]});
else if (!guild) else if (!guild)
return send("You have to use this in a server if you want to send Mons with a username!"); return send("You have to use this in a server if you want to send Mons with a username!");
@ -168,17 +171,20 @@ export const PayCommand = new NamedCommand({
else if (user.bot && !IS_DEV_MODE) return send("You can't send Mons to a bot!"); else if (user.bot && !IS_DEV_MODE) return send("You can't send Mons to a bot!");
const confirmed = await confirm( const confirmed = await confirm(
await send(`Are you sure you want to send ${pluralise(amount, "Mon", "s")} to this person?`, { await send({
embed: { content: `Are you sure you want to send ${pluralise(amount, "Mon", "s")} to this person?`,
color: ECO_EMBED_COLOR, embeds: [
author: { {
name: user.tag, color: ECO_EMBED_COLOR,
icon_url: user.displayAvatarURL({ author: {
format: "png", name: user.tag,
dynamic: true icon_url: user.displayAvatarURL({
}) format: "png",
dynamic: true
})
}
} }
} ]
}), }),
author.id author.id
); );

View File

@ -21,7 +21,7 @@ export const MondayCommand = new NamedCommand({
user.money++; user.money++;
user.lastMonday = now.getTime(); user.lastMonday = now.getTime();
Storage.save(); Storage.save();
send("It is **Mon**day, my dudes.", getMoneyEmbed(author)); send({content: "It is **Mon**day, my dudes.", embeds: [getMoneyEmbed(author)]});
} else send("You've already claimed your **Mon**day reward for this week."); } else send("You've already claimed your **Mon**day reward for this week.");
} else { } else {
const weekdayName = WEEKDAY[weekday]; const weekdayName = WEEKDAY[weekday];
@ -47,7 +47,7 @@ export const AwardCommand = new NamedCommand({
const user = Storage.getUser(target.id); const user = Storage.getUser(target.id);
user.money++; user.money++;
Storage.save(); Storage.save();
send(`1 Mon given to ${target.username}.`, getMoneyEmbed(target)); send({content: `1 Mon given to ${target.username}.`, embeds: [getMoneyEmbed(target)]});
} else { } else {
send("This command is restricted to the bean."); send("This command is restricted to the bean.");
} }
@ -62,7 +62,10 @@ export const AwardCommand = new NamedCommand({
const user = Storage.getUser(target.id); const user = Storage.getUser(target.id);
user.money += amount; user.money += amount;
Storage.save(); Storage.save();
send(`${pluralise(amount, "Mon", "s")} given to ${target.username}.`, getMoneyEmbed(target)); send({
content: `${pluralise(amount, "Mon", "s")} given to ${target.username}.`,
embeds: [getMoneyEmbed(target)]
});
} else { } else {
send("You need to enter a number greater than 0."); send("You need to enter a number greater than 0.");
} }

View File

@ -52,7 +52,8 @@ export const ShopItems: ShopItem[] = [
description: "Buys what is technically a laser bridge.", description: "Buys what is technically a laser bridge.",
usage: "laser bridge", usage: "laser bridge",
run(message) { run(message) {
message.channel.send(random(lines), { message.channel.send({
content: random(lines),
files: [ files: [
{ {
attachment: attachment:

View File

@ -3,13 +3,13 @@ import {pluralise, split} from "../../../lib";
import {Storage, getPrefix} from "../../../structures"; import {Storage, getPrefix} from "../../../structures";
import {isAuthorized, ECO_EMBED_COLOR} from "./eco-utils"; import {isAuthorized, ECO_EMBED_COLOR} from "./eco-utils";
import {ShopItems, ShopItem} from "./eco-shop-items"; import {ShopItems, ShopItem} from "./eco-shop-items";
import {EmbedField} from "discord.js"; import {EmbedField, MessageEmbedOptions} from "discord.js";
export const ShopCommand = new NamedCommand({ export const ShopCommand = new NamedCommand({
description: "Displays the list of items you can buy in the shop.", description: "Displays the list of items you can buy in the shop.",
async run({send, guild, channel, author}) { async run({send, guild, channel, author}) {
if (isAuthorized(guild, channel)) { if (isAuthorized(guild, channel)) {
function getShopEmbed(selection: ShopItem[], title: string) { function getShopEmbed(selection: ShopItem[], title: string): MessageEmbedOptions {
const fields: EmbedField[] = []; const fields: EmbedField[] = [];
for (const item of selection) for (const item of selection)
@ -20,13 +20,11 @@ export const ShopCommand = new NamedCommand({
}); });
return { return {
embed: { color: ECO_EMBED_COLOR,
color: ECO_EMBED_COLOR, title: title,
title: title, fields: fields,
fields: fields, footer: {
footer: { text: "Mon Shop | TravBot Services"
text: "Mon Shop | TravBot Services"
}
} }
}; };
} }
@ -35,10 +33,14 @@ export const ShopCommand = new NamedCommand({
const pageAmount = shopPages.length; const pageAmount = shopPages.length;
paginate(send, author.id, pageAmount, (page, hasMultiplePages) => { paginate(send, author.id, pageAmount, (page, hasMultiplePages) => {
return getShopEmbed( return {
shopPages[page], embeds: [
hasMultiplePages ? `Shop (Page ${page + 1} of ${pageAmount})` : "Shop" getShopEmbed(
); shopPages[page],
hasMultiplePages ? `Shop (Page ${page + 1} of ${pageAmount})` : "Shop"
)
]
};
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import {pluralise} from "../../../lib"; import {pluralise} from "../../../lib";
import {Storage} from "../../../structures"; import {Storage} from "../../../structures";
import {User, Guild, TextChannel, DMChannel, NewsChannel} from "discord.js"; import {User, Guild, TextChannel, DMChannel, NewsChannel, Channel, TextBasedChannels} from "discord.js";
export const ECO_EMBED_COLOR = 0xf1c40f; export const ECO_EMBED_COLOR = 0xf1c40f;
@ -61,18 +61,10 @@ export function getSendEmbed(sender: User, receiver: User, amount: number): obje
}; };
} }
export function isAuthorized(guild: Guild | null, channel: TextChannel | DMChannel | NewsChannel): boolean { export function isAuthorized(guild: Guild | null, channel: TextBasedChannels): boolean {
if (IS_DEV_MODE) { if ((guild?.id === "637512823676600330" && channel?.id === "669464416420364288") || IS_DEV_MODE) return true;
return true; else {
} channel.send("Sorry, this command can only be used in Monika's emote server. (#mon-stocks)");
if (guild?.id !== "637512823676600330") {
channel.send("Sorry, this command can only be used in Monika's emote server.");
return false; return false;
} else if (channel?.id !== "669464416420364288") {
channel.send("Sorry, this command can only be used in <#669464416420364288>.");
return false;
} else {
return true;
} }
} }

View File

@ -10,11 +10,11 @@ export default new NamedCommand({
const attachment = message.attachments.first()!; const attachment = message.attachments.first()!;
const gif = await petPetGif(attachment.url); const gif = await petPetGif(attachment.url);
const file = new MessageAttachment(gif, "pat.gif"); const file = new MessageAttachment(gif, "pat.gif");
send(file); send({attachments: [file]});
} else { } else {
const gif = await petPetGif(author.displayAvatarURL({format: "png"})); const gif = await petPetGif(author.displayAvatarURL({format: "png"}));
const file = new MessageAttachment(gif, "pat.gif"); const file = new MessageAttachment(gif, "pat.gif");
send(file); send({attachments: [file]});
} }
}, },
id: "user", id: "user",
@ -24,7 +24,7 @@ export default new NamedCommand({
const user: User = args[0]; const user: User = args[0];
const gif = await petPetGif(user.displayAvatarURL({format: "png"})); const gif = await petPetGif(user.displayAvatarURL({format: "png"}));
const file = new MessageAttachment(gif, "pat.gif"); const file = new MessageAttachment(gif, "pat.gif");
send(file); send({attachments: [file]});
} }
}), }),
any: new RestCommand({ any: new RestCommand({
@ -36,7 +36,7 @@ export default new NamedCommand({
else { else {
const gif = await petPetGif(user.displayAvatarURL({format: "png"})); const gif = await petPetGif(user.displayAvatarURL({format: "png"}));
const file = new MessageAttachment(gif, "pat.gif"); const file = new MessageAttachment(gif, "pat.gif");
send(file); send({attachments: [file]});
} }
} }
}) })

View File

@ -33,26 +33,35 @@ async function execPoll(send: SendFunction, message: Message, user: User, questi
dynamic: true, dynamic: true,
size: 2048 size: 2048
}) || user.defaultAvatarURL; }) || user.defaultAvatarURL;
const msg = await send( const msg = await send({
new MessageEmbed() embeds: [
.setAuthor(`Poll created by ${message.author.username}`, icon) new MessageEmbed()
.setColor(0xffffff) .setAuthor(`Poll created by ${message.author.username}`, icon)
.setFooter("React to vote.") .setColor(0xffffff)
.setDescription(question) .setFooter("React to vote.")
); .setDescription(question)
]
});
const results = await poll(msg, [AGREE, DISAGREE], duration); const results = await poll(msg, [AGREE, DISAGREE], duration);
send( send({
new MessageEmbed() embeds: [
.setAuthor(`The results of ${message.author.username}'s poll:`, icon) new MessageEmbed()
.setTitle(question) .setAuthor(`The results of ${message.author.username}'s poll:`, icon)
.setDescription( .setTitle(question)
`${AGREE} ${pluralise( .setDescription(
results[AGREE], `${AGREE} ${pluralise(
"", results[AGREE],
"people who agree", "",
"person who agrees" "people who agree",
)}\n${DISAGREE} ${pluralise(results[DISAGREE], "", "people who disagree", "person who disagrees")}` "person who agrees"
) )}\n${DISAGREE} ${pluralise(
); results[DISAGREE],
"",
"people who disagree",
"person who disagrees"
)}`
)
]
});
msg.delete(); msg.delete();
} }

View File

@ -6,15 +6,17 @@ export default new NamedCommand({
usage: "[number from 1 to 9]", usage: "[number from 1 to 9]",
async run({send}) { async run({send}) {
send({ send({
embed: { embeds: [
title: "Ravioli ravioli...", {
image: { title: "Ravioli ravioli...",
url: `https://raw.githubusercontent.com/keanuplayz/TravBot/master/assets/ravi${Random.int( image: {
1, url: `https://raw.githubusercontent.com/keanuplayz/TravBot/master/assets/ravi${Random.int(
10 1,
)}.png` 10
)}.png`
}
} }
} ]
}); });
}, },
number: new Command({ number: new Command({
@ -23,12 +25,14 @@ export default new NamedCommand({
if (arg >= 1 && arg <= 9) { if (arg >= 1 && arg <= 9) {
send({ send({
embed: { embeds: [
title: "Ravioli ravioli...", {
image: { title: "Ravioli ravioli...",
url: `https://raw.githubusercontent.com/keanuplayz/TravBot/master/assets/ravi${arg}.png` image: {
url: `https://raw.githubusercontent.com/keanuplayz/TravBot/master/assets/ravi${arg}.png`
}
} }
} ]
}); });
} else { } else {
send("Please provide a number between 1 and 9."); send("Please provide a number between 1 and 9.");

View File

@ -36,25 +36,25 @@ export default new NamedCommand({
usage: "([text])", usage: "([text])",
async run({send, author}) { async run({send, author}) {
const msg = await send(transform(phrase)); const msg = await send(transform(phrase));
msg.createReactionCollector( msg.createReactionCollector({
(reaction, user) => { filter: (reaction, user) => {
if (user.id === author.id && reaction.emoji.name === "❌") msg.delete(); if (user.id === author.id && reaction.emoji.name === "❌") msg.delete();
return false; return false;
}, },
{time: 60000} time: 60000
); });
}, },
any: new RestCommand({ any: new RestCommand({
async run({send, author, combined}) { async run({send, author, combined}) {
phrase = combined; phrase = combined;
const msg = await send(transform(phrase)); const msg = await send(transform(phrase));
msg.createReactionCollector( msg.createReactionCollector({
(reaction, user) => { filter: (reaction, user) => {
if (user.id === author.id && reaction.emoji.name === "❌") msg.delete(); if (user.id === author.id && reaction.emoji.name === "❌") msg.delete();
return false; return false;
}, },
{time: 60000} time: 60000
); });
} }
}) })
}); });

View File

@ -21,7 +21,7 @@ export default new NamedCommand({
if (res.tags && res.tags.length > 0 && res.tags.join(" ").length < 1024) if (res.tags && res.tags.length > 0 && res.tags.join(" ").length < 1024)
embed.addField("Tags", res.tags.join(", "), true); embed.addField("Tags", res.tags.join(", "), true);
send(embed); send({embeds: [embed]});
}) })
.catch(() => { .catch(() => {
send("Sorry, that word was not found."); send("Sorry, that word was not found.");

View File

@ -29,7 +29,7 @@ export default new NamedCommand({
.addField("Winds", current.winddisplay, true) .addField("Winds", current.winddisplay, true)
.addField("Humidity", `${current.humidity}%`, true); .addField("Humidity", `${current.humidity}%`, true);
return send({ return send({
embed embeds: [embed]
}); });
} }
); );

View File

@ -57,7 +57,7 @@ export default new NamedCommand({
const id = author.id; const id = author.id;
if (id in registry) { if (id in registry) {
send(`${author} ${registry[id]}`, {allowedMentions: {parse: []}}); send({content: `${author} ${registry[id]}`, allowedMentions: {parse: []}});
} else { } else {
send("You haven't been added to the registry yet!"); send("You haven't been added to the registry yet!");
} }
@ -69,9 +69,9 @@ export default new NamedCommand({
const id = user.id; const id = user.id;
if (id in registry) { if (id in registry) {
send(`${user} ${registry[id]}`, {allowedMentions: {parse: []}}); send({content: `${user} ${registry[id]}`, allowedMentions: {parse: []}});
} else { } else {
send(`${user} hasn't been added to the registry yet!`, {allowedMentions: {parse: []}}); send({content: `${user} hasn't been added to the registry yet!`, allowedMentions: {parse: []}});
} }
} }
}), }),
@ -81,9 +81,9 @@ export default new NamedCommand({
if (typeof user !== "string") { if (typeof user !== "string") {
if (user.id in registry) { if (user.id in registry) {
send(`${user} ${registry[user.id]}`, {allowedMentions: {parse: []}}); send({content: `${user} ${registry[user.id]}`, allowedMentions: {parse: []}});
} else { } else {
send(`${user} hasn't been added to the registry yet!`, {allowedMentions: {parse: []}}); send({content: `${user} hasn't been added to the registry yet!`, allowedMentions: {parse: []}});
} }
} else { } else {
send(user); send(user);

View File

@ -1,9 +1,7 @@
import {Command, NamedCommand, getPermissionLevel, getPermissionName, CHANNEL_TYPE, RestCommand} from "onion-lasers"; import {Command, NamedCommand, getPermissionLevel, getPermissionName, CHANNEL_TYPE, RestCommand} from "onion-lasers";
import {clean} from "../../lib";
import {Config, Storage} from "../../structures"; import {Config, Storage} from "../../structures";
import {Permissions, TextChannel, User, Role, Channel} from "discord.js"; import {Permissions, TextChannel, User, Role, Channel, Util} from "discord.js";
import {logs} from "../../modules/globals"; import {logs} from "../../modules/globals";
import {inspect} from "util";
function getLogBuffer(type: string) { function getLogBuffer(type: string) {
return { return {
@ -372,28 +370,34 @@ export default new NamedCommand({
} }
}) })
}), }),
eval: new NamedCommand({ // TODO: Reimplement this entire command, for `send` doesn't allow
description: "Evaluate code.", // types like `unknown` to be sent anymore. Perhaps try to echo
usage: "<code>", // whatever `evaled` is into an empty buffer and send this.
permission: PERMISSIONS.BOT_OWNER, // (see: `Buffer.alloc(...)`) This is unlikely to work though, since
run: "You have to enter some code to execute first.", // `Buffer.alloc(...)` requires a length, which we can't retrieve from
any: new RestCommand({ // an `unknown` variable.
// You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. // eval: new NamedCommand({
async run({send, message, channel, guild, author, member, client, args, combined}) { // description: "Evaluate code.",
try { // usage: "<code>",
let evaled: unknown = eval(combined); // permission: PERMISSIONS.BOT_OWNER,
// If promises like message.channel.send() are invoked, await them so unnecessary error reports don't leak into the command handler. // run: "You have to enter some code to execute first.",
// Also, it's more useful to see the value rather than Promise { <pending> }. // any: new RestCommand({
if (evaled instanceof Promise) evaled = await evaled; // // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed.
if (typeof evaled !== "string") evaled = inspect(evaled); // async run({send, message, channel, guild, author, member, client, args, combined}) {
// Also await this send call so that if the message is empty, it doesn't leak into the command handler. // try {
await send(clean(evaled), {code: "js", split: true}); // let evaled: unknown = eval(combined);
} catch (err) { // // If promises like message.channel.send() are invoked, await them so unnecessary error reports don't leak into the command handler.
send(clean(err), {code: "js", split: true}); // // Also, it's more useful to see the value rather than Promise { <pending> }.
} // if (evaled instanceof Promise) evaled = await evaled;
} // if (typeof evaled !== "string") evaled = inspect(evaled);
}) // // Also await this send call so that if the message is empty, it doesn't leak into the command handler.
}), // await send(clean(evaled), {code: "js", split: true});
// } catch (err) {
// send(clean(err), {code: "js", split: true});
// }
// }
// })
// }),
nick: new NamedCommand({ nick: new NamedCommand({
description: "Change the bot's nickname.", description: "Change the bot's nickname.",
permission: PERMISSIONS.BOT_SUPPORT, permission: PERMISSIONS.BOT_SUPPORT,
@ -410,8 +414,12 @@ export default new NamedCommand({
description: "Shows a list of all guilds the bot is a member of.", description: "Shows a list of all guilds the bot is a member of.",
permission: PERMISSIONS.BOT_SUPPORT, permission: PERMISSIONS.BOT_SUPPORT,
async run({send, client}) { async run({send, client}) {
const guildList = client.guilds.cache.array().map((e) => e.name); const guildList = Util.splitMessage(
send(guildList, {split: true}); Array.from(client.guilds.cache.map((e) => e.name).values()).join("\n")
);
for (let guildListPart of guildList) {
send(guildListPart);
}
} }
}), }),
activity: new NamedCommand({ activity: new NamedCommand({

View File

@ -22,7 +22,7 @@ export default new NamedCommand({
const helpMenuPages: [string, string][] = []; // An array of (category, description) tuples. const helpMenuPages: [string, string][] = []; // An array of (category, description) tuples.
// Prevent the description of one category from overflowing by splitting it into multiple pages if needed. // Prevent the description of one category from overflowing by splitting it into multiple pages if needed.
for (const category of commands.keyArray()) { for (const category of commands.keys()) {
const commandList = commands.get(category)!; const commandList = commands.get(category)!;
let output = LEGEND; let output = LEGEND;
@ -45,10 +45,16 @@ export default new NamedCommand({
paginate(send, author.id, helpMenuPages.length, (page, hasMultiplePages) => { paginate(send, author.id, helpMenuPages.length, (page, hasMultiplePages) => {
const [category, output] = helpMenuPages[page]; const [category, output] = helpMenuPages[page];
return new MessageEmbed() return {
.setTitle(hasMultiplePages ? `${category} (Page ${page + 1} of ${helpMenuPages.length})` : category) embeds: [
.setDescription(output) new MessageEmbed()
.setColor(EMBED_COLOR); .setTitle(
hasMultiplePages ? `${category} (Page ${page + 1} of ${helpMenuPages.length})` : category
)
.setDescription(output)
.setColor(EMBED_COLOR)
]
};
}); });
}, },
any: new RestCommand({ any: new RestCommand({
@ -87,43 +93,45 @@ export default new NamedCommand({
aliases = formattedAliases.join(", ") || "None"; aliases = formattedAliases.join(", ") || "None";
} }
return send( return send({
new MessageEmbed() embeds: [
.setTitle(header) new MessageEmbed()
.setDescription(command.description) .setTitle(header)
.setColor(EMBED_COLOR) .setDescription(command.description)
.addFields( .setColor(EMBED_COLOR)
{ .addFields(
name: "Aliases", {
value: aliases, name: "Aliases",
inline: true value: aliases,
}, inline: true
{ },
name: "Category", {
value: category, name: "Category",
inline: true value: category,
}, inline: true
{ },
name: "Permission Required", {
value: `\`${getPermissionName(result.permission)}\` (Level ${result.permission})`, name: "Permission Required",
inline: true value: `\`${getPermissionName(result.permission)}\` (Level ${result.permission})`,
}, inline: true
{ },
name: "Channel Type", {
value: getChannelTypeName(result.channelType), name: "Channel Type",
inline: true value: getChannelTypeName(result.channelType),
}, inline: true
{ },
name: "NSFW Only?", {
value: result.nsfw ? "Yes" : "No", name: "NSFW Only?",
inline: true value: result.nsfw ? "Yes" : "No",
}, inline: true
{ },
name: "Usages", {
value: append name: "Usages",
} value: append
) }
); )
]
});
} }
}) })
}); });

View File

@ -18,7 +18,7 @@ export default new NamedCommand({
.setTitle("Math Calculation") .setTitle("Math Calculation")
.addField("Input", `\`\`\`js\n${combined}\`\`\``) .addField("Input", `\`\`\`js\n${combined}\`\`\``)
.addField("Output", `\`\`\`js\n${resp}\`\`\``); .addField("Output", `\`\`\`js\n${resp}\`\`\``);
return send(embed); return send({embeds: [embed]});
} }
}) })
}); });

View File

@ -11,16 +11,16 @@ export default new NamedCommand({
var queryString = args[0]; var queryString = args[0];
let url = new URL(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${queryString}`); let url = new URL(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${queryString}`);
const content = await getContent(url.toString()); const content = await getContent(url.toString());
const msg = await send({embed: content}); const msg = await send({embeds: [content]});
const react = await msg.react("❌"); const react = await msg.react("❌");
const collector = msg.createReactionCollector( const collector = msg.createReactionCollector({
(reaction, user) => { filter: (reaction, user) => {
if (user.id === author.id && reaction.emoji.name === "❌") msg.delete(); if (user.id === author.id && reaction.emoji.name === "❌") msg.delete();
return false; return false;
}, },
{time: 60000} time: 60000
); });
collector.on("end", () => { collector.on("end", () => {
if (!msg.deleted) react.users.remove(msg.author); if (!msg.deleted) react.users.remove(msg.author);

View File

@ -1,15 +1,15 @@
import {MessageEmbed, version as djsversion, Guild, User, GuildMember} from "discord.js"; import {MessageEmbed, version as djsversion, Guild, User, GuildMember, TextChannel, VoiceChannel} from "discord.js";
import ms from "ms"; import ms from "ms";
import os from "os"; import os from "os";
import {Command, NamedCommand, getUserByNickname, CHANNEL_TYPE, getGuildByName, RestCommand} from "onion-lasers"; import {Command, NamedCommand, getUserByNickname, CHANNEL_TYPE, getGuildByName, RestCommand} from "onion-lasers";
import {formatBytes, trimArray} from "../../lib"; import {formatBytes, trimArray} from "../../lib";
import {verificationLevels, filterLevels, regions} from "../../defs/info"; import {verificationLevels, filterLevels} from "../../defs/info";
import moment, {utc} from "moment"; import moment, {utc} from "moment";
export default new NamedCommand({ export default new NamedCommand({
description: "Command to provide all sorts of info about the current server, a user, etc.", description: "Command to provide all sorts of info about the current server, a user, etc.",
async run({send, author, member}) { async run({send, author, member}) {
send(await getUserInfo(author, member)); send({embeds: [await getUserInfo(author, member)]});
}, },
subcommands: { subcommands: {
avatar: new NamedCommand({ avatar: new NamedCommand({
@ -55,39 +55,47 @@ export default new NamedCommand({
const core = os.cpus()[0]; const core = os.cpus()[0];
const embed = new MessageEmbed() const embed = new MessageEmbed()
.setColor(guild?.me?.displayHexColor || "BLUE") .setColor(guild?.me?.displayHexColor || "BLUE")
.addField("General", [ .addField(
`** Client:** ${client.user?.tag} (${client.user?.id})`, "General",
`** Servers:** ${client.guilds.cache.size.toLocaleString()}`, [
`** Users:** ${client.guilds.cache `** Client:** ${client.user?.tag} (${client.user?.id})`,
.reduce((a: any, b: {memberCount: any}) => a + b.memberCount, 0) `** Servers:** ${client.guilds.cache.size.toLocaleString()}`,
.toLocaleString()}`, `** Users:** ${client.guilds.cache
`** Channels:** ${client.channels.cache.size.toLocaleString()}`, .reduce((a: any, b: {memberCount: any}) => a + b.memberCount, 0)
`** Creation Date:** ${utc(client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`, .toLocaleString()}`,
`** Node.JS:** ${process.version}`, `** Channels:** ${client.channels.cache.size.toLocaleString()}`,
`** Version:** v${BOT_VERSION}`, `** Creation Date:** ${utc(client.user?.createdTimestamp).format(
`** Discord.JS:** v${djsversion}`, "Do MMMM YYYY HH:mm:ss"
"\u200b" )}`,
]) `** Node.JS:** ${process.version}`,
.addField("System", [ `** Version:** v${process.env.npm_package_version}`,
`** Platform:** ${process.platform}`, `** Discord.JS:** v${djsversion}`,
`** Uptime:** ${ms(os.uptime() * 1000, { "\u200b"
long: true ].join("\n")
})}`, )
`** CPU:**`, .addField(
`\u3000 • Cores: ${os.cpus().length}`, "System",
`\u3000 • Model: ${core.model}`, [
`\u3000 • Speed: ${core.speed}MHz`, `** Platform:** ${process.platform}`,
`** Memory:**`, `** Uptime:** ${ms(os.uptime() * 1000, {
`\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`, long: true
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}` })}`,
]) `** CPU:**`,
`\u3000 • Cores: ${os.cpus().length}`,
`\u3000 • Model: ${core.model}`,
`\u3000 • Speed: ${core.speed}MHz`,
`** Memory:**`,
`\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`,
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}`
].join("\n")
)
.setTimestamp(); .setTimestamp();
const avatarURL = client.user?.displayAvatarURL({ const avatarURL = client.user?.displayAvatarURL({
dynamic: true, dynamic: true,
size: 2048 size: 2048
}); });
if (avatarURL) embed.setThumbnail(avatarURL); if (avatarURL) embed.setThumbnail(avatarURL);
send(embed); send({embeds: [embed]});
} }
}), }),
guild: new NamedCommand({ guild: new NamedCommand({
@ -95,14 +103,14 @@ export default new NamedCommand({
usage: "(<guild name>/<guild ID>)", usage: "(<guild name>/<guild ID>)",
channelType: CHANNEL_TYPE.GUILD, channelType: CHANNEL_TYPE.GUILD,
async run({send, guild}) { async run({send, guild}) {
send(await getGuildInfo(guild!, guild)); send({embeds: [await getGuildInfo(guild!, guild)]});
}, },
id: "guild", id: "guild",
guild: new Command({ guild: new Command({
description: "Display info about a guild by its ID.", description: "Display info about a guild by its ID.",
async run({send, guild, args}) { async run({send, guild, args}) {
const targetGuild = args[0] as Guild; const targetGuild = args[0] as Guild;
send(await getGuildInfo(targetGuild, guild)); send({embeds: [await getGuildInfo(targetGuild, guild)]});
} }
}), }),
any: new RestCommand({ any: new RestCommand({
@ -111,7 +119,7 @@ export default new NamedCommand({
const targetGuild = getGuildByName(combined); const targetGuild = getGuildByName(combined);
if (typeof targetGuild !== "string") { if (typeof targetGuild !== "string") {
send(await getGuildInfo(targetGuild, guild)); send({embeds: [await getGuildInfo(targetGuild, guild)]});
} else { } else {
send(targetGuild); send(targetGuild);
} }
@ -126,7 +134,7 @@ export default new NamedCommand({
const user = args[0] as User; const user = args[0] as User;
// Transforms the User object into a GuildMember object of the current guild. // Transforms the User object into a GuildMember object of the current guild.
const member = guild?.members.resolve(user); const member = guild?.members.resolve(user);
send(await getUserInfo(user, member)); send({embeds: [await getUserInfo(user, member)]});
} }
}), }),
any: new RestCommand({ any: new RestCommand({
@ -135,7 +143,7 @@ export default new NamedCommand({
const user = await getUserByNickname(combined, guild); const user = await getUserByNickname(combined, guild);
// Transforms the User object into a GuildMember object of the current guild. // Transforms the User object into a GuildMember object of the current guild.
const member = guild?.members.resolve(user); const member = guild?.members.resolve(user);
if (typeof user !== "string") send(await getUserInfo(user, member)); if (typeof user !== "string") send({embeds: [await getUserInfo(user, member)]});
else send(user); else send(user);
} }
}) })
@ -147,20 +155,21 @@ async function getUserInfo(user: User, member: GuildMember | null | undefined):
const embed = new MessageEmbed() const embed = new MessageEmbed()
.setThumbnail(user.displayAvatarURL({dynamic: true, size: 512})) .setThumbnail(user.displayAvatarURL({dynamic: true, size: 512}))
.setColor("BLUE") .setColor("BLUE")
.addField("User", [ .addField(
`** Username:** ${user.username}`, "User",
`** Discriminator:** ${user.discriminator}`, [
`** ID:** ${user.id}`, `** Username:** ${user.username}`,
`** Flags:** ${userFlags.length ? userFlags.join(", ") : "None"}`, `** Discriminator:** ${user.discriminator}`,
`** Avatar:** [Link to avatar](${user.displayAvatarURL({ `** ID:** ${user.id}`,
dynamic: true `** Flags:** ${userFlags.length ? userFlags.join(", ") : "None"}`,
})})`, `** Avatar:** [Link to avatar](${user.displayAvatarURL({
`** Time Created:** ${moment(user.createdTimestamp).format("LT")} ${moment(user.createdTimestamp).format( dynamic: true
"LL" })})`,
)} ${moment(user.createdTimestamp).fromNow()}`, `** Time Created:** ${moment(user.createdTimestamp).format("LT")} ${moment(
`** Status:** ${user.presence.status}`, user.createdTimestamp
`** Game:** ${user.presence.activities || "Not playing a game."}` ).format("LL")} ${moment(user.createdTimestamp).fromNow()}`
]); ].join("\n")
);
if (member) { if (member) {
const roles = member.roles.cache const roles = member.roles.cache
@ -170,16 +179,21 @@ async function getUserInfo(user: User, member: GuildMember | null | undefined):
embed embed
.setColor(member.displayHexColor) .setColor(member.displayHexColor)
.addField("Member", [ .addField(
`** Highest Role:** ${ "Member",
member.roles.highest.id === member.guild.id ? "None" : member.roles.highest.name [
}`, `** Status:** ${member.presence?.status}`,
`** Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`, `** Game:** ${member.presence?.activities ?? "Not playing a game."}`,
`** Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`, `** Highest Role:** ${
`** Roles:** [${roles.length}]: ${ member.roles.highest.id === member.guild.id ? "None" : member.roles.highest.name
roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ") }`,
}` `** Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`,
]); `** Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`,
`** Roles:** [${roles.length}]: ${
roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ")
}`
].join("\n")
);
} }
return embed; return embed;
@ -199,39 +213,47 @@ async function getGuildInfo(guild: Guild, currentGuild: Guild | null) {
const owner = await guild.fetchOwner(); const owner = await guild.fetchOwner();
embed embed
.addField("General", [ .addField(
`** Name:** ${guild.name}`, "General",
`** ID:** ${guild.id}`, [
`** Owner:** ${owner.user.tag} (${guild.ownerID})`, `** Name:** ${guild.name}`,
`** Region:** ${regions[guild.region]}`, `** ID:** ${guild.id}`,
`** Boost Tier:** ${guild.premiumTier ? `Tier ${guild.premiumTier}` : "None"}`, `** Owner:** ${owner.user.tag} (${guild.ownerId})`,
`** Explicit Filter:** ${filterLevels[guild.explicitContentFilter]}`, `** Boost Tier:** ${guild.premiumTier ? `Tier ${guild.premiumTier}` : "None"}`,
`** Verification Level:** ${verificationLevels[guild.verificationLevel]}`, `** Explicit Filter:** ${filterLevels[guild.explicitContentFilter]}`,
`** Time Created:** ${moment(guild.createdTimestamp).format("LT")} ${moment(guild.createdTimestamp).format( `** Verification Level:** ${verificationLevels[guild.verificationLevel]}`,
"LL" `** Time Created:** ${moment(guild.createdTimestamp).format("LT")} ${moment(
)} ${moment(guild.createdTimestamp).fromNow()}`, guild.createdTimestamp
"\u200b" ).format("LL")} ${moment(guild.createdTimestamp).fromNow()}`,
]) "\u200b"
.addField("Statistics", [ ].join("\n")
`** Role Count:** ${roles.length}`, )
`** Emoji Count:** ${emojis.size}`, .addField(
`** Regular Emoji Count:** ${emojis.filter((emoji) => !emoji.animated).size}`, "Statistics",
`** Animated Emoji Count:** ${emojis.filter((emoji) => emoji.animated).size}`, [
`** Member Count:** ${guild.memberCount}`, `** Role Count:** ${roles.length}`,
`** Humans:** ${members.filter((member) => !member.user.bot).size}`, `** Emoji Count:** ${emojis.size}`,
`** Bots:** ${members.filter((member) => member.user.bot).size}`, `** Regular Emoji Count:** ${emojis.filter((emoji) => !emoji.animated).size}`,
`** Text Channels:** ${channels.filter((channel) => channel.type === "text").size}`, `** Animated Emoji Count:** ${emojis.filter((emoji) => !!emoji.animated).size}`,
`** Voice Channels:** ${channels.filter((channel) => channel.type === "voice").size}`, `** Member Count:** ${guild.memberCount}`,
`** Boost Count:** ${guild.premiumSubscriptionCount || "0"}`, `** Humans:** ${members.filter((member) => !member.user.bot).size}`,
`\u200b` `** Bots:** ${members.filter((member) => member.user.bot).size}`,
]) `** Text Channels:** ${channels.filter((channel) => channel instanceof TextChannel).size}`,
.addField("Presence", [ `** Voice Channels:** ${channels.filter((channel) => channel instanceof VoiceChannel).size}`,
`** Online:** ${members.filter((member) => member.presence.status === "online").size}`, `** Boost Count:** ${guild.premiumSubscriptionCount || "0"}`,
`** Idle:** ${members.filter((member) => member.presence.status === "idle").size}`, `\u200b`
`** Do Not Disturb:** ${members.filter((member) => member.presence.status === "dnd").size}`, ].join("\n")
`** Offline:** ${members.filter((member) => member.presence.status === "offline").size}`, )
displayRoles ? "\u200b" : "" .addField(
]) "Presence",
[
`** Online:** ${members.filter((member) => member.presence?.status === "online").size}`,
`** Idle:** ${members.filter((member) => member.presence?.status === "idle").size}`,
`** Do Not Disturb:** ${members.filter((member) => member.presence?.status === "dnd").size}`,
`** Offline:** ${members.filter((member) => member.presence?.status === "offline").size}`,
displayRoles ? "\u200b" : ""
].join("\n")
)
.setTimestamp(); .setTimestamp();
if (iconURL) embed.setThumbnail(iconURL); if (iconURL) embed.setThumbnail(iconURL);

View File

@ -9,7 +9,7 @@ export default new NamedCommand({
description: "Lists all emotes the bot has in it's registry,", description: "Lists all emotes the bot has in it's registry,",
usage: "<regex pattern> (-flags)", usage: "<regex pattern> (-flags)",
async run({send, author, client}) { async run({send, author, client}) {
displayEmoteList(client.emojis.cache.array(), send, author); displayEmoteList(Array.from(client.emojis.cache.values()), send, author);
}, },
any: new RestCommand({ any: new RestCommand({
description: description:
@ -20,7 +20,7 @@ export default new NamedCommand({
const guildID: string = args[0]; const guildID: string = args[0];
displayEmoteList( displayEmoteList(
client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(), Array.from(client.emojis.cache.filter((emote) => emote.guild.id === guildID).values()),
send, send,
author author
); );
@ -32,7 +32,7 @@ export default new NamedCommand({
flags = args.pop().substring(1); flags = args.pop().substring(1);
} }
let emoteCollection = client.emojis.cache.array(); let emoteCollection = Array.from(client.emojis.cache.values());
// Creates a sandbox to stop a regular expression if it takes too much time to search. // Creates a sandbox to stop a regular expression if it takes too much time to search.
// To avoid passing in a giant data structure, I'll just pass in the structure {[id: string]: [name: string]}. // To avoid passing in a giant data structure, I'll just pass in the structure {[id: string]: [name: string]}.
let emotes = new Map<string, string>(); let emotes = new Map<string, string>();
@ -61,12 +61,18 @@ export default new NamedCommand({
emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted. emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted.
displayEmoteList(emoteCollection, send, author); displayEmoteList(emoteCollection, send, author);
} catch (error) { } catch (error) {
if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") { // FIXME: `error` is of type `unknown` here.
// Also: <https://stackoverflow.com/questions/40141005/property-code-does-not-exist-on-type-error>
let errorName = "???";
if (error instanceof Error) {
errorName = error.name;
}
if (errorName === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
send( send(
`The regular expression you entered exceeded the time limit of ${REGEX_TIMEOUT_MS} milliseconds.` `The regular expression you entered exceeded the time limit of ${REGEX_TIMEOUT_MS} milliseconds.`
); );
} else { } else {
throw new Error(error); throw new Error(errorName);
} }
} }
} else { } else {
@ -102,7 +108,7 @@ async function displayEmoteList(emotes: GuildEmoji[], send: SendFunction, author
} }
embed.setDescription(desc); embed.setDescription(desc);
return embed; return {embeds: [embed]};
}); });
} else { } else {
send("No valid emotes found by that query."); send("No valid emotes found by that query.");

View File

@ -1,5 +1,5 @@
import {NamedCommand, RestCommand} from "onion-lasers"; import {NamedCommand, RestCommand} from "onion-lasers";
import {Message, Channel, TextChannel} from "discord.js"; import {Message, Channel, TextChannel, TextBasedChannels} from "discord.js";
import {processEmoteQuery} from "./modules/emote-utils"; import {processEmoteQuery} from "./modules/emote-utils";
export default new NamedCommand({ export default new NamedCommand({
@ -15,7 +15,7 @@ export default new NamedCommand({
if (message.reference) { if (message.reference) {
// If the command message is a reply to another message, use that as the react target. // If the command message is a reply to another message, use that as the react target.
target = await channel.messages.fetch(message.reference.messageID!); target = await channel.messages.fetch(message.reference.messageId!);
} }
// handles reacts by message id/distance // handles reacts by message id/distance
else if (args.length >= 2) { else if (args.length >= 2) {
@ -29,7 +29,7 @@ export default new NamedCommand({
const guildID = match[1]; const guildID = match[1];
const channelID = match[2]; const channelID = match[2];
const messageID = match[3]; const messageID = match[3];
let tmpChannel: Channel | undefined = channel; let tmpChannel: TextBasedChannels | undefined = channel;
if (guild?.id !== guildID) { if (guild?.id !== guildID) {
try { try {
@ -39,12 +39,13 @@ export default new NamedCommand({
} }
} }
if (tmpChannel.id !== channelID) tmpChannel = guild.channels.cache.get(channelID); if (tmpChannel?.id !== channelID)
tmpChannel = guild.channels.cache.get(channelID) as TextBasedChannels;
if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`);
if (message.id !== messageID) { if (message.id !== messageID) {
try { try {
target = await (tmpChannel as TextChannel).messages.fetch(messageID); target = await tmpChannel.messages.fetch(messageID);
} catch { } catch {
return send(`\`${messageID}\` is an invalid message ID!`); return send(`\`${messageID}\` is an invalid message ID!`);
} }
@ -57,14 +58,15 @@ export default new NamedCommand({
const match = copyIDPattern.exec(last)!; const match = copyIDPattern.exec(last)!;
const channelID = match[1]; const channelID = match[1];
const messageID = match[2]; const messageID = match[2];
let tmpChannel: Channel | undefined = channel; let tmpChannel: TextBasedChannels | undefined = channel;
if (tmpChannel.id !== channelID) tmpChannel = guild?.channels.cache.get(channelID); if (tmpChannel?.id !== channelID)
tmpChannel = guild?.channels.cache.get(channelID) as TextBasedChannels;
if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`);
if (message.id !== messageID) { if (message.id !== messageID) {
try { try {
target = await (tmpChannel as TextChannel).messages.fetch(messageID); target = await tmpChannel.messages.fetch(messageID);
} catch { } catch {
return send(`\`${messageID}\` is an invalid message ID!`); return send(`\`${messageID}\` is an invalid message ID!`);
} }

View File

@ -29,7 +29,8 @@ export default new NamedCommand({
const resolvedMessage = resolveMessageWithEmotes(combined); const resolvedMessage = resolveMessageWithEmotes(combined);
if (resolvedMessage) if (resolvedMessage)
webhook.send(resolvedMessage, { webhook.send({
content: resolvedMessage,
username: member!.nickname ?? author.username, username: member!.nickname ?? author.username,
// Webhooks cannot have animated avatars, so requesting the animated version is a moot point. // Webhooks cannot have animated avatars, so requesting the animated version is a moot point.
avatarURL: avatarURL:
@ -38,12 +39,13 @@ export default new NamedCommand({
}) || author.defaultAvatarURL, }) || author.defaultAvatarURL,
allowedMentions: {parse: []}, // avoids double pings allowedMentions: {parse: []}, // avoids double pings
// "embeds" will not be included because it messes with the default ones that generate // "embeds" will not be included because it messes with the default ones that generate
files: message.attachments.array() files: Array.from(message.attachments.values())
}); });
else send("Cannot send an empty message."); else send("Cannot send an empty message.");
} else { } else {
const resolvedMessage = resolveMessageWithEmotes(combined); const resolvedMessage = resolveMessageWithEmotes(combined);
if (resolvedMessage) send(`*${author} says:*\n${resolvedMessage}`, {allowedMentions: {parse: []}}); if (resolvedMessage)
send({content: `*${author} says:*\n${resolvedMessage}`, allowedMentions: {parse: []}});
else send("Cannot send an empty message."); else send("Cannot send an empty message.");
} }

View File

@ -1,7 +1,7 @@
import {NamedCommand, CHANNEL_TYPE} from "onion-lasers"; import {NamedCommand, CHANNEL_TYPE} from "onion-lasers";
import {pluralise} from "../../lib"; import {pluralise} from "../../lib";
import moment from "moment"; import moment from "moment";
import {Collection, TextChannel} from "discord.js"; import {Collection, TextChannel, Util} from "discord.js";
const lastUsedTimestamps = new Collection<string, number>(); const lastUsedTimestamps = new Collection<string, number>();
@ -33,7 +33,7 @@ export default new NamedCommand({
let totalUserEmoteUsage = 0; let totalUserEmoteUsage = 0;
// IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise. // IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise.
const allTextChannelsInCurrentGuild = guild!.channels.cache.filter( const allTextChannelsInCurrentGuild = guild!.channels.cache.filter(
(channel) => channel.type === "text" && channel.viewable (channel) => channel instanceof TextChannel && channel.viewable
) as Collection<string, TextChannel>; ) as Collection<string, TextChannel>;
let messagesSearched = 0; let messagesSearched = 0;
let channelsSearched = 0; let channelsSearched = 0;
@ -41,7 +41,7 @@ export default new NamedCommand({
const totalChannels = allTextChannelsInCurrentGuild.size; const totalChannels = allTextChannelsInCurrentGuild.size;
const statusMessage = await send("Gathering emotes..."); const statusMessage = await send("Gathering emotes...");
let warnings = 0; let warnings = 0;
channel.startTyping(); channel.sendTyping();
// Initialize the emote stats object with every emote in the current guild. // Initialize the emote stats object with every emote in the current guild.
// The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with. // The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with.
@ -63,7 +63,7 @@ export default new NamedCommand({
for (const channel of allTextChannelsInCurrentGuild.values()) { for (const channel of allTextChannelsInCurrentGuild.values()) {
currentChannelName = channel.name; currentChannelName = channel.name;
let selected = channel.lastMessageID ?? message.id; let selected = channel.lastMessageId ?? message.id;
let continueLoop = true; let continueLoop = true;
while (continueLoop) { while (continueLoop) {
@ -161,7 +161,6 @@ export default new NamedCommand({
)}.` )}.`
); );
console.log("[scanemotes]", `Finished operation in ${finishTime - startTime} ms.`); console.log("[scanemotes]", `Finished operation in ${finishTime - startTime} ms.`);
channel.stopTyping();
// Display stats on emote usage. // Display stats on emote usage.
// This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end. // This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end.
@ -180,7 +179,10 @@ export default new NamedCommand({
); );
} }
return await send(lines, {split: true}); let emoteList = Util.splitMessage(lines.join("\n"));
for (let emoteListPart of emoteList) {
return await send(emoteListPart);
}
}, },
subcommands: { subcommands: {
forcereset: new NamedCommand({ forcereset: new NamedCommand({

View File

@ -34,11 +34,14 @@ export default new NamedCommand({
const stream = streamList.get(userID)!; const stream = streamList.get(userID)!;
stream.description = combined; stream.description = combined;
stream.update(); stream.update();
send("Successfully set the stream description to:", { send({
embed: { content: "Successfully set the stream description to:",
description: stream.description, embeds: [
color: member!.displayColor {
} description: stream.description,
color: member!.displayColor
}
]
}); });
} else { } else {
send("You can only use this command when streaming."); send("You can only use this command when streaming.");
@ -70,12 +73,15 @@ export default new NamedCommand({
const stream = streamList.get(userID)!; const stream = streamList.get(userID)!;
stream.thumbnail = combined; stream.thumbnail = combined;
stream.update(); stream.update();
send(`Successfully set the stream thumbnail to: ${combined}`, { send({
embed: { content: `Successfully set the stream thumbnail to: ${combined}`,
description: stream.description, embeds: [
thumbnail: {url: combined}, {
color: member!.displayColor description: stream.description,
} thumbnail: {url: combined},
color: member!.displayColor
}
]
}); });
} else { } else {
send("You can only use this command when streaming."); send("You can only use this command when streaming.");

View File

@ -124,42 +124,40 @@ function getTimeEmbed(user: User) {
} }
const embed = { const embed = {
embed: { color: TIME_EMBED_COLOR,
color: TIME_EMBED_COLOR, author: {
author: { name: user.username,
name: user.username, icon_url: user.displayAvatarURL({
icon_url: user.displayAvatarURL({ format: "png",
format: "png", dynamic: true
dynamic: true })
}) },
fields: [
{
name: "Local Date",
value: localDate
}, },
fields: [ {
{ name: "Day of the Week",
name: "Local Date", value: dayOfWeek
value: localDate },
}, {
{ name: "Local Time",
name: "Day of the Week", value: localTime
value: dayOfWeek },
}, {
{ name: daylightSavingsRegion !== null ? "Current Timezone Offset" : "Timezone Offset",
name: "Local Time", value: timezoneOffset
value: localTime },
}, {
{ name: "Observes Daylight Savings?",
name: daylightSavingsRegion !== null ? "Current Timezone Offset" : "Timezone Offset", value: daylightSavingsRegion ? "Yes" : "No"
value: timezoneOffset }
}, ]
{
name: "Observes Daylight Savings?",
value: daylightSavingsRegion ? "Yes" : "No"
}
]
}
}; };
if (daylightSavingsRegion) { if (daylightSavingsRegion) {
embed.embed.fields.push( embed.fields.push(
{ {
name: "Daylight Savings Active?", name: "Daylight Savings Active?",
value: hasDaylightSavings(daylightSavingsRegion) ? "Yes" : "No" value: hasDaylightSavings(daylightSavingsRegion) ? "Yes" : "No"
@ -178,7 +176,7 @@ export default new NamedCommand({
description: "Show others what time it is for you.", description: "Show others what time it is for you.",
aliases: ["tz"], aliases: ["tz"],
async run({send, author}) { async run({send, author}) {
send(getTimeEmbed(author)); send({embeds: [getTimeEmbed(author)]});
}, },
subcommands: { subcommands: {
// Welcome to callback hell. We hope you enjoy your stay here! // Welcome to callback hell. We hope you enjoy your stay here!
@ -298,10 +296,11 @@ export default new NamedCommand({
const finalize = () => { const finalize = () => {
Storage.save(); Storage.save();
send( send({
"You've finished setting up your timezone! Just check to see if this looks right, and if it doesn't, run this setup again.", content:
getTimeEmbed(author) "You've finished setting up your timezone! Just check to see if this looks right, and if it doesn't, run this setup again.",
); embeds: [getTimeEmbed(author)]
});
}; };
if (hasDST) { if (hasDST) {
@ -358,23 +357,25 @@ export default new NamedCommand({
const time = moment().utc(); const time = moment().utc();
send({ send({
embed: { embeds: [
color: TIME_EMBED_COLOR, {
fields: [ color: TIME_EMBED_COLOR,
{ fields: [
name: "Local Date", {
value: time.format(DATE_FORMAT) name: "Local Date",
}, value: time.format(DATE_FORMAT)
{ },
name: "Day of the Week", {
value: time.format(DOW_FORMAT) name: "Day of the Week",
}, value: time.format(DOW_FORMAT)
{ },
name: "Local Time", {
value: time.format(TIME_FORMAT) name: "Local Time",
} value: time.format(TIME_FORMAT)
] }
} ]
}
]
}); });
} }
}), }),
@ -387,14 +388,14 @@ export default new NamedCommand({
user: new Command({ user: new Command({
description: "See what time it is for someone else.", description: "See what time it is for someone else.",
async run({send, args}) { async run({send, args}) {
send(getTimeEmbed(args[0])); send({embeds: [getTimeEmbed(args[0])]});
} }
}), }),
any: new RestCommand({ any: new RestCommand({
description: "See what time it is for someone else (by their username).", description: "See what time it is for someone else (by their username).",
async run({send, guild, combined}) { async run({send, guild, combined}) {
const user = await getUserByNickname(combined, guild); const user = await getUserByNickname(combined, guild);
if (typeof user !== "string") send(getTimeEmbed(user)); if (typeof user !== "string") send({embeds: [getTimeEmbed(user)]});
else send(user); else send(user);
} }
}) })

View File

@ -17,7 +17,7 @@ export default new NamedCommand({
); );
} }
send(embed); send({embeds: [embed]});
}, },
subcommands: { subcommands: {
add: new NamedCommand({ add: new NamedCommand({

View File

@ -16,19 +16,21 @@ export default new NamedCommand({
}) })
.then((res) => { .then((res) => {
send({ send({
embed: { embeds: [
title: "Translation", {
fields: [ title: "Translation",
{ fields: [
name: "Input", {
value: `\`\`\`${input}\`\`\`` name: "Input",
}, value: `\`\`\`${input}\`\`\``
{ },
name: "Output", {
value: `\`\`\`${res}\`\`\`` name: "Output",
} value: `\`\`\`${res}\`\`\``
] }
} ]
}
]
}); });
}) })
.catch((error) => { .catch((error) => {

View File

@ -4,7 +4,18 @@ import path from "path";
// This is here in order to make it much less of a headache to access the client from other files. // This is here in order to make it much less of a headache to access the client from other files.
// This of course won't actually do anything until the setup process is complete and it logs in. // This of course won't actually do anything until the setup process is complete and it logs in.
export const client = new Client({intents: Intents.ALL}); export const client = new Client({
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MEMBERS,
Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS,
Intents.FLAGS.GUILD_VOICE_STATES,
Intents.FLAGS.GUILD_PRESENCES,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGES
]
});
import {launch} from "onion-lasers"; import {launch} from "onion-lasers";
import setup from "./modules/setup"; import setup from "./modules/setup";
@ -44,7 +55,7 @@ launch(client, path.join(__dirname, "commands"), {
{ {
// OWNER // // OWNER //
name: "Server Owner", name: "Server Owner",
check: (_user, member) => !!member && member.guild.ownerID === member.id check: (_user, member) => !!member && member.guild.ownerId === member.id
}, },
{ {
// BOT_SUPPORT // // BOT_SUPPORT //
@ -67,7 +78,8 @@ launch(client, path.join(__dirname, "commands"), {
// Initialize Modules // // Initialize Modules //
import "./modules/ready"; import "./modules/ready";
import "./modules/presence"; import "./modules/presence";
import "./modules/lavalink"; // TODO: Reimplement entire music system, contact Sink
// import "./modules/lavalink";
import "./modules/emoteRegistry"; import "./modules/emoteRegistry";
import "./modules/systemInfo"; import "./modules/systemInfo";
import "./modules/intercept"; import "./modules/intercept";

View File

@ -154,7 +154,11 @@ export function getContent(url: string): Promise<{url: string}> {
const parsedData = JSON.parse(rawData); const parsedData = JSON.parse(rawData);
resolve(parsedData); resolve(parsedData);
} catch (e) { } catch (e) {
reject(`Error: ${e.message}`); let errorMessage = "Something went wrong! We don't know what, though...";
if (e instanceof Error) {
errorMessage = e.message;
}
reject(`Error: ${errorMessage}`);
} }
}); });
}).on("error", (err: {message: any}) => { }).on("error", (err: {message: any}) => {

View File

@ -11,8 +11,8 @@ function updateGlobalEmoteRegistry(): void {
ref: emote.name, ref: emote.name,
id: emote.id, id: emote.id,
name: emote.name, name: emote.name,
requires_colons: emote.requiresColons || false, requires_colons: emote.requiresColons ?? false,
animated: emote.animated, animated: emote.animated ?? false,
url: emote.url, url: emote.url,
guild_id: emote.guild.name, guild_id: emote.guild.name,
guild_name: emote.guild.name guild_name: emote.guild.name

View File

@ -25,7 +25,7 @@ client.on("guildMemberAdd", async (member) => {
if (welcomeChannel) { if (welcomeChannel) {
const channel = member.guild.channels.cache.get(welcomeChannel); const channel = member.guild.channels.cache.get(welcomeChannel);
if (channel && channel.type === "text") { if (channel && channel instanceof TextChannel) {
if (welcomeType === "graphical") { if (welcomeType === "graphical") {
const canvas = createCanvas(700, 250); const canvas = createCanvas(700, 250);
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
@ -60,9 +60,9 @@ client.on("guildMemberAdd", async (member) => {
ctx.drawImage(avatar, 25, 25, 200, 200); ctx.drawImage(avatar, 25, 25, 200, 200);
const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png"); const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png");
(channel as TextChannel).send(`Welcome \`${member.user.tag}\`!`, attachment); channel.send({content: `Welcome \`${member.user.tag}\`!`, attachments: [attachment]});
} else if (welcomeType === "text") { } else if (welcomeType === "text") {
(channel as TextChannel).send( channel.send(
parseVars( parseVars(
welcomeMessage || "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D", welcomeMessage || "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D",
{ {

View File

@ -1,51 +1,49 @@
import attachClientToLavalink from "discord.js-lavalink-lib"; // import attachClientToLavalink from "discord.js-lavalink-lib";
import {Config} from "../structures"; // import {Config} from "../structures";
import {client} from "../index"; // import {client} from "../index";
//
if (Config.lavalink) { // // Although the example showed to do "client.music = LavaLink(...)" and "(client as any).music = Lavalink(...)" was done to match that, nowhere in the library is client.music ever actually used nor does the function return anything. In other words, client.music is undefined and is never used.
// Although the example showed to do "client.music = LavaLink(...)" and "(client as any).music = Lavalink(...)" was done to match that, nowhere in the library is client.music ever actually used nor does the function return anything. In other words, client.music is undefined and is never used. // attachClientToLavalink(client, {
attachClientToLavalink(client, { // lavalink: {
lavalink: { // restnode: {
restnode: { // host: "localhost",
host: "localhost", // port: 2333,
port: 2333, // password: "youshallnotpass"
password: "youshallnotpass" // },
}, // nodes: [
nodes: [ // {
{ // host: "localhost",
host: "localhost", // port: 2333,
port: 2333, // password: "youshallnotpass"
password: "youshallnotpass" // }
} // ]
] // },
}, // prefix: Config.prefix,
prefix: Config.prefix, // helpCmd: "mhelp",
helpCmd: "mhelp", // admins: ["717352467280691331"]
admins: ["717352467280691331"] // });
}); //
// // Disable the unhandledRejection listener by Lavalink because it captures every single unhandled
// Disable the unhandledRejection listener by Lavalink because it captures every single unhandled // // rejection and adds its message with it. Then replace it with a better, more selective error handler.
// rejection and adds its message with it. Then replace it with a better, more selective error handler. // for (const listener of process.listeners("unhandledRejection")) {
for (const listener of process.listeners("unhandledRejection")) { // if (listener.toString().includes("discord.js-lavalink-musicbot")) {
if (listener.toString().includes("discord.js-lavalink-musicbot")) { // process.off("unhandledRejection", listener);
process.off("unhandledRejection", listener); // }
} // }
} //
// process.on("unhandledRejection", (reason: any) => {
process.on("unhandledRejection", (reason: any) => { // if (reason?.code === "ECONNREFUSED") {
if (reason?.code === "ECONNREFUSED") { // // This is console.warn instead of console.error because on development environments, unless Lavalink is being tested, it won't interfere with the bot's functionality.
// This is console.warn instead of console.error because on development environments, unless Lavalink is being tested, it won't interfere with the bot's functionality. // console.warn(
console.warn( // `[discord.js-lavalink-musicbot] Caught unhandled rejection: ${reason.stack}\nIf this is causing issues, head to the support server at https://discord.gg/dNN4azK`
`[discord.js-lavalink-musicbot] Caught unhandled rejection: ${reason.stack}\nIf this is causing issues, head to the support server at https://discord.gg/dNN4azK` // );
); // }
} // });
}); //
// // It's unsafe to process uncaughtException because after an uncaught exception, the system
// It's unsafe to process uncaughtException because after an uncaught exception, the system // // becomes corrupted. So disable Lavalink from adding a hook to it.
// becomes corrupted. So disable Lavalink from adding a hook to it. // for (const listener of process.listeners("uncaughtException")) {
for (const listener of process.listeners("uncaughtException")) { // if (listener.toString().includes("discord.js-lavalink-musicbot")) {
if (listener.toString().includes("discord.js-lavalink-musicbot")) { // process.off("uncaughtException", listener);
process.off("uncaughtException", listener); // }
} // }
}
}

View File

@ -2,12 +2,9 @@ import {client} from "../index";
import {MessageEmbed} from "discord.js"; import {MessageEmbed} from "discord.js";
import {getPrefix} from "../structures"; import {getPrefix} from "../structures";
import {getMessageByID} from "onion-lasers"; import {getMessageByID} from "onion-lasers";
import {Storage} from "../structures";
client.on("message", async (message) => { client.on("message", (message) => {
const {messageEmbeds} = Storage.getGuild(message.guild!.id); (async () => {
if (messageEmbeds) {
// Only execute if the message is from a user and isn't a command. // Only execute if the message is from a user and isn't a command.
if (message.content.startsWith(getPrefix(message.guild)) || message.author.bot) return; if (message.content.startsWith(getPrefix(message.guild)) || message.author.bot) return;
const messageLink = extractFirstMessageLink(message.content); const messageLink = extractFirstMessageLink(message.content);
@ -27,11 +24,7 @@ client.on("message", async (message) => {
]; ];
if (!linkMessage.cleanContent && embeds.length === 0) { if (!linkMessage.cleanContent && embeds.length === 0) {
return message.channel.send(new MessageEmbed().setDescription("🚫 The message is empty.")); return message.channel.send({embeds: [new MessageEmbed().setDescription("🚫 The message is empty.")]});
}
if (linkMessage.cleanContent.length > 2048) {
return message.channel.send(new MessageEmbed().setDescription("🚫 This message is too long."));
} }
const infoEmbed = new MessageEmbed() const infoEmbed = new MessageEmbed()
@ -49,8 +42,8 @@ client.on("message", async (message) => {
infoEmbed.setImage(image!.url); infoEmbed.setImage(image!.url);
} }
return await message.channel.send(infoEmbed); return await message.channel.send({embeds: [infoEmbed]});
} else return; })();
}); });
export function extractFirstMessageLink(message: string): [string, string, string] | null { export function extractFirstMessageLink(message: string): [string, string, string] | null {

View File

@ -38,7 +38,7 @@ function getStreamEmbed(
// I decided to not include certain fields: // I decided to not include certain fields:
// .addField("Activity", "CrossCode", true) - Probably too much presence data involved, increasing memory usage. // .addField("Activity", "CrossCode", true) - Probably too much presence data involved, increasing memory usage.
// .addField("Viewers", 5, true) - There doesn't seem to currently be a way to track how many viewers there are. Presence data for "WATCHING" doesn't seem to affect it, and listening to raw client events doesn't seem to make it appear either. // .addField("Viewers", 5, true) - There doesn't seem to currently be a way to track how many viewers there are. Presence data for "WATCHING" doesn't seem to affect it, and listening to raw client events doesn't seem to make it appear either.
.addField("Voice Channel", channel, true) .addField("Voice Channel", channel.toString(), true)
.addField("Category", category, true) .addField("Category", category, true)
.setColor(streamer.displayColor) .setColor(streamer.displayColor)
.setFooter( .setFooter(
@ -90,21 +90,23 @@ client.on("voiceStateUpdate", async (before, after) => {
streamer: member, streamer: member,
channel: voiceChannel, channel: voiceChannel,
category, category,
message: await textChannel.send( message: await textChannel.send({
streamNotificationPing, content: streamNotificationPing,
getStreamEmbed(member, voiceChannel, streamStart, category) embeds: [getStreamEmbed(member, voiceChannel, streamStart, category)]
), }),
update(this: Stream) { update(this: Stream) {
this.message.edit( this.message.edit({
getStreamEmbed( embeds: [
this.streamer, getStreamEmbed(
this.channel, this.streamer,
streamStart, this.channel,
this.category, streamStart,
this.description, this.category,
this.thumbnail this.description,
) this.thumbnail
); )
]
});
}, },
streamStart streamStart
}); });
@ -125,7 +127,7 @@ client.on("voiceStateUpdate", async (before, after) => {
}); });
client.on("channelUpdate", (before, after) => { client.on("channelUpdate", (before, after) => {
if (before.type === "voice" && after.type === "voice") { if (before instanceof VoiceChannel && after instanceof VoiceChannel) {
for (const stream of streamList.values()) { for (const stream of streamList.values()) {
if (after.id === stream.channel.id) { if (after.id === stream.channel.id) {
stream.update(); stream.update();

View File

@ -13,8 +13,8 @@ client.on("guildCreate", async (guild) => {
if (Config.systemLogsChannel) { if (Config.systemLogsChannel) {
const channel = client.channels.cache.get(Config.systemLogsChannel); const channel = client.channels.cache.get(Config.systemLogsChannel);
if (channel && channel.type === "text") { if (channel instanceof TextChannel) {
(channel as TextChannel).send( channel.send(
`TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${owner.user.tag}\` (\`${owner.user.id}\`)` `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${owner.user.tag}\` (\`${owner.user.id}\`)`
); );
} else { } else {
@ -29,8 +29,8 @@ client.on("guildDelete", (guild) => {
if (Config.systemLogsChannel) { if (Config.systemLogsChannel) {
const channel = client.channels.cache.get(Config.systemLogsChannel); const channel = client.channels.cache.get(Config.systemLogsChannel);
if (channel && channel.type === "text") { if (channel instanceof TextChannel) {
(channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); channel.send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`);
} else { } else {
console.warn( console.warn(
`${Config.systemLogsChannel} is not a valid text channel for system logs! Removing it from storage.` `${Config.systemLogsChannel} is not a valid text channel for system logs! Removing it from storage.`

View File

@ -61,7 +61,7 @@ export async function refreshWebhookCache(): Promise<void> {
// If there are stored webhook IDs/tokens that don't work, delete those webhooks from storage. // If there are stored webhook IDs/tokens that don't work, delete those webhooks from storage.
try { try {
const webhook = await client.fetchWebhook(id, token); const webhook = await client.fetchWebhook(id, token);
webhookStorage.set(webhook.channelID, webhook); webhookStorage.set(webhook.channelId, webhook);
} catch { } catch {
delete Config.webhooks[id]; delete Config.webhooks[id];
Config.save(); Config.save();

View File

@ -10,7 +10,7 @@
// Type Settings // // Type Settings //
"strict": true, // Enables all strict checks possible. "strict": true, // Enables all strict checks possible.
"noImplicitReturns": true, // Makes sure you don't accidentally return something + undefined. "noImplicitReturns": false, // Makes sure you don't accidentally return something + undefined.
"noFallthroughCasesInSwitch": true, // Prevents accidentally forgetting to break every switch case. Of course, if you know what you're doing, feel free to add a @ts-ignore, which also signals that it's not a mistake. "noFallthroughCasesInSwitch": true, // Prevents accidentally forgetting to break every switch case. Of course, if you know what you're doing, feel free to add a @ts-ignore, which also signals that it's not a mistake.
"forceConsistentCasingInFileNames": true, // Make import paths case-sensitive. "./tEst" is no longer the same as "./test". "forceConsistentCasingInFileNames": true, // Make import paths case-sensitive. "./tEst" is no longer the same as "./test".
"esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting. "esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting.