mirror of
https://github.com/xHyroM/roles-bot.git
synced 2024-11-22 00:11:06 +01:00
feat(bot): add modals
This commit is contained in:
parent
5cb18619ad
commit
ae5e39d08d
11 changed files with 216 additions and 33 deletions
|
@ -25,6 +25,7 @@ Promise.all([
|
||||||
charset: "utf8",
|
charset: "utf8",
|
||||||
minify: !dev,
|
minify: !dev,
|
||||||
watch: watch,
|
watch: watch,
|
||||||
|
external: ["node:events"],
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
import { Command } from "../structs/Command";
|
import { Command } from "../structs/Command";
|
||||||
import { ChannelType } from "discord-api-types/v10";
|
import { MessageFlags, TextInputStyle } from "discord-api-types/v10";
|
||||||
import { ActionRowBuilder, ChannelSelectMenuBuilder } from "builders";
|
import { ActionRowBuilder, TextInputBuilder } from "builders";
|
||||||
|
|
||||||
new Command({
|
new Command({
|
||||||
name: "setup",
|
name: "setup",
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
acknowledge: false,
|
||||||
run: async (ctx) => {
|
run: async (ctx) => {
|
||||||
await ctx.editReply({
|
return ctx.returnModal({
|
||||||
content: "Setup",
|
title: "Setup",
|
||||||
|
custom_id: "test",
|
||||||
|
components: [
|
||||||
|
new ActionRowBuilder<TextInputBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new TextInputBuilder()
|
||||||
|
.setLabel("ASDSAD")
|
||||||
|
.setCustomId("prefix")
|
||||||
|
.setPlaceholder("Prefix")
|
||||||
|
.setStyle(TextInputStyle.Paragraph)
|
||||||
|
.setRequired(true),
|
||||||
|
)
|
||||||
|
.toJSON(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* await ctx.editReply({
|
||||||
|
content: "Select the channel to which the panel will be sent.",
|
||||||
components: [
|
components: [
|
||||||
new ActionRowBuilder<ChannelSelectMenuBuilder>()
|
new ActionRowBuilder<ChannelSelectMenuBuilder>()
|
||||||
.addComponents(
|
.addComponents(
|
||||||
new ChannelSelectMenuBuilder()
|
new ChannelSelectMenuBuilder()
|
||||||
.setCustomId("channel")
|
.setCustomId("setup:part-channel")
|
||||||
.addChannelTypes(
|
.addChannelTypes(
|
||||||
ChannelType.GuildAnnouncement,
|
ChannelType.GuildAnnouncement,
|
||||||
ChannelType.GuildText,
|
ChannelType.GuildText,
|
||||||
|
@ -20,5 +40,6 @@ new Command({
|
||||||
.toJSON(),
|
.toJSON(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
35
packages/bot/src/components/setup.ts
Normal file
35
packages/bot/src/components/setup.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { ActionRowBuilder, RoleSelectMenuBuilder } from "builders";
|
||||||
|
import { Component } from "../structs/Component";
|
||||||
|
import { MessageFlags } from "discord-api-types/v10";
|
||||||
|
|
||||||
|
new Component({
|
||||||
|
id: "setup:part-channel",
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
run: async (ctx) => {
|
||||||
|
await ctx.editReply({
|
||||||
|
content: "Select the roles that will be available in the menu.",
|
||||||
|
components: [
|
||||||
|
new ActionRowBuilder<RoleSelectMenuBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new RoleSelectMenuBuilder()
|
||||||
|
.setCustomId("setup:part-roles")
|
||||||
|
.setPlaceholder("Select roles")
|
||||||
|
.setMinValues(1)
|
||||||
|
.setMaxValues(25),
|
||||||
|
)
|
||||||
|
.toJSON(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new Component({
|
||||||
|
id: "setup:part-roles",
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
run: async (ctx) => {
|
||||||
|
await ctx.editReply({
|
||||||
|
content: "done",
|
||||||
|
components: [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,18 +1,21 @@
|
||||||
import "./commands/setup";
|
import "./commands/setup";
|
||||||
|
import "./components/setup";
|
||||||
|
import "./modals/setup";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
APIApplicationCommandInteraction,
|
APIApplicationCommandInteraction,
|
||||||
APIMessageComponentInteraction,
|
APIMessageComponentInteraction,
|
||||||
|
APIModalSubmitInteraction,
|
||||||
APIPingInteraction,
|
APIPingInteraction,
|
||||||
InteractionResponseType,
|
InteractionResponseType,
|
||||||
InteractionType,
|
InteractionType,
|
||||||
} from "discord-api-types/v10";
|
} from "discord-api-types/v10";
|
||||||
import { COMMANDS, COMPONENTS } from "./registers";
|
import { COMMANDS, COMPONENTS, MODALS } from "./registers";
|
||||||
import { verify } from "./utils/verify";
|
import { verify } from "./utils/verify";
|
||||||
import respond from "./utils/respond";
|
import respond from "./utils/respond";
|
||||||
import { CommandContext } from "./structs/contexts/CommandContext";
|
import { CommandContext } from "./structs/contexts/CommandContext";
|
||||||
import { ComponentContext } from "./structs/contexts/ComponentContext";
|
import { ComponentContext } from "./structs/contexts/ComponentContext";
|
||||||
|
import { ModalContext } from "./structs/contexts/ModalContext";
|
||||||
console.log(COMMANDS);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch: async (request: Request, env: Env) => {
|
fetch: async (request: Request, env: Env) => {
|
||||||
|
@ -27,6 +30,7 @@ export default {
|
||||||
const interaction = (await request.json()) as
|
const interaction = (await request.json()) as
|
||||||
| APIPingInteraction
|
| APIPingInteraction
|
||||||
| APIApplicationCommandInteraction
|
| APIApplicationCommandInteraction
|
||||||
|
| APIModalSubmitInteraction
|
||||||
| APIMessageComponentInteraction;
|
| APIMessageComponentInteraction;
|
||||||
|
|
||||||
if (interaction.type === InteractionType.Ping)
|
if (interaction.type === InteractionType.Ping)
|
||||||
|
@ -34,34 +38,66 @@ export default {
|
||||||
type: InteractionResponseType.Pong,
|
type: InteractionResponseType.Pong,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (interaction.type === InteractionType.ApplicationCommand) {
|
switch (interaction.type) {
|
||||||
const command = COMMANDS.find(
|
case InteractionType.ApplicationCommand: {
|
||||||
(cmd) => cmd.name === interaction.data.name,
|
const command = COMMANDS.find(
|
||||||
);
|
(cmd) => cmd.name === interaction.data.name,
|
||||||
|
);
|
||||||
|
|
||||||
if (!command) return new Response("Unknown command", { status: 404 });
|
if (!command) return new Response("Unknown command", { status: 404 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return respond({
|
if (command.acknowledge)
|
||||||
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
return respond({
|
||||||
});
|
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
||||||
} finally {
|
data: {
|
||||||
command.run(new CommandContext(interaction, env));
|
flags: command.flags,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (command.acknowledge)
|
||||||
|
command.run(new CommandContext(interaction, env));
|
||||||
|
// rome-ignore lint/correctness/noUnsafeFinally: it works, must do better typings etc...
|
||||||
|
else return command.run(new CommandContext(interaction, env));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
case InteractionType.ModalSubmit: {
|
||||||
|
const modal = MODALS.find((md) => md.id === interaction.data.custom_id);
|
||||||
|
|
||||||
const component = COMPONENTS.find(
|
if (!modal) return new Response("Unknown modal", { status: 404 });
|
||||||
(cmp) => cmp.id === interaction.data.custom_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!component) return new Response("Unknown component", { status: 404 });
|
try {
|
||||||
|
return respond({
|
||||||
|
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
||||||
|
data: {
|
||||||
|
flags: modal.flags,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
modal.run(new ModalContext(interaction, env));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case InteractionType.MessageComponent: {
|
||||||
|
const component = COMPONENTS.find(
|
||||||
|
(cmp) => cmp.id === interaction.data.custom_id,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
if (!component)
|
||||||
return respond({
|
return new Response("Unknown component", { status: 404 });
|
||||||
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
||||||
});
|
try {
|
||||||
} finally {
|
return respond({
|
||||||
component.run(new ComponentContext(interaction, env));
|
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
||||||
|
data: {
|
||||||
|
flags: component.flags,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
component.run(new ComponentContext(interaction, env));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
8
packages/bot/src/modals/setup.ts
Normal file
8
packages/bot/src/modals/setup.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { Modal } from "../structs/Modal";
|
||||||
|
|
||||||
|
new Modal({
|
||||||
|
id: "test",
|
||||||
|
run: async (ctx) => {
|
||||||
|
// TODO: finish
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,8 +1,10 @@
|
||||||
import { Command } from "./structs/Command";
|
import { Command } from "./structs/Command";
|
||||||
import { Component } from "./structs/Component";
|
import { Component } from "./structs/Component";
|
||||||
|
import { Modal } from "./structs/Modal";
|
||||||
|
|
||||||
export const COMMANDS: Command[] = [];
|
export const COMMANDS: Command[] = [];
|
||||||
export const COMPONENTS: Component[] = [];
|
export const COMPONENTS: Component[] = [];
|
||||||
|
export const MODALS: Modal[] = [];
|
||||||
|
|
||||||
export const registerCommand = (command: Command) => {
|
export const registerCommand = (command: Command) => {
|
||||||
COMMANDS.push(command);
|
COMMANDS.push(command);
|
||||||
|
@ -11,3 +13,7 @@ export const registerCommand = (command: Command) => {
|
||||||
export const registerComponent = (component: Component) => {
|
export const registerComponent = (component: Component) => {
|
||||||
COMPONENTS.push(component);
|
COMPONENTS.push(component);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const registerModal = (modal: Modal) => {
|
||||||
|
MODALS.push(modal);
|
||||||
|
};
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
|
import { MessageFlags } from "discord-api-types/v10";
|
||||||
import { registerCommand } from "../registers";
|
import { registerCommand } from "../registers";
|
||||||
import { CommandContext } from "./contexts/CommandContext";
|
import { CommandContext } from "./contexts/CommandContext";
|
||||||
|
|
||||||
interface CommandOptions {
|
interface CommandOptions {
|
||||||
name: string;
|
name: string;
|
||||||
|
acknowledge?: boolean;
|
||||||
|
flags?: MessageFlags;
|
||||||
run: (interaction: CommandContext) => void;
|
run: (interaction: CommandContext) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Command {
|
export class Command<A> {
|
||||||
public name: string;
|
public name: string;
|
||||||
public run: (interaction: CommandContext) => void;
|
public acknowledge: boolean;
|
||||||
|
public flags: MessageFlags | undefined;
|
||||||
|
public run: (interaction: CommandContext) => void | Response;
|
||||||
|
|
||||||
constructor(options: CommandOptions) {
|
constructor(options: CommandOptions) {
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
|
this.acknowledge = options.acknowledge ?? true;
|
||||||
|
this.flags = options.flags;
|
||||||
this.run = options.run;
|
this.run = options.run;
|
||||||
|
|
||||||
registerCommand(this);
|
registerCommand(this);
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
|
import { MessageFlags } from "discord-api-types/v10";
|
||||||
import { registerComponent } from "../registers";
|
import { registerComponent } from "../registers";
|
||||||
import { ComponentContext } from "./contexts/ComponentContext";
|
import { ComponentContext } from "./contexts/ComponentContext";
|
||||||
|
|
||||||
interface ComponentOptions {
|
interface ComponentOptions {
|
||||||
id: string;
|
id: string;
|
||||||
|
flags?: MessageFlags;
|
||||||
run: (interaction: ComponentContext) => void;
|
run: (interaction: ComponentContext) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Component {
|
export class Component {
|
||||||
public id: string;
|
public id: string;
|
||||||
|
public flags: MessageFlags | undefined;
|
||||||
public run: (interaction: ComponentContext) => void;
|
public run: (interaction: ComponentContext) => void;
|
||||||
|
|
||||||
constructor(options: ComponentOptions) {
|
constructor(options: ComponentOptions) {
|
||||||
this.id = options.id;
|
this.id = options.id;
|
||||||
|
this.flags = options.flags;
|
||||||
this.run = options.run;
|
this.run = options.run;
|
||||||
|
|
||||||
registerComponent(this);
|
registerComponent(this);
|
||||||
|
|
23
packages/bot/src/structs/Modal.ts
Normal file
23
packages/bot/src/structs/Modal.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { MessageFlags } from "discord-api-types/v10";
|
||||||
|
import { registerModal } from "../registers";
|
||||||
|
import { ModalContext } from "./contexts/ModalContext";
|
||||||
|
|
||||||
|
interface ModalOptions {
|
||||||
|
id: string;
|
||||||
|
flags?: MessageFlags;
|
||||||
|
run: (interaction: ModalContext) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Modal {
|
||||||
|
public id: string;
|
||||||
|
public flags: MessageFlags | undefined;
|
||||||
|
public run: (interaction: ModalContext) => void;
|
||||||
|
|
||||||
|
constructor(options: ModalOptions) {
|
||||||
|
this.id = options.id;
|
||||||
|
this.flags = options.flags;
|
||||||
|
this.run = options.run;
|
||||||
|
|
||||||
|
registerModal(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
import {
|
import {
|
||||||
APIInteraction,
|
APIInteraction,
|
||||||
APIInteractionResponseCallbackData,
|
APIInteractionResponseCallbackData,
|
||||||
|
APIModalInteractionResponseCallbackData,
|
||||||
|
InteractionResponseType,
|
||||||
RouteBases,
|
RouteBases,
|
||||||
Routes,
|
Routes,
|
||||||
} from "discord-api-types/v10";
|
} from "discord-api-types/v10";
|
||||||
|
import respond from "../../utils/respond";
|
||||||
|
|
||||||
export class Context {
|
export class Context {
|
||||||
public interaction: APIInteraction;
|
public interaction: APIInteraction;
|
||||||
|
@ -17,7 +20,7 @@ export class Context {
|
||||||
public async editReply(content: APIInteractionResponseCallbackData) {
|
public async editReply(content: APIInteractionResponseCallbackData) {
|
||||||
return await fetch(
|
return await fetch(
|
||||||
`${RouteBases.api}${Routes.webhookMessage(
|
`${RouteBases.api}${Routes.webhookMessage(
|
||||||
this.interaction.application_id,
|
this.interaction.id,
|
||||||
this.interaction.token,
|
this.interaction.token,
|
||||||
)}`,
|
)}`,
|
||||||
{
|
{
|
||||||
|
@ -30,4 +33,31 @@ export class Context {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showModal(content: APIModalInteractionResponseCallbackData) {
|
||||||
|
return await fetch(
|
||||||
|
`${RouteBases.api}${Routes.interactionCallback(
|
||||||
|
this.interaction.id,
|
||||||
|
this.interaction.token,
|
||||||
|
)}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: InteractionResponseType.Modal,
|
||||||
|
data: content,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bot ${this.env.token}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async returnModal(content: APIModalInteractionResponseCallbackData) {
|
||||||
|
return respond({
|
||||||
|
type: InteractionResponseType.Modal,
|
||||||
|
data: content,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
packages/bot/src/structs/contexts/ModalContext.ts
Normal file
12
packages/bot/src/structs/contexts/ModalContext.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { APIModalSubmitInteraction } from "discord-api-types/v10";
|
||||||
|
import { Context } from "./Context";
|
||||||
|
|
||||||
|
export class ModalContext extends Context {
|
||||||
|
public interaction: APIModalSubmitInteraction;
|
||||||
|
|
||||||
|
constructor(interaction: APIModalSubmitInteraction, env: Env) {
|
||||||
|
super(interaction, env);
|
||||||
|
|
||||||
|
this.interaction = interaction;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue