mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Merge branch 'typescript' of https://github.com/keanuplayz/TravBot-v3 into experimental-core
This commit is contained in:
commit
00addd468c
12 changed files with 2242 additions and 119 deletions
1919
package-lock.json
generated
1919
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,8 +10,7 @@
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"inquirer": "^7.3.3",
|
"inquirer": "^7.3.3",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3"
|
||||||
"os": "^0.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/glob": "^7.1.3",
|
"@types/glob": "^7.1.3",
|
||||||
|
|
|
@ -124,14 +124,15 @@ export default new Command({
|
||||||
number: new Command({
|
number: new Command({
|
||||||
description: "Amount of messages to delete.",
|
description: "Amount of messages to delete.",
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
|
if ($.channel.type === "dm") {
|
||||||
|
await $.channel.send("Can't clear messages in the DMs!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
$.message.delete();
|
$.message.delete();
|
||||||
const fetched = await $.channel.messages.fetch({
|
const fetched = await $.channel.messages.fetch({
|
||||||
limit: $.args[0]
|
limit: $.args[0]
|
||||||
});
|
});
|
||||||
$.channel
|
await $.channel.bulkDelete(fetched);
|
||||||
/// @ts-ignore
|
|
||||||
.bulkDelete(fetched)
|
|
||||||
.catch((error: any) => $.channel.send(`Error: ${error}`));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
@ -157,8 +158,7 @@ export default new Command({
|
||||||
permission: Command.PERMISSIONS.BOT_SUPPORT,
|
permission: Command.PERMISSIONS.BOT_SUPPORT,
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
const nickName = $.args.join(" ");
|
const nickName = $.args.join(" ");
|
||||||
const trav = $.guild?.members.cache.find((member) => member.id === $.client.user?.id);
|
await $.guild?.me?.setNickname(nickName);
|
||||||
await trav?.setNickname(nickName);
|
|
||||||
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES))
|
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES))
|
||||||
$.message.delete({timeout: 5000}).catch($.handler.bind($));
|
$.message.delete({timeout: 5000}).catch($.handler.bind($));
|
||||||
$.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000}));
|
$.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000}));
|
||||||
|
|
|
@ -62,9 +62,9 @@ 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: TextChannel | DMChannel | NewsChannel): boolean {
|
||||||
if (guild?.id === "637512823676600330" || process.argv[2] === "dev") return true;
|
if (guild?.id === "637512823676600330" && channel?.id === "669464416420364288" || process.argv[2] === "dev") return true;
|
||||||
else {
|
else {
|
||||||
channel.send("Sorry, this command can only be used in Monika's emote server.");
|
channel.send("Sorry, this command can only be used in Monika's emote server. (#mon-stocks)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import {CommonLibrary, formatBytes, trimArray} from "../core/lib";
|
||||||
import {verificationLevels, filterLevels, regions, flags} from "../defs/info";
|
import {verificationLevels, filterLevels, regions, flags} from "../defs/info";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import utc from "moment";
|
import utc from "moment";
|
||||||
|
import {Guild} from "discord.js";
|
||||||
|
|
||||||
|
const {version} = require("../../package.json");
|
||||||
|
|
||||||
export default new Command({
|
export default new Command({
|
||||||
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.",
|
||||||
|
@ -34,13 +37,6 @@ export default new Command({
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
const core = os.cpus()[0];
|
const core = os.cpus()[0];
|
||||||
const embed = new MessageEmbed()
|
const embed = new MessageEmbed()
|
||||||
.setThumbnail(
|
|
||||||
/// @ts-ignore
|
|
||||||
$.client.user?.displayAvatarURL({
|
|
||||||
dynamic: true,
|
|
||||||
size: 2048
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.setColor($.guild?.me?.displayHexColor || "BLUE")
|
.setColor($.guild?.me?.displayHexColor || "BLUE")
|
||||||
.addField("General", [
|
.addField("General", [
|
||||||
`**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`,
|
`**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`,
|
||||||
|
@ -66,84 +62,59 @@ export default new Command({
|
||||||
`\u3000 • Speed: ${core.speed}MHz`,
|
`\u3000 • Speed: ${core.speed}MHz`,
|
||||||
`**❯ Memory:**`,
|
`**❯ Memory:**`,
|
||||||
`\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`,
|
`\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`,
|
||||||
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapTotal)}`
|
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}`
|
||||||
])
|
])
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
const avatarURL = $.client.user?.displayAvatarURL({
|
||||||
|
dynamic: true,
|
||||||
|
size: 2048
|
||||||
|
});
|
||||||
|
if (avatarURL) embed.setThumbnail(avatarURL);
|
||||||
$.channel.send(embed);
|
$.channel.send(embed);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
guild: new Command({
|
guild: new Command({
|
||||||
description: "Displays info about the current guild.",
|
description: "Displays info about the current guild or another guild.",
|
||||||
|
usage: "(<guild name>/<guild ID>)",
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
if ($.guild) {
|
if ($.guild) {
|
||||||
const roles = $.guild.roles.cache
|
$.channel.send(await getGuildInfo($.guild, $.guild));
|
||||||
.sort((a, b) => b.position - a.position)
|
|
||||||
.map((role) => role.toString());
|
|
||||||
const members = $.guild.members.cache;
|
|
||||||
const channels = $.guild.channels.cache;
|
|
||||||
const emojis = $.guild.emojis.cache;
|
|
||||||
const iconURL = $.guild.iconURL({dynamic: true});
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setDescription(`**Guild information for __${$.guild.name}__**`)
|
|
||||||
.setColor("BLUE");
|
|
||||||
if (iconURL)
|
|
||||||
embed
|
|
||||||
.setThumbnail(iconURL)
|
|
||||||
.addField("General", [
|
|
||||||
`**❯ Name:** ${$.guild.name}`,
|
|
||||||
`**❯ ID:** ${$.guild.id}`,
|
|
||||||
`**❯ Owner:** ${$.guild.owner?.user.tag} (${$.guild.ownerID})`,
|
|
||||||
`**❯ Region:** ${regions[$.guild.region]}`,
|
|
||||||
`**❯ Boost Tier:** ${$.guild.premiumTier ? `Tier ${$.guild.premiumTier}` : "None"}`,
|
|
||||||
`**❯ Explicit Filter:** ${filterLevels[$.guild.explicitContentFilter]}`,
|
|
||||||
`**❯ Verification Level:** ${verificationLevels[$.guild.verificationLevel]}`,
|
|
||||||
`**❯ Time Created:** ${moment($.guild.createdTimestamp).format("LT")} ${moment(
|
|
||||||
$.guild.createdTimestamp
|
|
||||||
).format("LL")} ${moment($.guild.createdTimestamp).fromNow()})`,
|
|
||||||
"\u200b"
|
|
||||||
])
|
|
||||||
.addField("Statistics", [
|
|
||||||
`**❯ Role Count:** ${roles.length}`,
|
|
||||||
`**❯ Emoji Count:** ${emojis.size}`,
|
|
||||||
`**❯ Regular Emoji Count:** ${emojis.filter((emoji) => !emoji.animated).size}`,
|
|
||||||
`**❯ Animated Emoji Count:** ${emojis.filter((emoji) => emoji.animated).size}`,
|
|
||||||
`**❯ Member Count:** ${$.guild.memberCount}`,
|
|
||||||
`**❯ Humans:** ${members.filter((member) => !member.user.bot).size}`,
|
|
||||||
`**❯ Bots:** ${members.filter((member) => member.user.bot).size}`,
|
|
||||||
`**❯ Text Channels:** ${channels.filter((channel) => channel.type === "text").size}`,
|
|
||||||
`**❯ Voice Channels:** ${channels.filter((channel) => channel.type === "voice").size}`,
|
|
||||||
`**❯ Boost Count:** ${$.guild.premiumSubscriptionCount || "0"}`,
|
|
||||||
`\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
|
|
||||||
}`,
|
|
||||||
"\u200b"
|
|
||||||
])
|
|
||||||
.addField(
|
|
||||||
`Roles [${roles.length - 1}]`,
|
|
||||||
roles.length < 10 ? roles.join(", ") : roles.length > 10 ? trimArray(roles) : "None"
|
|
||||||
)
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
$.channel.send(embed);
|
|
||||||
} else {
|
} else {
|
||||||
$.channel.send("Please execute this command in a guild.");
|
$.channel.send("Please execute this command in a guild.");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
any: new Command({
|
||||||
|
description: "Display info about a guild by finding its name or ID.",
|
||||||
|
async run($: CommonLibrary): Promise<any> {
|
||||||
|
// If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild
|
||||||
|
if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) {
|
||||||
|
const id = $.args[0];
|
||||||
|
const guild = $.client.guilds.cache.get(id);
|
||||||
|
|
||||||
|
if (guild) {
|
||||||
|
$.channel.send(await getGuildInfo(guild, $.guild));
|
||||||
|
} else {
|
||||||
|
$.channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const query: string = $.args.join(" ").toLowerCase();
|
||||||
|
const guild = $.client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query));
|
||||||
|
|
||||||
|
if (guild) {
|
||||||
|
$.channel.send(await getGuildInfo(guild, $.guild));
|
||||||
|
} else {
|
||||||
|
$.channel.send(`None of the servers I'm in matches the query \`${query}\`!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
user: new Command({
|
user: new Command({
|
||||||
description: "Displays info about mentioned user.",
|
description: "Displays info about mentioned user.",
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
// 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($.args[0]) ?? (await $.guild?.members.fetch($.args[0]));
|
const member = await $.guild?.members.fetch($.args[0]);
|
||||||
|
|
||||||
if (!member)
|
if (!member)
|
||||||
return $.channel.send(
|
return $.channel.send(
|
||||||
|
@ -154,8 +125,7 @@ export default new Command({
|
||||||
.sort((a: {position: number}, b: {position: number}) => b.position - a.position)
|
.sort((a: {position: number}, b: {position: number}) => b.position - a.position)
|
||||||
.map((role: {toString: () => any}) => role.toString())
|
.map((role: {toString: () => any}) => role.toString())
|
||||||
.slice(0, -1);
|
.slice(0, -1);
|
||||||
// @ts-ignore - Discord.js' typings seem to be outdated here. According to their v12 docs, it's User.fetchFlags() instead of User.flags.
|
const userFlags = (await member.user.fetchFlags()).toArray();
|
||||||
const userFlags = ((await member.user.fetchFlags()) as UserFlags).toArray();
|
|
||||||
|
|
||||||
const embed = new MessageEmbed()
|
const embed = new MessageEmbed()
|
||||||
.setThumbnail(member.user.displayAvatarURL({dynamic: true, size: 512}))
|
.setThumbnail(member.user.displayAvatarURL({dynamic: true, size: 512}))
|
||||||
|
@ -188,3 +158,64 @@ export default new Command({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function getGuildInfo(guild: Guild, currentGuild: Guild | null) {
|
||||||
|
const members = await guild.members.fetch({
|
||||||
|
withPresences: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
const roles = guild.roles.cache.sort((a, b) => b.position - a.position).map((role) => role.toString());
|
||||||
|
const channels = guild.channels.cache;
|
||||||
|
const emojis = guild.emojis.cache;
|
||||||
|
const iconURL = guild.iconURL({dynamic: true});
|
||||||
|
const embed = new MessageEmbed().setDescription(`**Guild information for __${guild.name}__**`).setColor("BLUE");
|
||||||
|
const displayRoles = !!(currentGuild && guild.id === currentGuild.id);
|
||||||
|
if (iconURL) {
|
||||||
|
embed
|
||||||
|
.setThumbnail(iconURL)
|
||||||
|
.addField("General", [
|
||||||
|
`**❯ Name:** ${guild.name}`,
|
||||||
|
`**❯ ID:** ${guild.id}`,
|
||||||
|
`**❯ Owner:** ${guild.owner?.user.tag} (${guild.ownerID})`,
|
||||||
|
`**❯ Region:** ${regions[guild.region]}`,
|
||||||
|
`**❯ Boost Tier:** ${guild.premiumTier ? `Tier ${guild.premiumTier}` : "None"}`,
|
||||||
|
`**❯ Explicit Filter:** ${filterLevels[guild.explicitContentFilter]}`,
|
||||||
|
`**❯ Verification Level:** ${verificationLevels[guild.verificationLevel]}`,
|
||||||
|
`**❯ Time Created:** ${moment(guild.createdTimestamp).format("LT")} ${moment(
|
||||||
|
guild.createdTimestamp
|
||||||
|
).format("LL")} ${moment(guild.createdTimestamp).fromNow()}`,
|
||||||
|
"\u200b"
|
||||||
|
])
|
||||||
|
.addField("Statistics", [
|
||||||
|
`**❯ Role Count:** ${roles.length}`,
|
||||||
|
`**❯ Emoji Count:** ${emojis.size}`,
|
||||||
|
`**❯ Regular Emoji Count:** ${emojis.filter((emoji) => !emoji.animated).size}`,
|
||||||
|
`**❯ Animated Emoji Count:** ${emojis.filter((emoji) => emoji.animated).size}`,
|
||||||
|
`**❯ Member Count:** ${guild.memberCount}`,
|
||||||
|
`**❯ Humans:** ${members.filter((member) => !member.user.bot).size}`,
|
||||||
|
`**❯ Bots:** ${members.filter((member) => member.user.bot).size}`,
|
||||||
|
`**❯ Text Channels:** ${channels.filter((channel) => channel.type === "text").size}`,
|
||||||
|
`**❯ Voice Channels:** ${channels.filter((channel) => channel.type === "voice").size}`,
|
||||||
|
`**❯ Boost Count:** ${guild.premiumSubscriptionCount || "0"}`,
|
||||||
|
`\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" : ""
|
||||||
|
])
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
// Only add the roles if the guild the bot is sending the message to is the same one that's being requested.
|
||||||
|
if (displayRoles) {
|
||||||
|
embed.addField(
|
||||||
|
`Roles [${roles.length - 1}]`,
|
||||||
|
roles.length < 10 ? roles.join(", ") : roles.length > 10 ? trimArray(roles) : "None"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
|
@ -9,15 +9,14 @@ export default new Command({
|
||||||
|
|
||||||
if (!voiceChannel) return $.channel.send("You are not in a voice channel.");
|
if (!voiceChannel) return $.channel.send("You are not in a voice channel.");
|
||||||
|
|
||||||
if (!$.guild?.me?.hasPermission("MANAGE_CHANNELS"))
|
if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS"))
|
||||||
return $.channel.send("I am lacking the required permissions to perform this action.");
|
return $.channel.send("I am lacking the required permissions to perform this action.");
|
||||||
|
|
||||||
if ($.args.length === 0) return $.channel.send("Please provide a new voice channel name.");
|
if ($.args.length === 0) return $.channel.send("Please provide a new voice channel name.");
|
||||||
|
|
||||||
const changeVC = $.guild.channels.resolve(voiceChannel.id);
|
const prevName = voiceChannel.name;
|
||||||
$.channel
|
const newName = $.args.join(" ");
|
||||||
.send(`Changed channel name from "${voiceChannel}" to "${$.args.join(" ")}".`)
|
await voiceChannel.setName(newName);
|
||||||
/// @ts-ignore
|
await $.channel.send(`Changed channel name from "${prevName}" to "${newName}".`);
|
||||||
.then(changeVC?.setName($.args.join(" ")));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,6 @@ export default new Command({
|
||||||
async run({guild, channel, message, args}) {
|
async run({guild, channel, message, args}) {
|
||||||
let output = "";
|
let output = "";
|
||||||
for (const query of args) output += queryClosestEmoteByName(query).toString();
|
for (const query of args) output += queryClosestEmoteByName(query).toString();
|
||||||
if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) message.delete();
|
|
||||||
channel.send(output);
|
channel.send(output);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,32 +1,117 @@
|
||||||
|
import {GuildEmoji} from "discord.js";
|
||||||
import {MessageEmbed} from "discord.js";
|
import {MessageEmbed} from "discord.js";
|
||||||
import Command from "../../core/command";
|
import Command from "../../core/command";
|
||||||
import {CommonLibrary} from "../../core/lib";
|
import {CommonLibrary} from "../../core/lib";
|
||||||
|
import vm from "vm";
|
||||||
|
|
||||||
|
const REGEX_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
export default new Command({
|
export default new Command({
|
||||||
description: "Lists all emotes the bot has in it's registry,",
|
description: "Lists all emotes the bot has in it's registry,",
|
||||||
endpoint: true,
|
usage: "<regex pattern> (-flags)",
|
||||||
async run($: CommonLibrary): Promise<any> {
|
async run($: CommonLibrary): Promise<any> {
|
||||||
const nsfw: string | string[] = [];
|
displayEmoteList($, $.client.emojis.cache.array());
|
||||||
const pages = $.client.emojis.cache.filter((x) => !nsfw.includes(x.guild.id), this).array();
|
},
|
||||||
const pagesSplit = $(pages).split(20);
|
any: new Command({
|
||||||
$.log(pagesSplit);
|
description:
|
||||||
var embed = new MessageEmbed().setTitle("**Emoji list!**").setColor("AQUA");
|
"Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i",
|
||||||
let desc = "";
|
async run($: CommonLibrary): Promise<any> {
|
||||||
|
// If a guild ID is provided, filter all emotes by that guild (but only if there aren't any arguments afterward)
|
||||||
|
if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) {
|
||||||
|
const guildID: string = $.args[0];
|
||||||
|
|
||||||
for (const emote of pagesSplit[0]) {
|
displayEmoteList($, $.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array());
|
||||||
desc += `${emote} | ${emote.name}\n`;
|
} else {
|
||||||
|
// Otherwise, search via a regex pattern
|
||||||
|
let flags: string | undefined = undefined;
|
||||||
|
|
||||||
|
if (/^-[dgimsuy]{1,7}$/.test($.args[$.args.length - 1])) {
|
||||||
|
flags = $.args.pop().substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emoteCollection = $.client.emojis.cache.array();
|
||||||
|
// 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]}.
|
||||||
|
//let emotes: {[id: string]: string} = {};
|
||||||
|
let emotes = new Map<string, string>();
|
||||||
|
|
||||||
|
for (const emote of emoteCollection) {
|
||||||
|
emotes.set(emote.id, emote.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The result will be sandbox.emotes because it'll be modified in-place.
|
||||||
|
const sandbox = {
|
||||||
|
regex: new RegExp($.args.join(" "), flags),
|
||||||
|
emotes
|
||||||
|
};
|
||||||
|
const context = vm.createContext(sandbox);
|
||||||
|
|
||||||
|
if (vm.isContext(sandbox)) {
|
||||||
|
// Restrict an entire query to the timeout specified.
|
||||||
|
try {
|
||||||
|
const script = new vm.Script(
|
||||||
|
"for(const [id, name] of emotes.entries()) if(!regex.test(name)) emotes.delete(id);"
|
||||||
|
);
|
||||||
|
script.runInContext(context, {timeout: REGEX_TIMEOUT_MS});
|
||||||
|
emotes = sandbox.emotes;
|
||||||
|
emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted.
|
||||||
|
displayEmoteList($, emoteCollection);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
||||||
|
$.channel.send(
|
||||||
|
`The regular expression you entered exceeded the time limit of ${REGEX_TIMEOUT_MS} milliseconds.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$.channel.send("Failed to initialize sandbox.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
async function displayEmoteList($: CommonLibrary, emotes: GuildEmoji[]) {
|
||||||
|
emotes.sort((a, b) => {
|
||||||
|
const first = a.name.toLowerCase();
|
||||||
|
const second = b.name.toLowerCase();
|
||||||
|
|
||||||
|
if (first > second) return 1;
|
||||||
|
else if (first < second) return -1;
|
||||||
|
else return 0;
|
||||||
|
});
|
||||||
|
const sections = $(emotes).split(20);
|
||||||
|
const pages = sections.length;
|
||||||
|
const embed = new MessageEmbed().setTitle("**Emotes**").setColor("AQUA");
|
||||||
|
let desc = "";
|
||||||
|
|
||||||
|
// Gather the first page (if it even exists, which it might not if there no valid emotes appear)
|
||||||
|
if (pages > 0) {
|
||||||
|
for (const emote of sections[0]) {
|
||||||
|
desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
embed.setDescription(desc);
|
embed.setDescription(desc);
|
||||||
const msg = await $.channel.send({embed});
|
|
||||||
|
|
||||||
$.paginate(msg, $.author.id, pages.length, (page) => {
|
if (pages > 1) {
|
||||||
let desc = "";
|
embed.setTitle(`**Emotes** (Page 1 of ${pages})`);
|
||||||
for (const emote of pagesSplit[page]) {
|
const msg = await $.channel.send({embed});
|
||||||
desc += `${emote} | ${emote.name}\n`;
|
|
||||||
}
|
$.paginate(msg, $.author.id, pages, (page) => {
|
||||||
embed.setDescription(desc);
|
let desc = "";
|
||||||
msg.edit(embed);
|
for (const emote of sections[page]) {
|
||||||
});
|
desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`;
|
||||||
|
}
|
||||||
|
embed.setTitle(`**Emotes** (Page ${page + 1} of ${pages})`);
|
||||||
|
embed.setDescription(desc);
|
||||||
|
msg.edit(embed);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await $.channel.send({embed});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$.channel.send("No valid emotes found by that query.");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ export function formatUTCTimestamp(now = new Date()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function botHasPermission(guild: Guild | null, permission: number): boolean {
|
export function botHasPermission(guild: Guild | null, permission: number): boolean {
|
||||||
return !!(client.user && guild?.members.resolve(client.user)?.hasPermission(permission));
|
return !!guild?.me?.hasPermission(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateGlobalEmoteRegistry(): void {
|
export function updateGlobalEmoteRegistry(): void {
|
||||||
|
@ -212,20 +212,22 @@ $.paginate = async (
|
||||||
|
|
||||||
callback(page);
|
callback(page);
|
||||||
};
|
};
|
||||||
|
const BACKWARDS_EMOJI = "⬅️";
|
||||||
|
const FORWARDS_EMOJI = "➡️";
|
||||||
const handle = (emote: string, reacterID: string) => {
|
const handle = (emote: string, reacterID: string) => {
|
||||||
switch (emote) {
|
switch (emote) {
|
||||||
case "⬅️":
|
case BACKWARDS_EMOJI:
|
||||||
turn(-1);
|
turn(-1);
|
||||||
break;
|
break;
|
||||||
case "➡️":
|
case FORWARDS_EMOJI:
|
||||||
turn(1);
|
turn(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen for reactions and call the handler.
|
// Listen for reactions and call the handler.
|
||||||
await message.react("⬅️");
|
let backwardsReaction = await message.react(BACKWARDS_EMOJI);
|
||||||
await message.react("➡️");
|
let forwardsReaction = await message.react(FORWARDS_EMOJI);
|
||||||
eventListeners.set(message.id, handle);
|
eventListeners.set(message.id, handle);
|
||||||
await message.awaitReactions(
|
await message.awaitReactions(
|
||||||
(reaction, user) => {
|
(reaction, user) => {
|
||||||
|
@ -244,8 +246,8 @@ $.paginate = async (
|
||||||
);
|
);
|
||||||
// When time's up, remove the bot's own reactions.
|
// When time's up, remove the bot's own reactions.
|
||||||
eventListeners.delete(message.id);
|
eventListeners.delete(message.id);
|
||||||
message.reactions.cache.get("⬅️")?.users.remove(message.author);
|
backwardsReaction.users.remove(message.author);
|
||||||
message.reactions.cache.get("➡️")?.users.remove(message.author);
|
forwardsReaction.users.remove(message.author);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Waits for the sender to either confirm an action or let it pass (and delete the message).
|
// Waits for the sender to either confirm an action or let it pass (and delete the message).
|
||||||
|
|
|
@ -4,11 +4,16 @@ import {hasPermission, getPermissionLevel, PermissionNames} from "../core/permis
|
||||||
import {Permissions} from "discord.js";
|
import {Permissions} from "discord.js";
|
||||||
import {getPrefix} from "../core/structures";
|
import {getPrefix} from "../core/structures";
|
||||||
import $, {replyEventListeners} from "../core/lib";
|
import $, {replyEventListeners} from "../core/lib";
|
||||||
|
import quote from "../modules/message_embed";
|
||||||
|
|
||||||
export default new Event<"message">({
|
export default new Event<"message">({
|
||||||
async on(message) {
|
async on(message) {
|
||||||
const commands = await loadableCommands;
|
const commands = await loadableCommands;
|
||||||
|
|
||||||
|
if (message.content.toLowerCase().includes("remember to drink water")) {
|
||||||
|
message.react("🚱");
|
||||||
|
}
|
||||||
|
|
||||||
// Message Setup //
|
// Message Setup //
|
||||||
if (message.author.bot) return;
|
if (message.author.bot) return;
|
||||||
|
|
||||||
|
@ -24,6 +29,10 @@ export default new Event<"message">({
|
||||||
const clientUser = message.client.user;
|
const clientUser = message.client.user;
|
||||||
let usesBotSpecificPrefix = false;
|
let usesBotSpecificPrefix = false;
|
||||||
|
|
||||||
|
if (!message.content.startsWith(prefix)) {
|
||||||
|
return quote(message);
|
||||||
|
}
|
||||||
|
|
||||||
// If the client user exists, check if it starts with the bot-specific prefix.
|
// If the client user exists, check if it starts with the bot-specific prefix.
|
||||||
if (clientUser) {
|
if (clientUser) {
|
||||||
// If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other).
|
// If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other).
|
||||||
|
|
32
src/index.ts
32
src/index.ts
|
@ -1,13 +1,41 @@
|
||||||
import {Client} from "discord.js";
|
import * as discord from "discord.js";
|
||||||
import setup from "./setup";
|
import setup from "./setup";
|
||||||
import {Config} from "./core/structures";
|
import {Config} from "./core/structures";
|
||||||
import {loadEvents} from "./core/event";
|
import {loadEvents} from "./core/event";
|
||||||
import "discord.js-lavalink-lib";
|
import "discord.js-lavalink-lib";
|
||||||
import LavalinkMusic from "discord.js-lavalink-lib";
|
import LavalinkMusic from "discord.js-lavalink-lib";
|
||||||
|
|
||||||
|
declare module "discord.js" {
|
||||||
|
interface Presence {
|
||||||
|
patch(data: any): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The terrible hacks were written by none other than The Noble Programmer On The White PC.
|
||||||
|
|
||||||
|
// NOTE: Terrible hack ahead!!! In order to reduce the memory usage of the bot
|
||||||
|
// we only store the information from presences that we actually end up using,
|
||||||
|
// which currently is only the (online/idle/dnd/offline/...) status (see
|
||||||
|
// `src/commands/info.ts`). What data is retrieved from the `data` object
|
||||||
|
// (which contains the data received from the Gateway) and how can be seen
|
||||||
|
// here:
|
||||||
|
// <https://github.com/discordjs/discord.js/blob/cee6cf70ce76e9b06dc7f25bfd77498e18d7c8d4/src/structures/Presence.js#L81-L110>.
|
||||||
|
const oldPresencePatch = discord.Presence.prototype.patch;
|
||||||
|
discord.Presence.prototype.patch = function patch(data: any) {
|
||||||
|
oldPresencePatch.call(this, {status: data.status});
|
||||||
|
};
|
||||||
|
|
||||||
// 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();
|
export const client = new discord.Client();
|
||||||
|
|
||||||
|
// NOTE: Terrible hack continued!!! Unfortunately we can't receive the presence
|
||||||
|
// data at all when the GUILD_PRESENCES intent is disabled, so while we do
|
||||||
|
// waste network bandwidth and the CPU time for decoding the incoming packets,
|
||||||
|
// the function which handles those packets is NOP-ed out, which, among other
|
||||||
|
// things, skips the code which caches the referenced users in the packet. See
|
||||||
|
// <https://github.com/discordjs/discord.js/blob/cee6cf70ce76e9b06dc7f25bfd77498e18d7c8d4/src/client/actions/PresenceUpdate.js#L7-L41>.
|
||||||
|
(client["actions"] as any)["PresenceUpdate"].handle = () => {};
|
||||||
|
|
||||||
(client as any).music = LavalinkMusic(client, {
|
(client as any).music = LavalinkMusic(client, {
|
||||||
lavalink: {
|
lavalink: {
|
||||||
|
|
60
src/modules/message_embed.ts
Normal file
60
src/modules/message_embed.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { client } from '..'
|
||||||
|
import { Message, TextChannel, APIMessage, MessageEmbed } from 'discord.js'
|
||||||
|
import { getPrefix } from '../core/structures'
|
||||||
|
import { DiscordAPIError } from 'discord.js'
|
||||||
|
|
||||||
|
export default async function quote(message: Message) {
|
||||||
|
if (message.author.bot) return
|
||||||
|
// const message_link_regex = message.content.match(/(!)?https?:\/\/\w+\.com\/channels\/(\d+)\/(\d+)\/(\d+)/)
|
||||||
|
const message_link_regex = message.content.match(/([<!]?)https?:\/\/(?:ptb\.|canary\.|)discord(?:app)?\.com\/channels\/(\d+)\/(\d+)\/(\d+)(>?)/)
|
||||||
|
|
||||||
|
if (message_link_regex == null) return
|
||||||
|
const [, char, guildID, channelID, messageID] = message_link_regex
|
||||||
|
|
||||||
|
if (char || message.content.startsWith(getPrefix(message.guild))) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel
|
||||||
|
const link_message = await channel.messages.fetch(messageID)
|
||||||
|
|
||||||
|
let rtmsg: string | APIMessage = ''
|
||||||
|
if (link_message.cleanContent) {
|
||||||
|
rtmsg = new APIMessage(message.channel as TextChannel, {
|
||||||
|
content: link_message.cleanContent,
|
||||||
|
disableMentions: 'all',
|
||||||
|
files: link_message.attachments.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const embeds = [
|
||||||
|
...link_message.embeds.filter(v => v.type == 'rich'),
|
||||||
|
...link_message.attachments.values()
|
||||||
|
]
|
||||||
|
|
||||||
|
/// @ts-ignore
|
||||||
|
if (!link_message.cleanContent && embeds.empty) {
|
||||||
|
const Embed = new MessageEmbed()
|
||||||
|
.setDescription('🚫 The message is empty.')
|
||||||
|
return message.channel.send(Embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const infoEmbed = new MessageEmbed()
|
||||||
|
.setAuthor(
|
||||||
|
link_message.author.username,
|
||||||
|
link_message.author.displayAvatarURL({format: 'png', dynamic: true, size: 4096}))
|
||||||
|
.setTimestamp(link_message.createdTimestamp)
|
||||||
|
.setDescription(`${link_message.cleanContent}\n\nSent in **${link_message.guild?.name}** | <#${link_message.channel.id}> ([link](https://discord.com/channels/${guildID}/${channelID}/${messageID}))`);
|
||||||
|
if (link_message.attachments.size !== 0) {
|
||||||
|
const image = link_message.attachments.first();
|
||||||
|
/// @ts-ignore
|
||||||
|
infoEmbed.setImage(image.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
await message.channel.send(infoEmbed)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DiscordAPIError) {
|
||||||
|
message.channel.send("I don't have access to this channel, or something else went wrong.")
|
||||||
|
}
|
||||||
|
return console.error(error)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue