mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Reworked event loading
This commit is contained in:
parent
3ef487c4a4
commit
945102b7cf
18 changed files with 272 additions and 344 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,7 +1,6 @@
|
|||
# Specific to this repository
|
||||
dist/
|
||||
data/*
|
||||
!data/endpoints.json
|
||||
data/
|
||||
tmp/
|
||||
test*
|
||||
!test/
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import {Client, ClientEvents, Constants} from "discord.js";
|
||||
import Storage from "./storage";
|
||||
|
||||
interface EventOptions<K extends keyof ClientEvents> {
|
||||
readonly on?: (...args: ClientEvents[K]) => void;
|
||||
readonly once?: (...args: ClientEvents[K]) => void;
|
||||
}
|
||||
|
||||
export default class Event<K extends keyof ClientEvents> {
|
||||
private readonly on?: (...args: ClientEvents[K]) => void;
|
||||
private readonly once?: (...args: ClientEvents[K]) => void;
|
||||
|
||||
constructor(options: EventOptions<K>) {
|
||||
this.on = options.on;
|
||||
this.once = options.once;
|
||||
}
|
||||
|
||||
// For this function, I'm going to assume that the event is used with the correct arguments and that the event tag is checked in "storage.ts".
|
||||
public attach(client: Client, event: K) {
|
||||
if (this.on) client.on(event, this.on);
|
||||
if (this.once) client.once(event, this.once);
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadEvents(client: Client) {
|
||||
for (const file of Storage.open("dist/events", (filename: string) => filename.endsWith(".js"))) {
|
||||
const header = file.substring(0, file.indexOf(".js"));
|
||||
const event = (await import(`../events/${header}`)).default;
|
||||
|
||||
if ((Object.values(Constants.Events) as string[]).includes(header)) {
|
||||
event.attach(client, header);
|
||||
console.log(`Loading Event: ${header}`);
|
||||
} else
|
||||
console.warn(
|
||||
`"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)`
|
||||
);
|
||||
}
|
||||
}
|
158
src/core/handler.ts
Normal file
158
src/core/handler.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
import {client} from "../index";
|
||||
import Command, {loadableCommands} from "../core/command";
|
||||
import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions";
|
||||
import {Permissions} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import {replyEventListeners} from "../core/libd";
|
||||
import quote from "../modules/message_embed";
|
||||
import {Config} from "../core/structures";
|
||||
|
||||
client.on("message", async (message) => {
|
||||
const commands = await loadableCommands;
|
||||
|
||||
if (message.content.toLowerCase().includes("remember to drink water")) {
|
||||
message.react("🚱");
|
||||
}
|
||||
|
||||
// Message Setup //
|
||||
if (message.author.bot) return;
|
||||
|
||||
// If there's an inline reply, fire off that event listener (if it exists).
|
||||
if (message.reference) {
|
||||
const reference = message.reference;
|
||||
replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message);
|
||||
}
|
||||
|
||||
let prefix = getPrefix(message.guild);
|
||||
const originalPrefix = prefix;
|
||||
let exitEarly = !message.content.startsWith(prefix);
|
||||
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).
|
||||
// The pattern here has an optional space at the end to capture that and make it not mess with the header and args.
|
||||
const matches = message.content.match(new RegExp(`^<@!?${clientUser.id}> ?`));
|
||||
|
||||
if (matches) {
|
||||
prefix = matches[0];
|
||||
exitEarly = false;
|
||||
usesBotSpecificPrefix = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If it doesn't start with the current normal prefix or the bot-specific unique prefix, exit the thread of execution early.
|
||||
// Inline replies should still be captured here because if it doesn't exit early, two characters for a two-length prefix would still trigger commands.
|
||||
if (exitEarly) return;
|
||||
|
||||
const [header, ...args] = message.content.substring(prefix.length).split(/ +/);
|
||||
|
||||
// If the message is just the prefix itself, move onto this block.
|
||||
if (header === "" && args.length === 0) {
|
||||
// I moved the bot-specific prefix to a separate conditional block to separate the logic.
|
||||
// And because it listens for the mention as a prefix instead of a free-form mention, inline replies (probably) shouldn't ever trigger this unintentionally.
|
||||
if (usesBotSpecificPrefix) {
|
||||
message.channel.send(`${message.author.toString()}, my prefix on this guild is \`${originalPrefix}\`.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!commands.has(header)) return;
|
||||
|
||||
if (
|
||||
message.channel.type === "text" &&
|
||||
!message.channel.permissionsFor(message.client.user || "")?.has(Permissions.FLAGS.SEND_MESSAGES)
|
||||
) {
|
||||
let status;
|
||||
|
||||
if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR))
|
||||
status =
|
||||
"Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended.";
|
||||
else
|
||||
status =
|
||||
"Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong.";
|
||||
|
||||
return message.author.send(
|
||||
`I don't have permission to send messages in ${message.channel.toString()}. ${status}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".`
|
||||
);
|
||||
|
||||
// Subcommand Recursion //
|
||||
let command = commands.get(header);
|
||||
if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`);
|
||||
const params: any[] = [];
|
||||
let isEndpoint = false;
|
||||
let permLevel = command.permission ?? 0;
|
||||
|
||||
for (let param of args) {
|
||||
if (command.endpoint) {
|
||||
if (command.subcommands.size > 0 || command.user || command.number || command.any)
|
||||
console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`);
|
||||
isEndpoint = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const type = command.resolve(param);
|
||||
command = command.get(param);
|
||||
permLevel = command.permission ?? permLevel;
|
||||
|
||||
if (type === Command.TYPES.USER) {
|
||||
const id = param.match(/\d+/g)![0];
|
||||
try {
|
||||
params.push(await message.client.users.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No user found by the ID \`${id}\`!`);
|
||||
}
|
||||
} else if (type === Command.TYPES.NUMBER) params.push(Number(param));
|
||||
else if (type !== Command.TYPES.SUBCOMMAND) params.push(param);
|
||||
}
|
||||
|
||||
if (!message.member)
|
||||
return console.warn("This command was likely called from a DM channel meaning the member object is null.");
|
||||
|
||||
if (!hasPermission(message.member, permLevel)) {
|
||||
const userPermLevel = getPermissionLevel(message.member);
|
||||
return message.channel.send(
|
||||
`You don't have access to this command! Your permission level is \`${getPermissionName(
|
||||
userPermLevel
|
||||
)}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName(
|
||||
permLevel
|
||||
)}\` (${permLevel}).`
|
||||
);
|
||||
}
|
||||
|
||||
if (isEndpoint) return message.channel.send("Too many arguments!");
|
||||
|
||||
// Execute with dynamic library attached. //
|
||||
// The purpose of using $.bind($) is to clone the function so as to not modify the original $.
|
||||
// The cloned function doesn't copy the properties, so Object.assign() is used.
|
||||
// Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one.
|
||||
command.execute({
|
||||
args: params,
|
||||
author: message.author,
|
||||
channel: message.channel,
|
||||
client: message.client,
|
||||
guild: message.guild,
|
||||
member: message.member,
|
||||
message: message
|
||||
});
|
||||
});
|
||||
|
||||
client.once("ready", () => {
|
||||
if (client.user) {
|
||||
console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`);
|
||||
client.user.setActivity({
|
||||
type: "LISTENING",
|
||||
name: `${Config.prefix}help`
|
||||
});
|
||||
}
|
||||
});
|
|
@ -9,36 +9,25 @@ import {
|
|||
NewsChannel,
|
||||
MessageOptions
|
||||
} from "discord.js";
|
||||
import FileManager from "./storage";
|
||||
import {eventListeners} from "../events/messageReactionRemove";
|
||||
import {client} from "../index";
|
||||
import {EmoteRegistryDump} from "./structures";
|
||||
|
||||
// A list of message ID and callback pairs. You get the emote name and ID of the user reacting.
|
||||
const eventListeners: Map<string, (emote: string, id: string) => void> = new Map();
|
||||
|
||||
// Attached to the client, there can be one event listener attached to a message ID which is executed if present.
|
||||
client.on("messageReactionRemove", (reaction, user) => {
|
||||
const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES);
|
||||
|
||||
if (!canDeleteEmotes) {
|
||||
const callback = eventListeners.get(reaction.message.id);
|
||||
callback && callback(reaction.emoji.name, user.id);
|
||||
}
|
||||
});
|
||||
|
||||
export function botHasPermission(guild: Guild | null, permission: number): boolean {
|
||||
return !!guild?.me?.hasPermission(permission);
|
||||
}
|
||||
|
||||
export function updateGlobalEmoteRegistry(): void {
|
||||
const data: EmoteRegistryDump = {version: 1, list: []};
|
||||
|
||||
for (const guild of client.guilds.cache.values()) {
|
||||
for (const emote of guild.emojis.cache.values()) {
|
||||
data.list.push({
|
||||
ref: emote.name,
|
||||
id: emote.id,
|
||||
name: emote.name,
|
||||
requires_colons: emote.requiresColons || false,
|
||||
animated: emote.animated,
|
||||
url: emote.url,
|
||||
guild_id: emote.guild.name,
|
||||
guild_name: emote.guild.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
FileManager.write("emote-registry", data, true);
|
||||
}
|
||||
|
||||
// Maybe promisify this section to reduce the potential for creating callback hell? Especially if multiple questions in a row are being asked.
|
||||
|
||||
// Pagination function that allows for customization via a callback.
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {client} from "../index";
|
||||
import * as discord from "discord.js";
|
||||
|
||||
export default new Event<"channelCreate">({
|
||||
async on(channel) {
|
||||
const botGuilds = client.guilds;
|
||||
if (channel instanceof discord.GuildChannel) {
|
||||
const createdGuild = await botGuilds.fetch(channel.guild.id);
|
||||
console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {client} from "../index";
|
||||
import * as discord from "discord.js";
|
||||
|
||||
export default new Event<"channelDelete">({
|
||||
async on(channel) {
|
||||
const botGuilds = client.guilds;
|
||||
if (channel instanceof discord.GuildChannel) {
|
||||
const createdGuild = await botGuilds.fetch(channel.guild.id);
|
||||
console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"emojiCreate">({
|
||||
on(emote) {
|
||||
console.log(`Updated emote registry. ${emote.name}`);
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"emojiDelete">({
|
||||
on() {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"emojiUpdate">({
|
||||
on() {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"guildCreate">({
|
||||
on() {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"guildDelete">({
|
||||
on() {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
|
@ -1,149 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import Command, {loadableCommands} from "../core/command";
|
||||
import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions";
|
||||
import {Permissions} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import {replyEventListeners} from "../core/libd";
|
||||
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;
|
||||
|
||||
// If there's an inline reply, fire off that event listener (if it exists).
|
||||
if (message.reference) {
|
||||
const reference = message.reference;
|
||||
replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message);
|
||||
}
|
||||
|
||||
let prefix = getPrefix(message.guild);
|
||||
const originalPrefix = prefix;
|
||||
let exitEarly = !message.content.startsWith(prefix);
|
||||
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).
|
||||
// The pattern here has an optional space at the end to capture that and make it not mess with the header and args.
|
||||
const matches = message.content.match(new RegExp(`^<@!?${clientUser.id}> ?`));
|
||||
|
||||
if (matches) {
|
||||
prefix = matches[0];
|
||||
exitEarly = false;
|
||||
usesBotSpecificPrefix = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If it doesn't start with the current normal prefix or the bot-specific unique prefix, exit the thread of execution early.
|
||||
// Inline replies should still be captured here because if it doesn't exit early, two characters for a two-length prefix would still trigger commands.
|
||||
if (exitEarly) return;
|
||||
|
||||
const [header, ...args] = message.content.substring(prefix.length).split(/ +/);
|
||||
|
||||
// If the message is just the prefix itself, move onto this block.
|
||||
if (header === "" && args.length === 0) {
|
||||
// I moved the bot-specific prefix to a separate conditional block to separate the logic.
|
||||
// And because it listens for the mention as a prefix instead of a free-form mention, inline replies (probably) shouldn't ever trigger this unintentionally.
|
||||
if (usesBotSpecificPrefix) {
|
||||
message.channel.send(`${message.author.toString()}, my prefix on this guild is \`${originalPrefix}\`.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!commands.has(header)) return;
|
||||
|
||||
if (
|
||||
message.channel.type === "text" &&
|
||||
!message.channel.permissionsFor(message.client.user || "")?.has(Permissions.FLAGS.SEND_MESSAGES)
|
||||
) {
|
||||
let status;
|
||||
|
||||
if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR))
|
||||
status =
|
||||
"Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended.";
|
||||
else
|
||||
status =
|
||||
"Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong.";
|
||||
|
||||
return message.author.send(
|
||||
`I don't have permission to send messages in ${message.channel.toString()}. ${status}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".`
|
||||
);
|
||||
|
||||
// Subcommand Recursion //
|
||||
let command = commands.get(header);
|
||||
if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`);
|
||||
const params: any[] = [];
|
||||
let isEndpoint = false;
|
||||
let permLevel = command.permission ?? 0;
|
||||
|
||||
for (let param of args) {
|
||||
if (command.endpoint) {
|
||||
if (command.subcommands.size > 0 || command.user || command.number || command.any)
|
||||
console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`);
|
||||
isEndpoint = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const type = command.resolve(param);
|
||||
command = command.get(param);
|
||||
permLevel = command.permission ?? permLevel;
|
||||
|
||||
if (type === Command.TYPES.USER) {
|
||||
const id = param.match(/\d+/g)![0];
|
||||
try {
|
||||
params.push(await message.client.users.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No user found by the ID \`${id}\`!`);
|
||||
}
|
||||
} else if (type === Command.TYPES.NUMBER) params.push(Number(param));
|
||||
else if (type !== Command.TYPES.SUBCOMMAND) params.push(param);
|
||||
}
|
||||
|
||||
if (!message.member)
|
||||
return console.warn("This command was likely called from a DM channel meaning the member object is null.");
|
||||
|
||||
if (!hasPermission(message.member, permLevel)) {
|
||||
const userPermLevel = getPermissionLevel(message.member);
|
||||
return message.channel.send(
|
||||
`You don't have access to this command! Your permission level is \`${getPermissionName(
|
||||
userPermLevel
|
||||
)}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName(
|
||||
permLevel
|
||||
)}\` (${permLevel}).`
|
||||
);
|
||||
}
|
||||
|
||||
if (isEndpoint) return message.channel.send("Too many arguments!");
|
||||
|
||||
// Execute with dynamic library attached. //
|
||||
// The purpose of using $.bind($) is to clone the function so as to not modify the original $.
|
||||
// The cloned function doesn't copy the properties, so Object.assign() is used.
|
||||
// Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one.
|
||||
command.execute({
|
||||
args: params,
|
||||
author: message.author,
|
||||
channel: message.channel,
|
||||
client: message.client,
|
||||
guild: message.guild,
|
||||
member: message.member,
|
||||
message: message
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {Permissions} from "discord.js";
|
||||
import {botHasPermission} from "../core/libd";
|
||||
|
||||
// A list of message ID and callback pairs. You get the emote name and ID of the user reacting.
|
||||
export const eventListeners: Map<string, (emote: string, id: string) => void> = new Map();
|
||||
|
||||
// Attached to the client, there can be one event listener attached to a message ID which is executed if present.
|
||||
export default new Event<"messageReactionRemove">({
|
||||
on(reaction, user) {
|
||||
const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES);
|
||||
|
||||
if (!canDeleteEmotes) {
|
||||
const callback = eventListeners.get(reaction.message.id);
|
||||
callback && callback(reaction.emoji.name, user.id);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import Event from "../core/event";
|
||||
import {client} from "../index";
|
||||
import {Config} from "../core/structures";
|
||||
import {updateGlobalEmoteRegistry} from "../core/libd";
|
||||
|
||||
export default new Event<"ready">({
|
||||
once() {
|
||||
if (client.user) {
|
||||
console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`);
|
||||
client.user.setActivity({
|
||||
type: "LISTENING",
|
||||
name: `${Config.prefix}help`
|
||||
});
|
||||
}
|
||||
updateGlobalEmoteRegistry();
|
||||
}
|
||||
});
|
13
src/index.ts
13
src/index.ts
|
@ -1,17 +1,20 @@
|
|||
// Bootstrapping Section //
|
||||
import "./globals";
|
||||
import {Client} from "discord.js";
|
||||
import setup from "./setup";
|
||||
import {Config} from "./core/structures";
|
||||
import {loadEvents} from "./core/event";
|
||||
import {attachToClient} from "./modules/lavalink";
|
||||
|
||||
// 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();
|
||||
attachToClient(client);
|
||||
|
||||
// Command loading will start as soon as an instance of "core/command" is loaded, which is loaded during "events/message".
|
||||
// Send the login request to Discord's API and then load modules while waiting for it.
|
||||
setup.init().then(() => {
|
||||
loadEvents(client);
|
||||
client.login(Config.token).catch(setup.again);
|
||||
});
|
||||
|
||||
// Initialize Modules //
|
||||
import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler".
|
||||
import "./modules/lavalink";
|
||||
import "./modules/emoteRegistry";
|
||||
import "./modules/channelListener";
|
||||
|
|
20
src/modules/channelListener.ts
Normal file
20
src/modules/channelListener.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {client} from "../index";
|
||||
import {GuildChannel} from "discord.js";
|
||||
|
||||
client.on("channelCreate", async (channel) => {
|
||||
const botGuilds = client.guilds;
|
||||
|
||||
if (channel instanceof GuildChannel) {
|
||||
const createdGuild = await botGuilds.fetch(channel.guild.id);
|
||||
console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`);
|
||||
}
|
||||
});
|
||||
|
||||
client.on("channelDelete", async (channel) => {
|
||||
const botGuilds = client.guilds;
|
||||
|
||||
if (channel instanceof GuildChannel) {
|
||||
const createdGuild = await botGuilds.fetch(channel.guild.id);
|
||||
console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`);
|
||||
}
|
||||
});
|
53
src/modules/emoteRegistry.ts
Normal file
53
src/modules/emoteRegistry.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {client} from "../index";
|
||||
import FileManager from "../core/storage";
|
||||
import {EmoteRegistryDump} from "../core/structures";
|
||||
|
||||
function updateGlobalEmoteRegistry(): void {
|
||||
const data: EmoteRegistryDump = {version: 1, list: []};
|
||||
|
||||
for (const guild of client.guilds.cache.values()) {
|
||||
for (const emote of guild.emojis.cache.values()) {
|
||||
data.list.push({
|
||||
ref: emote.name,
|
||||
id: emote.id,
|
||||
name: emote.name,
|
||||
requires_colons: emote.requiresColons || false,
|
||||
animated: emote.animated,
|
||||
url: emote.url,
|
||||
guild_id: emote.guild.name,
|
||||
guild_name: emote.guild.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
FileManager.write("emote-registry", data, true);
|
||||
}
|
||||
|
||||
client.on("emojiCreate", (emote) => {
|
||||
console.log(`Updated emote registry. ${emote.name}`);
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
||||
|
||||
client.on("emojiDelete", () => {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
||||
|
||||
client.on("emojiUpdate", () => {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
||||
|
||||
client.on("guildCreate", () => {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
||||
|
||||
client.on("guildDelete", () => {
|
||||
console.log("Updated emote registry.");
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
||||
|
||||
client.on("ready", () => {
|
||||
updateGlobalEmoteRegistry();
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import {Presence, Client} from "discord.js";
|
||||
import {Presence} from "discord.js";
|
||||
import LavalinkMusic from "discord.js-lavalink-lib";
|
||||
import {Config} from "../core/structures";
|
||||
import {client} from "../index";
|
||||
|
||||
declare module "discord.js" {
|
||||
interface Presence {
|
||||
|
@ -28,10 +29,9 @@ Presence.prototype.patch = function patch(data: any) {
|
|||
// 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>.
|
||||
export function attachToClient(client: Client) {
|
||||
(client["actions"] as any)["PresenceUpdate"].handle = () => {};
|
||||
(client["actions"] as any)["PresenceUpdate"].handle = () => {};
|
||||
|
||||
(client as any).music = LavalinkMusic(client, {
|
||||
(client as any).music = LavalinkMusic(client, {
|
||||
lavalink: {
|
||||
restnode: {
|
||||
host: "localhost",
|
||||
|
@ -49,5 +49,4 @@ export function attachToClient(client: Client) {
|
|||
prefix: Config.prefix,
|
||||
helpCmd: "mhelp",
|
||||
admins: ["717352467280691331"]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue