djs-next 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of djs-next might be problematic. Click here for more details.
- package/README.md +250 -0
- package/assets/djs-next.png +0 -0
- package/bin/create-djs-next.js +78 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +1283 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1259 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +45 -0
- package/src/client.ts +492 -0
- package/src/handlers/commandHandler.ts +139 -0
- package/src/handlers/componentHandler.ts +31 -0
- package/src/handlers/eventHandler.ts +37 -0
- package/src/handlers/taskHandler.ts +38 -0
- package/src/handlers/utils.ts +25 -0
- package/src/index.ts +10 -0
- package/src/plugins/dnxt.ts +418 -0
- package/src/templates/cjs.ts +54 -0
- package/src/templates/esm.ts +50 -0
- package/src/templates/ts.ts +53 -0
- package/src/test/client.test.ts +8 -0
- package/src/types.ts +94 -0
- package/src/utils/PaginationBuilder.ts +94 -0
- package/src/utils/configLoader.ts +27 -0
- package/src/utils/i18n.ts +57 -0
- package/src/utils/paginate.ts +90 -0
- package/src/utils/prompts.ts +76 -0
- package/test_payload.js +3 -0
- package/test_reply.js +4 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const tsTemplates = {
|
|
2
|
+
index: `import { DJSNextClient, GatewayIntentBits } from 'djs-next';
|
|
3
|
+
import 'dotenv/config';
|
|
4
|
+
|
|
5
|
+
const client = new DJSNextClient({
|
|
6
|
+
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
|
|
7
|
+
commandsDir: './src/commands',
|
|
8
|
+
eventsDir: './src/events',
|
|
9
|
+
componentsDir: './src/components',
|
|
10
|
+
tasksDir: './src/tasks',
|
|
11
|
+
clientId: process.env.CLIENT_ID
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
client.start(process.env.DISCORD_TOKEN!);
|
|
15
|
+
`,
|
|
16
|
+
|
|
17
|
+
ping: `import { FileCommand } from 'djs-next';
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
description: 'Replies with the actual bot latency!',
|
|
21
|
+
execute: async (interaction, client) => {
|
|
22
|
+
const sent = await interaction.reply({ content: 'Pinging...', withResponse: true });
|
|
23
|
+
const msg = sent.resource?.message || await interaction.fetchReply();
|
|
24
|
+
await interaction.editReply(\`Pong! 🏓\\nWebsocket Latency: \\\`\${client.ws.ping}ms\\\`\\nAPI Latency: \\\`\${msg.createdTimestamp - interaction.createdTimestamp}ms\\\`\`);
|
|
25
|
+
},
|
|
26
|
+
executeText: async (message, args, client) => {
|
|
27
|
+
const sent = await message.reply('Pinging...');
|
|
28
|
+
await sent.edit(\`Pong! 🏓\\nWebsocket Latency: \\\`\${client.ws.ping}ms\\\`\\nAPI Latency: \\\`\${sent.createdTimestamp - message.createdTimestamp}ms\\\`\`);
|
|
29
|
+
}
|
|
30
|
+
} as FileCommand;
|
|
31
|
+
`,
|
|
32
|
+
|
|
33
|
+
ready: `import { DJSNextEvent, Events } from 'djs-next';
|
|
34
|
+
|
|
35
|
+
export const event: DJSNextEvent<Events.ClientReady> = {
|
|
36
|
+
name: Events.ClientReady,
|
|
37
|
+
once: true,
|
|
38
|
+
execute: (client) => {
|
|
39
|
+
console.log('Ready! Logged in as ' + client.user?.tag);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
`,
|
|
43
|
+
|
|
44
|
+
healthcheck: `import { FileTask } from 'djs-next';
|
|
45
|
+
|
|
46
|
+
export const task: FileTask = {
|
|
47
|
+
interval: 60000,
|
|
48
|
+
execute: async (client) => {
|
|
49
|
+
console.log('[Task] Healthcheck completed.');
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
`
|
|
53
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientOptions,
|
|
3
|
+
ClientEvents,
|
|
4
|
+
ChatInputCommandInteraction,
|
|
5
|
+
Client,
|
|
6
|
+
ApplicationCommandOptionData,
|
|
7
|
+
PermissionResolvable,
|
|
8
|
+
AutocompleteInteraction,
|
|
9
|
+
MessageComponentInteraction,
|
|
10
|
+
ModalSubmitInteraction,
|
|
11
|
+
Interaction,
|
|
12
|
+
Message
|
|
13
|
+
} from 'discord.js';
|
|
14
|
+
|
|
15
|
+
export interface CooldownAdapter {
|
|
16
|
+
get(commandId: string, userId: string): Promise<number | null> | number | null;
|
|
17
|
+
set(commandId: string, userId: string, expirationTime: number): Promise<void> | void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DJSNextConfig {
|
|
21
|
+
devGuildId?: string;
|
|
22
|
+
errorLogChannelId?: string;
|
|
23
|
+
responses?: {
|
|
24
|
+
developerOnly?: string | null;
|
|
25
|
+
guildOnly?: string | null;
|
|
26
|
+
cooldown?: string | null;
|
|
27
|
+
missingPerms?: string | null;
|
|
28
|
+
errorBoundary?: string | null;
|
|
29
|
+
};
|
|
30
|
+
locales?: string[];
|
|
31
|
+
defaultLocale?: string;
|
|
32
|
+
directories?: {
|
|
33
|
+
commands?: string;
|
|
34
|
+
events?: string;
|
|
35
|
+
components?: string;
|
|
36
|
+
tasks?: string;
|
|
37
|
+
locales?: string;
|
|
38
|
+
};
|
|
39
|
+
cooldownAdapter?: CooldownAdapter;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface DJSNextClientOptions extends ClientOptions {
|
|
43
|
+
commandsDir?: string;
|
|
44
|
+
eventsDir?: string;
|
|
45
|
+
componentsDir?: string;
|
|
46
|
+
tasksDir?: string;
|
|
47
|
+
clientId?: string;
|
|
48
|
+
guildId?: string;
|
|
49
|
+
developers?: string[];
|
|
50
|
+
prefixes?: string[] | string;
|
|
51
|
+
enableSlashCommands?: boolean;
|
|
52
|
+
enableTextCommands?: boolean;
|
|
53
|
+
enableMentionPrefix?: boolean | string[];
|
|
54
|
+
enableNoPrefix?: boolean | string[];
|
|
55
|
+
middleware?: (interaction: Interaction | Message, client: Client) => Promise<boolean> | boolean;
|
|
56
|
+
config?: DJSNextConfig;
|
|
57
|
+
db?: any;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface FileTask<DB = any> {
|
|
61
|
+
filepath?: string;
|
|
62
|
+
interval: number;
|
|
63
|
+
execute: (client: Client & { db: DB; t: Function; config: DJSNextConfig }) => Promise<void> | void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface FileCommand<DB = any> {
|
|
67
|
+
filepath?: string;
|
|
68
|
+
description: string;
|
|
69
|
+
options?: ApplicationCommandOptionData[];
|
|
70
|
+
cooldown?: number;
|
|
71
|
+
userPermissions?: PermissionResolvable[];
|
|
72
|
+
botPermissions?: PermissionResolvable[];
|
|
73
|
+
developerOnly?: boolean;
|
|
74
|
+
guildOnly?: boolean;
|
|
75
|
+
aliases?: string[];
|
|
76
|
+
preconditions?: string[];
|
|
77
|
+
execute?: (interaction: ChatInputCommandInteraction, client: Client & { db: DB; t: Function; config: DJSNextConfig }) => Promise<void> | void;
|
|
78
|
+
executeText?: (message: Message, args: string[], client: Client & { db: DB; t: Function; config: DJSNextConfig }) => Promise<void> | void;
|
|
79
|
+
autocomplete?: (interaction: AutocompleteInteraction, client: Client & { db: DB; t: Function; config: DJSNextConfig }) => Promise<void> | void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface FileComponent<DB = any> {
|
|
83
|
+
filepath?: string;
|
|
84
|
+
customId?: string;
|
|
85
|
+
preconditions?: string[];
|
|
86
|
+
execute: (interaction: MessageComponentInteraction | ModalSubmitInteraction, client: Client & { db: DB; t: Function; config: DJSNextConfig }, params?: Record<string, string>) => Promise<void> | void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface Event<K extends keyof ClientEvents = keyof ClientEvents, DB = any> {
|
|
90
|
+
filepath?: string;
|
|
91
|
+
name: K;
|
|
92
|
+
once?: boolean;
|
|
93
|
+
execute: (client: Client & { db: DB; t: Function; config: DJSNextConfig }, ...args: ClientEvents[K]) => Promise<void> | void;
|
|
94
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionRowBuilder,
|
|
3
|
+
ButtonBuilder,
|
|
4
|
+
ButtonStyle,
|
|
5
|
+
CommandInteraction,
|
|
6
|
+
EmbedBuilder,
|
|
7
|
+
Message,
|
|
8
|
+
ComponentType
|
|
9
|
+
} from 'discord.js';
|
|
10
|
+
|
|
11
|
+
export class PaginationBuilder {
|
|
12
|
+
private pages: EmbedBuilder[] = [];
|
|
13
|
+
private timeout: number = 60000;
|
|
14
|
+
|
|
15
|
+
constructor(pages?: EmbedBuilder[]) {
|
|
16
|
+
if (pages) this.pages = pages;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public addPage(embed: EmbedBuilder): this {
|
|
20
|
+
this.pages.push(embed);
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public setPages(pages: EmbedBuilder[]): this {
|
|
25
|
+
this.pages = pages;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public setTimeout(ms: number): this {
|
|
30
|
+
this.timeout = ms;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async build(target: CommandInteraction | Message): Promise<Message | null> {
|
|
35
|
+
if (this.pages.length === 0) throw new Error('[djs-next] PaginationBuilder requires at least one page.');
|
|
36
|
+
|
|
37
|
+
if (this.pages.length === 1) {
|
|
38
|
+
if (target instanceof CommandInteraction) {
|
|
39
|
+
if (target.deferred || target.replied) {
|
|
40
|
+
return await target.editReply({ embeds: [this.pages[0]] }) as Message;
|
|
41
|
+
}
|
|
42
|
+
return await target.reply({ embeds: [this.pages[0]], fetchReply: true }) as Message;
|
|
43
|
+
} else {
|
|
44
|
+
return await target.reply({ embeds: [this.pages[0]] });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let currentPage = 0;
|
|
49
|
+
|
|
50
|
+
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
|
51
|
+
new ButtonBuilder().setCustomId('prev').setLabel('◀').setStyle(ButtonStyle.Primary).setDisabled(true),
|
|
52
|
+
new ButtonBuilder().setCustomId('page').setLabel(`1 / ${this.pages.length}`).setStyle(ButtonStyle.Secondary).setDisabled(true),
|
|
53
|
+
new ButtonBuilder().setCustomId('next').setLabel('▶').setStyle(ButtonStyle.Primary)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
let replyMsg: Message;
|
|
57
|
+
if (target instanceof CommandInteraction) {
|
|
58
|
+
if (target.deferred || target.replied) {
|
|
59
|
+
replyMsg = await target.editReply({ embeds: [this.pages[0]], components: [row] }) as Message;
|
|
60
|
+
} else {
|
|
61
|
+
replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row], fetchReply: true }) as Message;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row] });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const userId = target instanceof CommandInteraction ? target.user.id : target.author.id;
|
|
68
|
+
|
|
69
|
+
const collector = replyMsg.createMessageComponentCollector({
|
|
70
|
+
componentType: ComponentType.Button,
|
|
71
|
+
time: this.timeout,
|
|
72
|
+
filter: i => i.user.id === userId
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
collector.on('collect', async i => {
|
|
76
|
+
if (i.customId === 'prev') currentPage--;
|
|
77
|
+
else if (i.customId === 'next') currentPage++;
|
|
78
|
+
|
|
79
|
+
const newRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
|
80
|
+
new ButtonBuilder().setCustomId('prev').setLabel('◀').setStyle(ButtonStyle.Primary).setDisabled(currentPage === 0),
|
|
81
|
+
new ButtonBuilder().setCustomId('page').setLabel(`${currentPage + 1} / ${this.pages.length}`).setStyle(ButtonStyle.Secondary).setDisabled(true),
|
|
82
|
+
new ButtonBuilder().setCustomId('next').setLabel('▶').setStyle(ButtonStyle.Primary).setDisabled(currentPage === this.pages.length - 1)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await i.update({ embeds: [this.pages[currentPage]], components: [newRow] });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
collector.on('end', () => {
|
|
89
|
+
replyMsg.edit({ components: [] }).catch(() => null);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return replyMsg;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { DJSNextConfig } from '../types.js';
|
|
5
|
+
|
|
6
|
+
export async function loadConfig(): Promise<DJSNextConfig> {
|
|
7
|
+
const exts = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'];
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
|
|
10
|
+
for (const ext of exts) {
|
|
11
|
+
const configPath = path.join(cwd, `djs-next.config${ext}`);
|
|
12
|
+
if (fs.existsSync(configPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const configModule = await import(pathToFileURL(configPath).href);
|
|
15
|
+
return configModule.default || configModule;
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(`[djs-next] Error loading config file ${configPath}:`, err);
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function defineConfig(config: DJSNextConfig): DJSNextConfig {
|
|
26
|
+
return config;
|
|
27
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
let localesCache: Record<string, Record<string, any>> = {};
|
|
5
|
+
let defaultLoc = 'en';
|
|
6
|
+
|
|
7
|
+
export function loadLocales(localesDir: string, defaultLocale?: string) {
|
|
8
|
+
if (defaultLocale) defaultLoc = defaultLocale;
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(localesDir)) return;
|
|
11
|
+
|
|
12
|
+
const files = fs.readdirSync(localesDir);
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
if (file.endsWith('.json')) {
|
|
15
|
+
const lang = file.replace('.json', '');
|
|
16
|
+
const content = fs.readFileSync(path.join(localesDir, file), 'utf8');
|
|
17
|
+
try {
|
|
18
|
+
localesCache[lang] = JSON.parse(content);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.error(`[djs-next] Failed to parse locale file: ${file}`, e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function translate(key: string, locale: string = defaultLoc, variables?: Record<string, string | number>): string {
|
|
27
|
+
// Fallback to default locale if the requested one is not found
|
|
28
|
+
const dict = localesCache[locale] || localesCache[defaultLoc] || {};
|
|
29
|
+
|
|
30
|
+
const keys = key.split('.');
|
|
31
|
+
let value: any = dict;
|
|
32
|
+
|
|
33
|
+
for (const k of keys) {
|
|
34
|
+
if (value && typeof value === 'object') {
|
|
35
|
+
value = value[k];
|
|
36
|
+
} else {
|
|
37
|
+
value = undefined;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (typeof value !== 'string') {
|
|
43
|
+
return key; // return the key itself if string not found
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (variables) {
|
|
47
|
+
for (const [varKey, varValue] of Object.entries(variables)) {
|
|
48
|
+
value = value.replace(new RegExp(`{{s*${varKey}s*}}`, 'g'), String(varValue));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getLocalesCache() {
|
|
56
|
+
return localesCache;
|
|
57
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionRowBuilder,
|
|
3
|
+
ButtonBuilder,
|
|
4
|
+
ButtonStyle,
|
|
5
|
+
CommandInteraction,
|
|
6
|
+
EmbedBuilder,
|
|
7
|
+
Message,
|
|
8
|
+
MessageComponentInteraction
|
|
9
|
+
} from 'discord.js';
|
|
10
|
+
|
|
11
|
+
export async function paginate(
|
|
12
|
+
context: Message | CommandInteraction | MessageComponentInteraction,
|
|
13
|
+
pages: EmbedBuilder[],
|
|
14
|
+
time: number = 60000
|
|
15
|
+
) {
|
|
16
|
+
const isMessage = 'author' in context;
|
|
17
|
+
|
|
18
|
+
if (!isMessage) {
|
|
19
|
+
if (!context.deferred && !context.replied) {
|
|
20
|
+
await context.deferReply();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (pages.length === 1) {
|
|
25
|
+
if (isMessage) {
|
|
26
|
+
return context.reply({ embeds: [pages[0]], components: [] });
|
|
27
|
+
} else {
|
|
28
|
+
return context.editReply({ embeds: [pages[0]], components: [] });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let index = 0;
|
|
33
|
+
|
|
34
|
+
const prevButton = new ButtonBuilder()
|
|
35
|
+
.setCustomId('djs_prev')
|
|
36
|
+
.setLabel('Previous')
|
|
37
|
+
.setStyle(ButtonStyle.Primary)
|
|
38
|
+
.setDisabled(true);
|
|
39
|
+
|
|
40
|
+
const nextButton = new ButtonBuilder()
|
|
41
|
+
.setCustomId('djs_next')
|
|
42
|
+
.setLabel('Next')
|
|
43
|
+
.setStyle(ButtonStyle.Primary);
|
|
44
|
+
|
|
45
|
+
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(prevButton, nextButton);
|
|
46
|
+
|
|
47
|
+
let message: Message;
|
|
48
|
+
if (isMessage) {
|
|
49
|
+
message = await context.reply({
|
|
50
|
+
embeds: [pages[index]],
|
|
51
|
+
components: [row],
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
message = await context.editReply({
|
|
55
|
+
embeds: [pages[index]],
|
|
56
|
+
components: [row],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const collector = message.createMessageComponentCollector({
|
|
61
|
+
filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
|
|
62
|
+
time
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
collector.on('collect', async (i: MessageComponentInteraction) => {
|
|
66
|
+
if (i.customId === 'djs_prev') {
|
|
67
|
+
index = index > 0 ? index - 1 : index;
|
|
68
|
+
} else if (i.customId === 'djs_next') {
|
|
69
|
+
index = index < pages.length - 1 ? index + 1 : index;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
prevButton.setDisabled(index === 0);
|
|
73
|
+
nextButton.setDisabled(index === pages.length - 1);
|
|
74
|
+
|
|
75
|
+
await i.update({
|
|
76
|
+
embeds: [pages[index]],
|
|
77
|
+
components: [new ActionRowBuilder<ButtonBuilder>().addComponents(prevButton, nextButton)]
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
collector.on('end', async () => {
|
|
82
|
+
prevButton.setDisabled(true);
|
|
83
|
+
nextButton.setDisabled(true);
|
|
84
|
+
if (message.editable) {
|
|
85
|
+
await message.edit({
|
|
86
|
+
components: [new ActionRowBuilder<ButtonBuilder>().addComponents(prevButton, nextButton)]
|
|
87
|
+
}).catch(() => {});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionRowBuilder,
|
|
3
|
+
ButtonBuilder,
|
|
4
|
+
ButtonStyle,
|
|
5
|
+
CommandInteraction,
|
|
6
|
+
EmbedBuilder,
|
|
7
|
+
Message,
|
|
8
|
+
MessageComponentInteraction
|
|
9
|
+
} from 'discord.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sends a confirmation prompt (Yes/No) to the user.
|
|
13
|
+
* @param context The message or interaction context.
|
|
14
|
+
* @param content The text or embed to display in the prompt.
|
|
15
|
+
* @param time Time to wait for a response in milliseconds.
|
|
16
|
+
* @returns Boolean indicating true for Yes, false for No, or null if timed out.
|
|
17
|
+
*/
|
|
18
|
+
export async function confirmPrompt(
|
|
19
|
+
context: Message | CommandInteraction | MessageComponentInteraction,
|
|
20
|
+
content: string | EmbedBuilder,
|
|
21
|
+
time: number = 30000
|
|
22
|
+
): Promise<boolean | null> {
|
|
23
|
+
const isMessage = 'author' in context;
|
|
24
|
+
|
|
25
|
+
if (!isMessage) {
|
|
26
|
+
if (!context.deferred && !context.replied) {
|
|
27
|
+
await context.deferReply();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const yesButton = new ButtonBuilder()
|
|
32
|
+
.setCustomId('djs_prompt_yes')
|
|
33
|
+
.setLabel('Yes')
|
|
34
|
+
.setStyle(ButtonStyle.Success);
|
|
35
|
+
|
|
36
|
+
const noButton = new ButtonBuilder()
|
|
37
|
+
.setCustomId('djs_prompt_no')
|
|
38
|
+
.setLabel('No')
|
|
39
|
+
.setStyle(ButtonStyle.Danger);
|
|
40
|
+
|
|
41
|
+
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(yesButton, noButton);
|
|
42
|
+
|
|
43
|
+
const payload: any = { components: [row] };
|
|
44
|
+
if (typeof content === 'string') {
|
|
45
|
+
payload.content = content;
|
|
46
|
+
} else {
|
|
47
|
+
payload.embeds = [content];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let message: Message;
|
|
51
|
+
if (isMessage) {
|
|
52
|
+
message = await context.reply(payload);
|
|
53
|
+
} else {
|
|
54
|
+
message = await context.editReply(payload);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const interaction = await message.awaitMessageComponent({
|
|
59
|
+
filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
|
|
60
|
+
time
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (interaction.customId === 'djs_prompt_yes') {
|
|
64
|
+
await interaction.update({ components: [] });
|
|
65
|
+
return true;
|
|
66
|
+
} else {
|
|
67
|
+
await interaction.update({ components: [] });
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (message.editable) {
|
|
72
|
+
await message.edit({ components: [] }).catch(() => {});
|
|
73
|
+
}
|
|
74
|
+
return null; // Timed out
|
|
75
|
+
}
|
|
76
|
+
}
|
package/test_payload.js
ADDED
package/test_reply.js
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "./dist",
|
|
12
|
+
"types": ["node"],
|
|
13
|
+
"ignoreDeprecations": "6.0"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"]
|
|
16
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
format: ['cjs', 'esm'], // Build for CommonJS and ES Modules
|
|
6
|
+
dts: true, // Generate declaration file (.d.ts)
|
|
7
|
+
splitting: false,
|
|
8
|
+
sourcemap: true,
|
|
9
|
+
clean: true,
|
|
10
|
+
});
|