Merge branch 'typescript' of https://github.com/keanuplayz/TravBot-v3 into experimental-core
This commit is contained in:
commit
00addd468c
File diff suppressed because it is too large
Load Diff
|
@ -10,8 +10,7 @@
|
|||
"glob": "^7.1.6",
|
||||
"inquirer": "^7.3.3",
|
||||
"moment": "^2.29.1",
|
||||
"ms": "^2.1.3",
|
||||
"os": "^0.1.1"
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.3",
|
||||
|
|
|
@ -124,14 +124,15 @@ export default new Command({
|
|||
number: new Command({
|
||||
description: "Amount of messages to delete.",
|
||||
async run($: CommonLibrary): Promise<any> {
|
||||
if ($.channel.type === "dm") {
|
||||
await $.channel.send("Can't clear messages in the DMs!");
|
||||
return;
|
||||
}
|
||||
$.message.delete();
|
||||
const fetched = await $.channel.messages.fetch({
|
||||
limit: $.args[0]
|
||||
});
|
||||
$.channel
|
||||
/// @ts-ignore
|
||||
.bulkDelete(fetched)
|
||||
.catch((error: any) => $.channel.send(`Error: ${error}`));
|
||||
await $.channel.bulkDelete(fetched);
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
@ -157,8 +158,7 @@ export default new Command({
|
|||
permission: Command.PERMISSIONS.BOT_SUPPORT,
|
||||
async run($: CommonLibrary): Promise<any> {
|
||||
const nickName = $.args.join(" ");
|
||||
const trav = $.guild?.members.cache.find((member) => member.id === $.client.user?.id);
|
||||
await trav?.setNickname(nickName);
|
||||
await $.guild?.me?.setNickname(nickName);
|
||||
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES))
|
||||
$.message.delete({timeout: 5000}).catch($.handler.bind($));
|
||||
$.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 {
|
||||
if (guild?.id === "637512823676600330" || process.argv[2] === "dev") return true;
|
||||
if (guild?.id === "637512823676600330" && channel?.id === "669464416420364288" || process.argv[2] === "dev") return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import {CommonLibrary, formatBytes, trimArray} from "../core/lib";
|
|||
import {verificationLevels, filterLevels, regions, flags} from "../defs/info";
|
||||
import moment from "moment";
|
||||
import utc from "moment";
|
||||
import {Guild} from "discord.js";
|
||||
|
||||
const {version} = require("../../package.json");
|
||||
|
||||
export default new Command({
|
||||
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> {
|
||||
const core = os.cpus()[0];
|
||||
const embed = new MessageEmbed()
|
||||
.setThumbnail(
|
||||
/// @ts-ignore
|
||||
$.client.user?.displayAvatarURL({
|
||||
dynamic: true,
|
||||
size: 2048
|
||||
})
|
||||
)
|
||||
.setColor($.guild?.me?.displayHexColor || "BLUE")
|
||||
.addField("General", [
|
||||
`**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`,
|
||||
|
@ -66,84 +62,59 @@ export default new Command({
|
|||
`\u3000 • Speed: ${core.speed}MHz`,
|
||||
`**❯ Memory:**`,
|
||||
`\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`,
|
||||
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapTotal)}`
|
||||
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}`
|
||||
])
|
||||
.setTimestamp();
|
||||
const avatarURL = $.client.user?.displayAvatarURL({
|
||||
dynamic: true,
|
||||
size: 2048
|
||||
});
|
||||
if (avatarURL) embed.setThumbnail(avatarURL);
|
||||
$.channel.send(embed);
|
||||
}
|
||||
}),
|
||||
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> {
|
||||
if ($.guild) {
|
||||
const roles = $.guild.roles.cache
|
||||
.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);
|
||||
$.channel.send(await getGuildInfo($.guild, $.guild));
|
||||
} else {
|
||||
$.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({
|
||||
description: "Displays info about mentioned user.",
|
||||
async run($: CommonLibrary): Promise<any> {
|
||||
// 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)
|
||||
return $.channel.send(
|
||||
|
@ -154,8 +125,7 @@ export default new Command({
|
|||
.sort((a: {position: number}, b: {position: number}) => b.position - a.position)
|
||||
.map((role: {toString: () => any}) => role.toString())
|
||||
.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()) as UserFlags).toArray();
|
||||
const userFlags = (await member.user.fetchFlags()).toArray();
|
||||
|
||||
const embed = new MessageEmbed()
|
||||
.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 (!$.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.");
|
||||
|
||||
if ($.args.length === 0) return $.channel.send("Please provide a new voice channel name.");
|
||||
|
||||
const changeVC = $.guild.channels.resolve(voiceChannel.id);
|
||||
$.channel
|
||||
.send(`Changed channel name from "${voiceChannel}" to "${$.args.join(" ")}".`)
|
||||
/// @ts-ignore
|
||||
.then(changeVC?.setName($.args.join(" ")));
|
||||
const prevName = voiceChannel.name;
|
||||
const newName = $.args.join(" ");
|
||||
await voiceChannel.setName(newName);
|
||||
await $.channel.send(`Changed channel name from "${prevName}" to "${newName}".`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -12,7 +12,6 @@ export default new Command({
|
|||
async run({guild, channel, message, args}) {
|
||||
let output = "";
|
||||
for (const query of args) output += queryClosestEmoteByName(query).toString();
|
||||
if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) message.delete();
|
||||
channel.send(output);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,32 +1,117 @@
|
|||
import {GuildEmoji} from "discord.js";
|
||||
import {MessageEmbed} from "discord.js";
|
||||
import Command from "../../core/command";
|
||||
import {CommonLibrary} from "../../core/lib";
|
||||
import vm from "vm";
|
||||
|
||||
const REGEX_TIMEOUT_MS = 1000;
|
||||
|
||||
export default new Command({
|
||||
description: "Lists all emotes the bot has in it's registry,",
|
||||
endpoint: true,
|
||||
usage: "<regex pattern> (-flags)",
|
||||
async run($: CommonLibrary): Promise<any> {
|
||||
const nsfw: string | string[] = [];
|
||||
const pages = $.client.emojis.cache.filter((x) => !nsfw.includes(x.guild.id), this).array();
|
||||
const pagesSplit = $(pages).split(20);
|
||||
$.log(pagesSplit);
|
||||
var embed = new MessageEmbed().setTitle("**Emoji list!**").setColor("AQUA");
|
||||
let desc = "";
|
||||
displayEmoteList($, $.client.emojis.cache.array());
|
||||
},
|
||||
any: new Command({
|
||||
description:
|
||||
"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",
|
||||
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]) {
|
||||
desc += `${emote} | ${emote.name}\n`;
|
||||
displayEmoteList($, $.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array());
|
||||
} 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);
|
||||
const msg = await $.channel.send({embed});
|
||||
|
||||
$.paginate(msg, $.author.id, pages.length, (page) => {
|
||||
let desc = "";
|
||||
for (const emote of pagesSplit[page]) {
|
||||
desc += `${emote} | ${emote.name}\n`;
|
||||
}
|
||||
embed.setDescription(desc);
|
||||
msg.edit(embed);
|
||||
});
|
||||
if (pages > 1) {
|
||||
embed.setTitle(`**Emotes** (Page 1 of ${pages})`);
|
||||
const msg = await $.channel.send({embed});
|
||||
|
||||
$.paginate(msg, $.author.id, pages, (page) => {
|
||||
let desc = "";
|
||||
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 {
|
||||
return !!(client.user && guild?.members.resolve(client.user)?.hasPermission(permission));
|
||||
return !!guild?.me?.hasPermission(permission);
|
||||
}
|
||||
|
||||
export function updateGlobalEmoteRegistry(): void {
|
||||
|
@ -212,20 +212,22 @@ $.paginate = async (
|
|||
|
||||
callback(page);
|
||||
};
|
||||
const BACKWARDS_EMOJI = "⬅️";
|
||||
const FORWARDS_EMOJI = "➡️";
|
||||
const handle = (emote: string, reacterID: string) => {
|
||||
switch (emote) {
|
||||
case "⬅️":
|
||||
case BACKWARDS_EMOJI:
|
||||
turn(-1);
|
||||
break;
|
||||
case "➡️":
|
||||
case FORWARDS_EMOJI:
|
||||
turn(1);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for reactions and call the handler.
|
||||
await message.react("⬅️");
|
||||
await message.react("➡️");
|
||||
let backwardsReaction = await message.react(BACKWARDS_EMOJI);
|
||||
let forwardsReaction = await message.react(FORWARDS_EMOJI);
|
||||
eventListeners.set(message.id, handle);
|
||||
await message.awaitReactions(
|
||||
(reaction, user) => {
|
||||
|
@ -244,8 +246,8 @@ $.paginate = async (
|
|||
);
|
||||
// When time's up, remove the bot's own reactions.
|
||||
eventListeners.delete(message.id);
|
||||
message.reactions.cache.get("⬅️")?.users.remove(message.author);
|
||||
message.reactions.cache.get("➡️")?.users.remove(message.author);
|
||||
backwardsReaction.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).
|
||||
|
|
|
@ -4,11 +4,16 @@ import {hasPermission, getPermissionLevel, PermissionNames} from "../core/permis
|
|||
import {Permissions} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import $, {replyEventListeners} from "../core/lib";
|
||||
import quote from "../modules/message_embed";
|
||||
|
||||
export default new Event<"message">({
|
||||
async on(message) {
|
||||
const commands = await loadableCommands;
|
||||
|
||||
if (message.content.toLowerCase().includes("remember to drink water")) {
|
||||
message.react("🚱");
|
||||
}
|
||||
|
||||
// Message Setup //
|
||||
if (message.author.bot) return;
|
||||
|
||||
|
@ -24,6 +29,10 @@ export default new Event<"message">({
|
|||
const clientUser = message.client.user;
|
||||
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 (clientUser) {
|
||||
// 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 {Config} from "./core/structures";
|
||||
import {loadEvents} from "./core/event";
|
||||
import "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 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, {
|
||||
lavalink: {
|
||||
|
|
|
@ -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…
Reference in New Issue