@itsmapleleaf/gatekeeper
Advanced tools
Comparing version 0.0.0-alpha.2 to 0.0.0-alpha.3
import type { CommandInteraction, GuildMember, MessageComponentInteraction } from "discord.js"; | ||
import type { AsyncQueue } from "../internal/async-queue.js"; | ||
import { ReplyComponentArgs } from "./reply-component.js"; | ||
import { ReplyComponent } from "./reply-component.js"; | ||
import type { ReplyManager } from "./reply-instance.js"; | ||
export declare type ComponentInteraction = { | ||
@@ -14,14 +14,18 @@ customId: string; | ||
}; | ||
declare type RenderReplyFn = () => ReplyComponent[] | undefined; | ||
export declare type CommandHandlerContext = { | ||
member: GuildMember; | ||
addReply: (...components: ReplyComponentArgs) => Promise<CommandReply>; | ||
addEphemeralReply: (...components: ReplyComponentArgs) => Promise<EphemeralCommandReply>; | ||
defer: () => Promise<CommandReply>; | ||
createReply: (render: RenderReplyFn) => Promise<CommandReplyHandle>; | ||
createEphemeralReply: (render: RenderReplyFn) => Promise<EphemeralCommandReplyHandle>; | ||
}; | ||
export declare type EphemeralCommandReply = { | ||
edit: (...components: ReplyComponentArgs) => Promise<void>; | ||
export declare type ReplyInstance = { | ||
handleMessageComponentInteraction(interaction: MessageComponentInteraction): Promise<void>; | ||
}; | ||
export declare type CommandReply = EphemeralCommandReply & { | ||
export declare type EphemeralCommandReplyHandle = { | ||
update: () => Promise<void>; | ||
}; | ||
export declare type CommandReplyHandle = EphemeralCommandReplyHandle & { | ||
delete: () => Promise<void>; | ||
}; | ||
export declare function createCommandHandlerContext(interaction: CommandInteraction, member: GuildMember, interactionQueue: AsyncQueue<MessageComponentInteraction>): CommandHandlerContext; | ||
export declare function createCommandHandlerContext(interaction: CommandInteraction, member: GuildMember, replyManager: ReplyManager): CommandHandlerContext; | ||
export {}; |
@@ -6,46 +6,102 @@ "use strict"; | ||
const reply_component_js_1 = require("./reply-component.js"); | ||
const isActionRow = (c) => (0, helpers_js_1.isObject)(c) && c.type === "actionRow"; | ||
function createCommandHandlerContext(interaction, member, interactionQueue) { | ||
const isActionRow = (component) => (0, helpers_js_1.isObject)(component) && component.type === "actionRow"; | ||
function createCommandHandlerContext(interaction, member, replyManager) { | ||
return { | ||
member, | ||
async addReply(...components) { | ||
const processResult = (0, reply_component_js_1.processReplyComponents)(components); | ||
const message = await addOrCreateReply(interaction, processResult.replyOptions); | ||
await handleMessageComponentInteraction(processResult.messageComponentIds, interactionQueue); | ||
async createReply(render) { | ||
let components = render(); | ||
if (!components) { | ||
return { | ||
async delete() { }, | ||
async update() { }, | ||
}; | ||
} | ||
const message = (await addReply(interaction, (0, reply_component_js_1.createInteractionReplyOptions)(components))); | ||
async function rerender() { | ||
components = render(); | ||
if (components) { | ||
await message.edit((0, reply_component_js_1.createInteractionReplyOptions)(components)); | ||
} | ||
else { | ||
replyManager.remove(instance); | ||
try { | ||
await message.delete(); | ||
} | ||
catch (_a) { } | ||
} | ||
} | ||
const instance = replyManager.add({ | ||
async handleMessageComponentInteraction(interaction) { | ||
const matchingComponents = components === null || components === void 0 ? void 0 : components.filter(isActionRow).flatMap((c) => c.children).filter((c) => c.customId === interaction.customId); | ||
if (!(matchingComponents === null || matchingComponents === void 0 ? void 0 : matchingComponents.length)) | ||
return; | ||
for (const component of matchingComponents !== null && matchingComponents !== void 0 ? matchingComponents : []) { | ||
if (component.type === "button" && interaction.isButton()) { | ||
await component.onClick(); | ||
} | ||
if (component.type === "selectMenu" && interaction.isSelectMenu()) { | ||
await component.onSelect(interaction.values); | ||
} | ||
} | ||
await rerender(); | ||
}, | ||
}); | ||
return { | ||
async edit(...components) { | ||
const processResult = (0, reply_component_js_1.processReplyComponents)(components); | ||
await message.edit(processResult.replyOptions); | ||
await handleMessageComponentInteraction(processResult.messageComponentIds, interactionQueue); | ||
}, | ||
async delete() { | ||
await message.delete(); | ||
replyManager.remove(instance); | ||
try { | ||
await message.delete(); | ||
} | ||
catch (_a) { } | ||
}, | ||
async update() { | ||
await rerender(); | ||
}, | ||
}; | ||
}, | ||
async addEphemeralReply(...components) { | ||
const processResult = (0, reply_component_js_1.processReplyComponents)(components); | ||
const message = await addOrCreateReply(interaction, Object.assign(Object.assign({}, (0, reply_component_js_1.processReplyComponents)(components)), { ephemeral: true })); | ||
await handleMessageComponentInteraction(processResult.messageComponentIds, interactionQueue); | ||
return { | ||
async edit(...components) { | ||
const processResult = (0, reply_component_js_1.processReplyComponents)(components); | ||
await message.edit(processResult.replyOptions); | ||
await handleMessageComponentInteraction(processResult.messageComponentIds, interactionQueue); | ||
async createEphemeralReply(render) { | ||
let components = render(); | ||
if (!components) { | ||
return { | ||
async update() { }, | ||
}; | ||
} | ||
const options = Object.assign(Object.assign({}, (0, reply_component_js_1.createInteractionReplyOptions)(components)), { ephemeral: true }); | ||
if (interaction.replied) { | ||
await interaction.followUp(options); | ||
return { | ||
async update() { | ||
console.warn("Ephemeral followup replies can't be updated - blame discord 🙃"); | ||
}, | ||
}; | ||
} | ||
await interaction.reply(options); | ||
async function rerender() { | ||
components = render(); | ||
if (!components) { | ||
replyManager.remove(instance); | ||
return; | ||
} | ||
return interaction.editReply((0, reply_component_js_1.createInteractionReplyOptions)(components)); | ||
} | ||
const instance = replyManager.add({ | ||
async handleMessageComponentInteraction(interaction) { | ||
const matchingComponents = components === null || components === void 0 ? void 0 : components.filter(isActionRow).flatMap((c) => c.children).filter((c) => c.customId === interaction.customId); | ||
if (!(matchingComponents === null || matchingComponents === void 0 ? void 0 : matchingComponents.length)) | ||
return; | ||
for (const component of matchingComponents !== null && matchingComponents !== void 0 ? matchingComponents : []) { | ||
if (component.type === "button" && interaction.isButton()) { | ||
await component.onClick(); | ||
} | ||
if (component.type === "selectMenu" && interaction.isSelectMenu()) { | ||
await component.onSelect(interaction.values); | ||
} | ||
} | ||
await rerender(); | ||
}, | ||
}; | ||
}, | ||
async defer() { | ||
await interaction.deferReply({ | ||
fetchReply: true, | ||
}); | ||
return { | ||
async edit(...components) { | ||
const processResult = (0, reply_component_js_1.processReplyComponents)(components); | ||
await interaction.editReply(processResult.replyOptions); | ||
await handleMessageComponentInteraction(processResult.messageComponentIds, interactionQueue); | ||
async update() { | ||
await rerender(); | ||
}, | ||
async delete() { | ||
await interaction.deleteReply(); | ||
}, | ||
}; | ||
@@ -56,28 +112,6 @@ }, | ||
exports.createCommandHandlerContext = createCommandHandlerContext; | ||
async function handleMessageComponentInteraction(messageComponentIds, interactionQueue) { | ||
if (messageComponentIds.size > 0) { | ||
const messageInteraction = await interactionQueue.pop(); | ||
const interactedComponents = [...messageComponentIds] | ||
.filter(([, id]) => id === messageInteraction.customId) | ||
.map(([component]) => component); | ||
for (const component of interactedComponents) { | ||
if (component.type === "button") { | ||
component.onClick(); | ||
} | ||
if (component.type === "selectMenu" && | ||
messageInteraction.isSelectMenu()) { | ||
component.onSelect(messageInteraction.values); | ||
} | ||
} | ||
} | ||
function addReply(interaction, options) { | ||
if (interaction.replied) | ||
return interaction.followUp(options); | ||
return interaction.reply(Object.assign(Object.assign({}, options), { fetchReply: true })); | ||
} | ||
function addOrCreateReply(interaction, reply) { | ||
const messagePromise = (() => { | ||
if (interaction.deferred) | ||
return interaction.editReply(reply); | ||
if (interaction.replied) | ||
return interaction.followUp(reply); | ||
return interaction.reply(Object.assign(Object.assign({}, reply), { fetchReply: true })); | ||
})(); | ||
return messagePromise; | ||
} |
import type { Client } from "discord.js"; | ||
import { CommandHandler } from "./command-handler"; | ||
import { CommandHandler } from "./command-handler.js"; | ||
export declare function applyCommands(client: Client, commands: CommandHandler[]): void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.applyCommands = void 0; | ||
const async_queue_js_1 = require("../internal/async-queue.js"); | ||
const client_events_js_1 = require("../internal/client-events.js"); | ||
const command_handler_1 = require("./command-handler"); | ||
async function syncCommands(bot, commands, guildId) { | ||
var _a, _b, _c, _d; | ||
const command_handler_js_1 = require("./command-handler.js"); | ||
const reply_instance_js_1 = require("./reply-instance.js"); | ||
async function syncCommands(commands, guild) { | ||
var _a; | ||
for (const command of commands) { | ||
console.info(`Adding command: ${command.name}`); | ||
await ((_a = bot.application) === null || _a === void 0 ? void 0 : _a.commands.create(command, guildId)); | ||
await guild.commands.create(command); | ||
} | ||
const commandNames = new Set(commands.map((c) => c.name)); | ||
for (const appCommand of (_c = (_b = bot.application) === null || _b === void 0 ? void 0 : _b.commands.cache.values()) !== null && _c !== void 0 ? _c : []) { | ||
for (const appCommand of (_a = guild.commands.cache.values()) !== null && _a !== void 0 ? _a : []) { | ||
if (!commandNames.has(appCommand.name)) { | ||
console.info(`Removing command: ${appCommand.name}`); | ||
await ((_d = bot.application) === null || _d === void 0 ? void 0 : _d.commands.delete(appCommand.id)); | ||
await guild.commands.delete(appCommand.id); | ||
} | ||
@@ -22,3 +22,3 @@ } | ||
function applyCommands(client, commands) { | ||
const interactionQueue = new async_queue_js_1.AsyncQueue(); | ||
const replyManager = new reply_instance_js_1.ReplyManager(); | ||
(0, client_events_js_1.bindClientEvents)(client, { | ||
@@ -28,7 +28,7 @@ async ready() { | ||
for (const [, guild] of client.guilds.cache) { | ||
await syncCommands(client, commands, guild.id); | ||
await syncCommands(commands, guild); | ||
} | ||
}, | ||
async guildCreate(guild) { | ||
await syncCommands(client, commands, guild.id); | ||
await syncCommands(commands, guild); | ||
}, | ||
@@ -43,8 +43,7 @@ async interactionCreate(interaction) { | ||
return; | ||
const context = (0, command_handler_1.createCommandHandlerContext)(interaction, interaction.member, interactionQueue); | ||
const context = (0, command_handler_js_1.createCommandHandlerContext)(interaction, interaction.member, replyManager); | ||
await handler.run(context); | ||
} | ||
if (interaction.isMessageComponent()) { | ||
interactionQueue.add(interaction); | ||
await interaction.deferUpdate(); | ||
await replyManager.handleMessageComponentInteraction(interaction); | ||
} | ||
@@ -51,0 +50,0 @@ }, |
@@ -12,13 +12,14 @@ import type { EmojiResolvable, InteractionReplyOptions, MessageButtonStyle, MessageEmbed, MessageEmbedOptions, MessageSelectOptionData } from "discord.js"; | ||
}>; | ||
export declare type ReplyComponentArgs = (string | ReplyComponent)[]; | ||
export declare type ActionRowChild = SelectMenuComponent | ButtonComponent; | ||
export declare type ButtonComponent = { | ||
type: "button"; | ||
customId: string; | ||
style: MessageButtonStyle; | ||
label: string; | ||
emoji?: EmojiResolvable; | ||
onClick: () => void; | ||
onClick: () => void | Promise<unknown>; | ||
}; | ||
export declare type SelectMenuComponent = { | ||
type: "selectMenu"; | ||
customId: string; | ||
options: MessageSelectOptionData[]; | ||
@@ -29,11 +30,8 @@ selected?: string | string[] | undefined; | ||
maxValues?: number | undefined; | ||
onSelect: (values: string[]) => void; | ||
onSelect: (values: string[]) => void | Promise<unknown>; | ||
}; | ||
export declare function embedComponent(embed: MessageEmbedOptions | MessageEmbed): ReplyComponent; | ||
export declare function actionRowComponent(...children: (ActionRowChild | ActionRowChild[])[]): ReplyComponent; | ||
export declare function buttonComponent(options: Omit<ButtonComponent, "type">): ButtonComponent; | ||
export declare function selectMenuComponent({ options, ...args }: Omit<SelectMenuComponent, "type">): SelectMenuComponent; | ||
export declare function processReplyComponents(components: ReplyComponent[]): { | ||
replyOptions: InteractionReplyOptions; | ||
messageComponentIds: Map<ActionRowChild, string>; | ||
}; | ||
export declare function buttonComponent(options: Omit<ButtonComponent, "type" | "customId">): ButtonComponent; | ||
export declare function selectMenuComponent({ options, ...args }: Omit<SelectMenuComponent, "type" | "customId">): SelectMenuComponent; | ||
export declare function createInteractionReplyOptions(components: ReplyComponent[]): InteractionReplyOptions; |
@@ -14,3 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.processReplyComponents = exports.selectMenuComponent = exports.buttonComponent = exports.actionRowComponent = exports.embedComponent = void 0; | ||
exports.createInteractionReplyOptions = exports.selectMenuComponent = exports.buttonComponent = exports.actionRowComponent = exports.embedComponent = void 0; | ||
const node_crypto_1 = require("node:crypto"); | ||
@@ -30,3 +30,3 @@ const helpers_js_1 = require("../internal/helpers.js"); | ||
function buttonComponent(options) { | ||
return Object.assign({ type: "button" }, options); | ||
return Object.assign(Object.assign({}, options), { type: "button", customId: (0, node_crypto_1.randomUUID)() }); | ||
} | ||
@@ -37,3 +37,3 @@ exports.buttonComponent = buttonComponent; | ||
const selectedOptions = [args.selected].flat().filter(helpers_js_1.isString); | ||
return Object.assign(Object.assign({}, args), { type: "selectMenu", options: options.map((option) => { | ||
return Object.assign(Object.assign({}, args), { type: "selectMenu", customId: (0, node_crypto_1.randomUUID)(), options: options.map((option) => { | ||
var _a; | ||
@@ -44,4 +44,3 @@ return (Object.assign(Object.assign({}, option), { default: (_a = option.default) !== null && _a !== void 0 ? _a : selectedOptions.includes(option.value) })); | ||
exports.selectMenuComponent = selectMenuComponent; | ||
function processReplyComponents(components) { | ||
const messageComponentIds = new Map(); | ||
function createInteractionReplyOptions(components) { | ||
const content = components.filter(helpers_js_1.isString).join("\n"); | ||
@@ -60,9 +59,7 @@ const embeds = components | ||
components: component.children.map((child) => { | ||
const customId = (0, node_crypto_1.randomUUID)(); | ||
messageComponentIds.set(child, customId); | ||
if (child.type === "selectMenu") { | ||
return Object.assign(Object.assign({}, child), { type: "SELECT_MENU", customId }); | ||
return Object.assign(Object.assign({}, child), { type: "SELECT_MENU" }); | ||
} | ||
else { | ||
return Object.assign(Object.assign({}, child), { type: "BUTTON", customId }); | ||
return Object.assign(Object.assign({}, child), { type: "BUTTON" }); | ||
} | ||
@@ -73,3 +70,3 @@ }), | ||
.filter(helpers_js_1.isTruthy); | ||
const options = { | ||
return { | ||
// workaround: can't send components by themselves | ||
@@ -80,4 +77,3 @@ content: content || "_ _", | ||
}; | ||
return { replyOptions: options, messageComponentIds }; | ||
} | ||
exports.processReplyComponents = processReplyComponents; | ||
exports.createInteractionReplyOptions = createInteractionReplyOptions; |
export declare class AsyncQueue<T> { | ||
#private; | ||
items: T[]; | ||
resolvers: Array<(value: T) => void>; | ||
add(item: T): void; | ||
pop(): Promise<T>; | ||
} |
"use strict"; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
if (kind === "m") throw new TypeError("Private method is not writable"); | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); | ||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
}; | ||
var _AsyncQueue_items, _AsyncQueue_resolvers; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -18,16 +6,16 @@ exports.AsyncQueue = void 0; | ||
constructor() { | ||
_AsyncQueue_items.set(this, []); | ||
_AsyncQueue_resolvers.set(this, []); | ||
this.items = []; | ||
this.resolvers = []; | ||
} | ||
add(item) { | ||
if (__classPrivateFieldGet(this, _AsyncQueue_resolvers, "f").length >= 0) { | ||
__classPrivateFieldGet(this, _AsyncQueue_resolvers, "f").forEach((resolve) => resolve(item)); | ||
__classPrivateFieldSet(this, _AsyncQueue_resolvers, [], "f"); | ||
if (this.resolvers.length >= 0) { | ||
this.resolvers.forEach((resolve) => resolve(item)); | ||
this.resolvers = []; | ||
} | ||
else { | ||
__classPrivateFieldGet(this, _AsyncQueue_items, "f").push(item); | ||
this.items.push(item); | ||
} | ||
} | ||
pop() { | ||
const item = __classPrivateFieldGet(this, _AsyncQueue_items, "f").shift(); | ||
const item = this.items.shift(); | ||
if (item) { | ||
@@ -37,3 +25,3 @@ return Promise.resolve(item); | ||
else { | ||
return new Promise((resolve) => __classPrivateFieldGet(this, _AsyncQueue_resolvers, "f").push(resolve)); | ||
return new Promise((resolve) => this.resolvers.push(resolve)); | ||
} | ||
@@ -43,2 +31,1 @@ } | ||
exports.AsyncQueue = AsyncQueue; | ||
_AsyncQueue_items = new WeakMap(), _AsyncQueue_resolvers = new WeakMap(); |
@@ -5,3 +5,3 @@ { | ||
"authors": "MapleLeaf", | ||
"version": "0.0.0-alpha.2", | ||
"version": "0.0.0-alpha.3", | ||
"main": "dist/main.js", | ||
@@ -12,3 +12,3 @@ "types": "dist/main.d.ts", | ||
], | ||
"repository": "itsMapleLeaf/gatekeeper", | ||
"repository": "https://github.com/itsMapleLeaf/gatekeeper", | ||
"keywords": [ | ||
@@ -15,0 +15,0 @@ "discord", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
26718
27
582