lilybird
Version:
A bun-first discord api wrapper written in TS
279 lines (278 loc) • 15.8 kB
JavaScript
import { GatewayEvent } from "#enums";
export class ListenerCompiler {
#stack;
#callbacks;
#transformers;
#shouldTransformClientUser;
constructor(options = {}) {
this.#stack = new Map();
this.#callbacks = new Map();
this.#transformers = options.transformers ?? {};
this.#shouldTransformClientUser = options.transformClient ?? false;
this.#stack.set("ready", "");
}
#createListener(event, name, extra = undefined) {
const handler = this.#callbacks.get(name);
const transformer = this.#transformers[name];
const temp = [`else if(payload.t === "${event}"){`];
const transf = `t_${name}`;
if (typeof transformer !== "undefined") {
this.#callbacks.set(transf, transformer.handler);
const spread = transformer.return === 1 ? "..." : "";
if (typeof extra !== "undefined") {
if (extra.content.includes("td.") || typeof handler !== "undefined")
temp.push(`const td = await ${transf}(client_ptr, payload.d);`);
if (extra.when === 0) {
temp.push(extra.content, typeof handler !== "undefined" ? `await ${name}(${spread}td)` : "");
}
else {
temp.push(typeof handler !== "undefined" ? `await ${name}(${spread}td)` : "", extra.content);
}
}
else if (typeof handler !== "undefined")
temp.push(`await ${name}(${spread}(await ${transf}(client_ptr, payload.d)));`);
}
else if (typeof extra !== "undefined") {
if (extra.content.includes("td."))
throw new Error(`No transformer found to apply to the caching handler '${event}'`);
if (extra.when === 0)
temp.push(extra.content, typeof handler !== "undefined" ? `await ${name}(client_ptr, payload.d);` : "");
else
temp.push(typeof handler !== "undefined" ? `await ${name}(client_ptr, payload.d);` : "", extra.content);
}
else if (typeof handler !== "undefined")
temp.push(`await ${name}(client_ptr, payload.d);`);
if (temp.length === 1)
return;
temp.push("}");
this.#stack.set(event, temp.join(""));
}
#addReadyListener(once = false, listener) {
const onceListener = this.#callbacks.get("once_ready");
const readyArr = [];
readyArr.push("if(payload.t === \"READY\"){", "Object.assign(client_ptr,{user:");
if (this.#shouldTransformClientUser && typeof this.#transformers.userUpdate !== "undefined") {
if (this.#transformers.userUpdate.return === 1)
throw new Error("The transformer for 'userUpdate' should only return 1 value");
this.#callbacks.set("t_userUpdate", this.#transformers.userUpdate.handler);
readyArr.push("await t_userUpdate(client, payload.d.user)");
}
else
readyArr.push("payload.d.user");
readyArr.push(`,sessionId:payload.d.session_id,application:payload.d.application,ready:${once ? "false" : "true"}});`, "Object.assign(resume_info_ptr,{url:payload.d.resume_gateway_url,id:payload.d.session_id});");
if (typeof listener !== "undefined")
this.#callbacks.set("ready", listener);
if (once || onceListener)
readyArr.push("if(!client_ptr.ready){client_ptr.ready=true;");
if (typeof this.#transformers.ready !== "undefined") {
this.#callbacks.set("t_ready", this.#transformers.ready.handler);
const spread = this.#transformers.ready.return === 1 ? "..." : "";
if (onceListener)
readyArr.push(`await once_ready(${spread}t_ready(client_ptr, payload.d));`);
if (once && typeof listener !== "undefined")
readyArr.push(`await ready(${spread}t_ready(client_ptr, payload.d));`);
}
else {
if (onceListener)
readyArr.push("await once_ready(client_ptr, payload.d);");
if (once && typeof listener !== "undefined")
readyArr.push("await ready(client_ptr, payload.d);");
}
if (once || onceListener)
readyArr.push("}");
if (!once && typeof listener !== "undefined") {
if (typeof this.#transformers.ready !== "undefined") {
const spread = this.#transformers.ready.return === 1 ? "..." : "";
readyArr.push(`await ready(${spread}t_ready(client_ptr, payload.d));`);
}
else
readyArr.push("await ready(client_ptr, payload.d);");
}
readyArr.push("}");
this.#stack.set("ready", readyArr.join(""));
}
appendCachingHandlers(options) {
if (typeof options !== "undefined" && options.delegate !== 2) {
if (options.delegate === 1 && options.applyTransformers === true && !options.safeToTransform) {
process.emitWarning("Transformers will be applied to external solution!", {
code: "EXTERNAL_TRANSFORMERS",
detail: "Some external caching managers might not support transformers, please read their documentation before proceeding."
});
}
if (Object.keys(options.enabled).length === 0)
throw new Error("Got unexpected empty object");
const defaults = {
create: 0,
update: 1,
delete: 1
};
const enabledGuild = options.enabled.guild === true ? defaults : typeof options.enabled.guild === "object" ? options.enabled.guild : undefined;
const enabledChannel = options.enabled.channel === true ? defaults : typeof options.enabled.channel === "object" ? options.enabled.channel : undefined;
const enabledThreads = options.enabled.channel === true
? defaults
: typeof options.enabled.channel === "object"
? options.enabled.channel.threads === true
? defaults
: typeof options.enabled.channel.threads === "object"
? options.enabled.channel.threads
: undefined
: undefined;
if (typeof options.enabled.voiceState !== "undefined" && typeof enabledGuild?.create === "undefined")
throw new Error("The 'voiceState' cache needs the `guildCreate' cache to be enabled");
if (typeof enabledGuild?.create !== "undefined") {
const temp = [];
if (typeof enabledChannel?.create !== "undefined") {
if (options.applyTransformers) {
if (!this.#callbacks.has("t_channelCreate")) {
if (typeof this.#transformers.channelCreate === "undefined")
throw Error("Missing 'channelCreate' transformer");
this.#callbacks.set("t_channelCreate", this.#transformers.channelCreate.handler);
}
}
temp.push("for (let i = 0, {channels} = td, {length} = channels; i < length; i++){", `const channel = ${options.applyTransformers ? "t_channelCreate(client_ptr, channels[i])" : "channels[i]"};`, `await client_ptr.cache.set(${1}, channel.id, channel);`, "}");
}
if (typeof enabledThreads?.create !== "undefined") {
if (options.applyTransformers) {
if (!this.#callbacks.has("t_channelCreate")) {
if (typeof this.#transformers.channelCreate === "undefined")
throw Error("Missing 'channelCreate' transformer");
this.#callbacks.set("t_channelCreate", this.#transformers.channelCreate.handler);
}
}
temp.push("for (let i = 0, {threads} = td, {length} = threads; i < length; i++){", `const channel = ${options.applyTransformers ? "t_channelCreate(client_ptr, threads[i])" : "threads[i]"};`, `await client_ptr.cache.set(${1}, channel.id, channel);`, "}");
}
if (typeof options.enabled.voiceState !== "undefined") {
if (options.applyTransformers) {
if (!this.#callbacks.has("t_voiceStateUpdate")) {
if (typeof this.#transformers.voiceStateUpdate === "undefined")
throw Error("Missing 'voiceStateUpdate' transformer");
this.#callbacks.set("t_voiceStateUpdate", this.#transformers.voiceStateUpdate.handler);
}
}
const key = options.customKeys?.guild_voice_states ?? "voice_states";
const vCid = options.customKeys?.voice_state_channel_id ?? "channel_id";
const vUid = options.customKeys?.voice_state_user_id ?? "user_id";
this.#createListener(GatewayEvent.VoiceStateUpdate, "voiceStateUpdate", {
when: options.enabled.voiceState,
content: `await client_ptr.cache.set(${2}, ${options.applyTransformers
? `\`\${td.${vUid}}:\${td.${vCid}}\``
: `\`\${payload.d.${vUid}}:\${payload.d.${vCid}}\``}, payload.d);`
});
temp.push(`for (let i = 0, {${key}} = td, {length} = ${key}; i < length; i++){`, `const voice = ${options.applyTransformers ? `t_voiceStateUpdate(client_ptr, ${key}[i])` : `${key}[i]`};`, `await client_ptr.cache.set(${2}, \`\${voice.${vUid}}:\${voice.${vCid}}\`, voice);`, "}");
}
temp.push(`await client_ptr.cache.set(${0},`, options.applyTransformers ? "td.id, td" : "payload.d.id, {...payload.d,channels: undefined,threads:undefined,voice_states:undefined}", ");");
this.#createListener(GatewayEvent.GuildCreate, "guildCreate", { when: enabledGuild.create, content: temp.join("") });
}
if (typeof enabledGuild?.update !== "undefined") {
this.#createListener(GatewayEvent.GuildUpdate, "guildUpdate", {
when: enabledGuild.update,
content: `await client_ptr.cache.set(${0}, ${options.applyTransformers ? "td.id, td" : "payload.d.id, payload.d"});`
});
}
if (typeof enabledGuild?.delete !== "undefined") {
this.#createListener(GatewayEvent.GuildDelete, "guildDelete", {
when: enabledGuild.delete,
content: `await client_ptr.cache.delete(${0}, ${options.applyTransformers ? "td.id" : "payload.d.id"});`
});
}
if (typeof enabledChannel?.create !== "undefined") {
this.#createListener(GatewayEvent.ChannelCreate, "channelCreate", {
when: enabledChannel.create,
content: `await client_ptr.cache.set(${1}, ${options.applyTransformers ? "td.id, td" : "payload.d.id, payload.d"});`
});
}
if (typeof enabledChannel?.update !== "undefined") {
this.#createListener(GatewayEvent.ChannelUpdate, "channelUpdate", {
when: enabledChannel.update,
content: `await client_ptr.cache.set(${1}, ${options.applyTransformers ? "td.id, td" : "payload.d.id, payload.d"});`
});
}
if (typeof enabledChannel?.delete !== "undefined") {
this.#createListener(GatewayEvent.ChannelDelete, "channelDelete", {
when: enabledChannel.delete,
content: `await client_ptr.cache.delete(${1}, ${options.applyTransformers ? "td.id" : "payload.d.id"});`
});
}
if (typeof enabledThreads?.create !== "undefined") {
this.#createListener(GatewayEvent.ThreadCreate, "threadCreate", {
when: enabledThreads.create,
content: `await client_ptr.cache.set(${1}, ${options.applyTransformers ? "td.id, td" : "payload.d.id, payload.d"});`
});
}
if (typeof enabledThreads?.update !== "undefined") {
this.#createListener(GatewayEvent.ThreadUpdate, "threadUpdate", {
when: enabledThreads.update,
content: `await client_ptr.cache.set(${1}, ${options.applyTransformers ? "td.id, td" : "payload.d.id, payload.d"});`
});
}
if (typeof enabledThreads?.delete !== "undefined") {
this.#createListener(GatewayEvent.ThreadDelete, "threadDelete", {
when: enabledThreads.delete,
content: `await client_ptr.cache.delete(${1}, ${options.applyTransformers ? "td.id" : "payload.d.id"});`
});
}
if (typeof options.enabled.self !== "undefined") {
if (options.applyTransformers) {
if (!this.#callbacks.has("t_userUpdate")) {
if (typeof this.#transformers.userUpdate === "undefined")
throw new Error("Missing 'userUpdate' transformer");
if (this.#transformers.userUpdate.return === 1)
throw new Error("The transformer for 'userUpdate' should only return 1 value");
this.#callbacks.set("t_userUpdate", this.#transformers.userUpdate.handler);
}
}
this.#createListener(GatewayEvent.UserUpdate, "userUpdate", {
when: options.enabled.self,
content: `client_ptr.user = ${options.applyTransformers ? "td" : "payload.d"}`
});
}
}
return this;
}
addListener(name, handler, once = false) {
if (name === "setup") {
this.#callbacks.set("once_ready", handler);
return this;
}
else if (name === "ready") {
this.#addReadyListener(once, handler);
return this;
}
const parsedName = `${name[0].toUpperCase()}${name.slice(1)}`;
const event = GatewayEvent[parsedName];
this.#callbacks.set(name, handler);
this.#createListener(event, name);
return this;
}
getCompilationOutput() {
const hasReadyListener = this.#stack.get("ready") !== "";
if (!hasReadyListener)
this.#addReadyListener();
const names = [...this.#callbacks.keys()];
const handlers = [...this.#callbacks.values()];
const compiledListeners = [...this.#stack.values()].join("");
return {
handlers: {
names,
callbacks: handlers
},
stack: compiledListeners
};
}
clearStack() {
this.#stack.clear();
this.#callbacks.clear();
this.#stack.set("ready", "");
}
getDispatchFunction(clientPointer, resumeInfoPointer) {
const { handlers, stack } = this.getCompilationOutput();
return new Function("client_ptr", "resume_info_ptr", ...handlers.names, `return async (payload) => { ${stack} }`)(clientPointer, resumeInfoPointer, ...handlers.callbacks);
}
addListenersFromObject(listeners) {
for (let i = 0, entries = Object.entries(listeners), { length } = entries; i < length; i++) {
const [event, handler] = entries[i];
this.addListener(event, handler);
}
return this;
}
}