wip: interactions

This commit is contained in:
Yuzu 2022-07-04 21:11:35 -05:00
parent 948cd2f717
commit 971f541f83
13 changed files with 676 additions and 155 deletions

36
:x Normal file
View File

@ -0,0 +1,36 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { ApplicationCommandTypes, DiscordInteraction } from "../../vendor/external.ts";
import { InteractionResponseTypes } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import * as Routes from "../../util/Routes.ts";
export class PingInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.commandId = data.data!.id;
this.commandName = data.data!.name;
this.commandType = data.data!.type;
this.commandGuildId = data.data!.guild_id;
}
commandId: Snowflake;
commandName: string;
commandType: ApplicationCommandTypes;
commandGuildId?: Snowflake;
async pong() {
await this.session.rest.runMethod<undefined>(
this.session.rest,
"POST",
Routes.INTERACTION_ID_TOKEN(this.id, this.token),
{
type: InteractionResponseTypes.Pong,
}
);
}
}
export default PingInteraction;

View File

@ -38,7 +38,7 @@ import Member from "../structures/Member.ts";
import Message from "../structures/Message.ts";
import User from "../structures/User.ts";
import Guild from "../structures/guilds/Guild.ts";
import Interaction from "../structures/interactions/Interaction.ts";
import InteractionFactory from "../structures/interactions/interactions/InteractionFactory.ts";
export type RawHandler<T> = (...args: [Session, number, T]) => void;
export type Handler<T extends unknown[]> = (...args: T) => unknown;
@ -106,12 +106,7 @@ export const GUILD_ROLE_DELETE: RawHandler<DiscordGuildRoleDelete> = (session, _
}
export const INTERACTION_CREATE: RawHandler<DiscordInteraction> = (session, _shardId, interaction) => {
session.unrepliedInteractions.add(BigInt(interaction.id));
// could be improved
setTimeout(() => session.unrepliedInteractions.delete(BigInt(interaction.id)), 15 * 60 * 1000);
session.emit("interactionCreate", new Interaction(session, interaction));
session.emit("interactionCreate", InteractionFactory.from(session, interaction));
};
export const CHANNEL_CREATE: RawHandler<DiscordChannel> = (session, _shardId, channel) => {

9
mod.ts
View File

@ -37,7 +37,14 @@ export * from "./structures/guilds/InviteGuild.ts";
export * from "./structures/builders/EmbedBuilder.ts";
export * from "./structures/interactions/Interaction.ts";
export * from "./structures/interactions/AutoCompleteInteraction.ts";
export * from "./structures/interactions/BaseInteraction.ts";
export * from "./structures/interactions/CommandInteraction.ts";
export * from "./structures/interactions/CommandInteractionOptionResolver.ts";
export * from "./structures/interactions/ComponentInteraction.ts";
export * from "./structures/interactions/InteractionFactory.ts";
export * from "./structures/interactions/ModalSubmitInteraction.ts";
export * from "./structures/interactions/PingInteraction.ts";
export * from "./session/Session.ts";

View File

@ -39,8 +39,6 @@ export class Session extends EventEmitter {
rest: ReturnType<typeof createRestManager>;
gateway: ReturnType<typeof createGatewayManager>;
unrepliedInteractions: Set<bigint> = new Set();
#botId: Snowflake;
#applicationId?: Snowflake;

View File

@ -0,0 +1,40 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { ApplicationCommandTypes, DiscordInteraction, InteractionTypes } from "../../vendor/external.ts";
import type { ApplicationCommandOptionChoice } from "./BaseInteraction.ts";
import { InteractionResponseTypes } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import * as Routes from "../../util/Routes.ts";
export class AutoCompleteInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.type = data.type as number;
this.commandId = data.data!.id;
this.commandName = data.data!.name;
this.commandType = data.data!.type;
this.commandGuildId = data.data!.guild_id;
}
override type: InteractionTypes.ApplicationCommandAutocomplete;
commandId: Snowflake;
commandName: string;
commandType: ApplicationCommandTypes;
commandGuildId?: Snowflake;
async respond(choices: ApplicationCommandOptionChoice[]) {
await this.session.rest.runMethod<undefined>(
this.session.rest,
"POST",
Routes.INTERACTION_ID_TOKEN(this.id, this.token),
{
data: { choices },
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
}
);
}
}
export default AutoCompleteInteraction;

View File

@ -0,0 +1,84 @@
import type { Model } from "../Base.ts";
import type { Session } from "../../session/Session.ts";
import type { DiscordInteraction } from "../../vendor/external.ts";
import type CommandInteraction from "./CommandInteraction.ts";
import type PingInteraction from "./PingInteraction.ts";
import { InteractionTypes } from "../../vendor/external.ts";
import { Snowflake } from "../../util/Snowflake.ts";
import User from "../User.ts";
import Member from "../Member.ts";
import Permsisions from "../Permissions.ts";
export abstract class BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
this.session = session;
this.id = data.id;
this.token = data.token;
this.type = data.type;
this.guildId = data.guild_id;
this.channelId = data.channel_id;
this.applicationId = data.application_id;
this.version = data.version;
// @ts-expect-error: vendor error
const perms = data.app_permissions as string;
if (perms) {
this.appPermissions = new Permsisions(BigInt(perms));
}
if (!data.guild_id) {
this.user = new User(session, data.user!);
} else {
this.member = new Member(session, data.member!, data.guild_id);
}
}
readonly session: Session;
readonly id: Snowflake;
readonly token: string;
type: InteractionTypes;
guildId?: Snowflake;
channelId?: Snowflake;
applicationId?: Snowflake;
user?: User;
member?: Member;
appPermissions?: Permsisions;
readonly version: 1;
get createdTimestamp() {
return Snowflake.snowflakeToTimestamp(this.id);
}
get createdAt() {
return new Date(this.createdTimestamp);
}
isCommand(): this is CommandInteraction {
return this.type === InteractionTypes.ApplicationCommand;
}
isAutoComplete() {
return this.type === InteractionTypes.ApplicationCommandAutocomplete;
}
isComponent() {
return this.type === InteractionTypes.MessageComponent;
}
isPing(): this is PingInteraction {
return this.type === InteractionTypes.Ping;
}
isModalSubmit() {
return this.type === InteractionTypes.ModalSubmit;
}
inGuild() {
return !!this.guildId;
}
}
export default BaseInteraction;

View File

@ -0,0 +1,108 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { ApplicationCommandTypes, DiscordMemberWithUser, DiscordInteraction, InteractionTypes } from "../../vendor/external.ts";
import type { CreateMessage } from "../Message.ts";
import { InteractionResponseTypes } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import CommandInteractionOptionResolver from "./CommandInteractionOptionResolver.ts";
import Attachment from "../Attachment.ts";
import User from "../User.ts";
import Member from "../Member.ts";
import Message from "../Message.ts";
import Role from "../Role.ts";
/**
* @link https://discord.com/developers/docs/interactions/slash-commands#interaction-response
* */
export interface InteractionResponse {
type: InteractionResponseTypes;
data?: InteractionApplicationCommandCallbackData;
}
/**
* @link https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionapplicationcommandcallbackdata
* */
export interface InteractionApplicationCommandCallbackData extends Omit<CreateMessage, "messageReference"> {
customId?: string;
title?: string;
// TODO: use builder
// components?: MessageComponents;
flags?: number;
choices?: ApplicationCommandOptionChoice[];
}
/**
* @link https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice
* */
export interface ApplicationCommandOptionChoice {
name: string;
value: string | number;
}
export class CommandInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.type = data.type as number;
this.commandId = data.data!.id;
this.commandName = data.data!.name;
this.commandType = data.data!.type;
this.commandGuildId = data.data!.guild_id;
this.options = new CommandInteractionOptionResolver(data.data!.options ?? []);
this.resolved = {
users: new Map(),
members: new Map(),
roles: new Map(),
attachments: new Map(),
messages: new Map(),
};
if (data.data!.resolved?.users) {
for (const [id, u] of Object.entries(data.data!.resolved.users)) {
this.resolved.users.set(id, new User(session, u));
}
}
if (data.data!.resolved?.members && !!super.guildId) {
for (const [id, m] of Object.entries(data.data!.resolved.members)) {
this.resolved.members.set(id, new Member(session, m as DiscordMemberWithUser, super.guildId!));
}
}
if (data.data!.resolved?.roles && !!super.guildId) {
for (const [id, r] of Object.entries(data.data!.resolved.roles)) {
this.resolved.roles.set(id, new Role(session, r, super.guildId!));
}
}
if (data.data!.resolved?.attachments) {
for (const [id, a] of Object.entries(data.data!.resolved.attachments)) {
this.resolved.attachments.set(id, new Attachment(session, a));
}
}
if (data.data!.resolved?.messages) {
for (const [id, m] of Object.entries(data.data!.resolved.messages)) {
this.resolved.messages.set(id, new Message(session, m));
}
}
}
override type: InteractionTypes.ApplicationCommand;
commandId: Snowflake;
commandName: string;
commandType: ApplicationCommandTypes;
commandGuildId?: Snowflake;
resolved: {
users: Map<Snowflake, User>;
members: Map<Snowflake, Member>;
roles: Map<Snowflake, Role>;
attachments: Map<Snowflake, Attachment>;
messages: Map<Snowflake, Message>;
};
options: CommandInteractionOptionResolver;
responded = false;
}
export default CommandInteraction;

View File

@ -0,0 +1,233 @@
import type { DiscordInteractionDataOption, DiscordInteractionDataResolved } from '../../vendor/external.ts';
import { ApplicationCommandOptionTypes } from "../../vendor/external.ts";
export function transformOasisInteractionDataOption(o: DiscordInteractionDataOption): CommandInteractionOption {
const output: CommandInteractionOption = { ...o, Otherwise: o.value as string | boolean | number | undefined };
switch (o.type) {
case ApplicationCommandOptionTypes.String:
output.String = o.value as string;
break;
case ApplicationCommandOptionTypes.Number:
output.Number = o.value as number;
break;
case ApplicationCommandOptionTypes.Integer:
output.Integer = o.value as number;
break;
case ApplicationCommandOptionTypes.Boolean:
output.Boolean = o.value as boolean;
break;
case ApplicationCommandOptionTypes.Role:
output.Role = BigInt(o.value as string);
break;
case ApplicationCommandOptionTypes.User:
output.User = BigInt(o.value as string);
break;
case ApplicationCommandOptionTypes.Channel:
output.Channel = BigInt(o.value as string);
break;
case ApplicationCommandOptionTypes.Mentionable:
case ApplicationCommandOptionTypes.SubCommand:
case ApplicationCommandOptionTypes.SubCommandGroup:
default:
output.Otherwise = o.value as string | boolean | number | undefined;
}
return output;
}
export interface CommandInteractionOption extends Omit<DiscordInteractionDataOption, 'value'> {
Attachment?: string;
Boolean?: boolean;
User?: bigint;
Role?: bigint;
Number?: number;
Integer?: number;
Channel?: bigint;
String?: string;
Mentionable?: string;
Otherwise: string | number | boolean | bigint | undefined;
}
/**
* Utility class to get the resolved options for a command
* It is really typesafe
* @example const option = ctx.options.getStringOption("name");
*/
export class CommandInteractionOptionResolver {
#subcommand?: string;
#group?: string;
hoistedOptions: CommandInteractionOption[];
resolved?: DiscordInteractionDataResolved;
constructor(options?: DiscordInteractionDataOption[], resolved?: DiscordInteractionDataResolved) {
this.hoistedOptions = options?.map(transformOasisInteractionDataOption) ?? [];
// warning: black magic do not edit and thank djs authors
if (this.hoistedOptions[0]?.type === ApplicationCommandOptionTypes.SubCommandGroup) {
this.#group = this.hoistedOptions[0].name;
this.hoistedOptions = (this.hoistedOptions[0].options ?? []).map(transformOasisInteractionDataOption);
}
if (this.hoistedOptions[0]?.type === ApplicationCommandOptionTypes.SubCommand) {
this.#subcommand = this.hoistedOptions[0].name;
this.hoistedOptions = (this.hoistedOptions[0].options ?? []).map(transformOasisInteractionDataOption);
}
this.resolved = resolved;
}
private getTypedOption(
name: string | number,
type: ApplicationCommandOptionTypes,
properties: Array<keyof CommandInteractionOption>,
required: boolean,
) {
const option = this.get(name, required);
if (!option) {
return;
}
if (option.type !== type) {
// pass
}
if (required === true && properties.every((prop) => typeof option[prop] === "undefined")) {
throw new TypeError(`Properties ${properties.join(', ')} are missing in option ${name}`);
}
return option;
}
get(name: string | number, required: true): CommandInteractionOption;
get(name: string | number, required: boolean): CommandInteractionOption | undefined;
get(name: string | number, required?: boolean) {
const option = this.hoistedOptions.find((o) =>
typeof name === 'number' ? o.name === name.toString() : o.name === name
);
if (!option) {
if (required && name in this.hoistedOptions.map((o) => o.name)) {
throw new TypeError('Option marked as required was undefined');
}
return;
}
return option;
}
/** searches for a string option */
getString(name: string | number, required: true): string;
getString(name: string | number, required?: boolean): string | undefined;
getString(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.String, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a number option */
getNumber(name: string | number, required: true): number;
getNumber(name: string | number, required?: boolean): number | undefined;
getNumber(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Number, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searhces for an integer option */
getInteger(name: string | number, required: true): number;
getInteger(name: string | number, required?: boolean): number | undefined;
getInteger(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Integer, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a boolean option */
getBoolean(name: string | number, required: true): boolean;
getBoolean(name: string | number, required?: boolean): boolean | undefined;
getBoolean(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Boolean, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a user option */
getUser(name: string | number, required: true): bigint;
getUser(name: string | number, required?: boolean): bigint | undefined;
getUser(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.User, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a channel option */
getChannel(name: string | number, required: true): bigint;
getChannel(name: string | number, required?: boolean): bigint | undefined;
getChannel(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Channel, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a mentionable-based option */
getMentionable(name: string | number, required: true): string;
getMentionable(name: string | number, required?: boolean): string | undefined;
getMentionable(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Mentionable, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for a mentionable-based option */
getRole(name: string | number, required: true): bigint;
getRole(name: string | number, required?: boolean): bigint | undefined;
getRole(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Role, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for an attachment option */
getAttachment(name: string | number, required: true): string;
getAttachment(name: string | number, required?: boolean): string | undefined;
getAttachment(name: string | number, required = false) {
const option = this.getTypedOption(name, ApplicationCommandOptionTypes.Attachment, ['Otherwise'], required);
return option?.Otherwise ?? undefined;
}
/** searches for the focused option */
getFocused(full = false) {
const focusedOption = this.hoistedOptions.find((option) => option.focused);
if (!focusedOption) {
throw new TypeError('No option found');
}
return full ? focusedOption : focusedOption.Otherwise;
}
getSubCommand(required = true) {
if (required && !this.#subcommand) {
throw new TypeError('Option marked as required was undefined');
}
return [this.#subcommand, this.hoistedOptions];
}
getSubCommandGroup(required = false) {
if (required && !this.#group) {
throw new TypeError('Option marked as required was undefined');
}
return [this.#group, this.hoistedOptions];
}
}
export default CommandInteractionOptionResolver;

View File

@ -0,0 +1,45 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { DiscordInteraction, InteractionTypes } from "../../vendor/external.ts";
import { MessageComponentTypes } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import Message from "../Message.ts";
export class ComponentInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.type = data.type as number;
this.componentType = data.data!.component_type!;
this.customId = data.data!.custom_id;
this.targetId = data.data!.target_id;
this.values = data.data!.values;
this.message = new Message(session, data.message!);
}
override type: InteractionTypes.MessageComponent;
componentType: MessageComponentTypes;
customId?: string;
targetId?: Snowflake;
values?: string[];
message: Message;
responded = false;
isButton() {
return this.componentType === MessageComponentTypes.Button;
}
isActionRow() {
return this.componentType === MessageComponentTypes.ActionRow;
}
isTextInput() {
return this.componentType === MessageComponentTypes.InputText;
}
isSelectMenu() {
return this.componentType === MessageComponentTypes.SelectMenu;
}
}
export default ComponentInteraction;

View File

@ -1,145 +0,0 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type {
DiscordInteraction,
DiscordMessage,
FileContent,
InteractionResponseTypes,
InteractionTypes,
} from "../../vendor/external.ts";
import type { MessageFlags } from "../../util/shared/flags.ts";
import type { AllowedMentions } from "../Message.ts";
import User from "../User.ts";
import Message from "../Message.ts";
import Member from "../Member.ts";
import * as Routes from "../../util/Routes.ts";
export interface InteractionResponse {
type: InteractionResponseTypes;
data?: InteractionApplicationCommandCallbackData;
}
export interface InteractionApplicationCommandCallbackData {
content?: string;
tts?: boolean;
allowedMentions?: AllowedMentions;
files?: FileContent[];
customId?: string;
title?: string;
// components?: Component[];
flags?: MessageFlags;
choices?: ApplicationCommandOptionChoice[];
}
/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */
export interface ApplicationCommandOptionChoice {
name: string;
value: string | number;
}
// TODO: abstract Interaction, CommandInteraction, ComponentInteraction, PingInteraction, etc
export class Interaction implements Model {
constructor(session: Session, data: DiscordInteraction) {
this.session = session;
this.id = data.id;
this.token = data.token;
this.type = data.type;
this.guildId = data.guild_id;
this.channelId = data.channel_id;
this.applicationId = data.application_id;
this.locale = data.locale;
this.data = data.data;
if (!data.guild_id) {
this.user = new User(session, data.user!);
} else {
this.member = new Member(session, data.member!, data.guild_id);
}
}
readonly session: Session;
readonly id: Snowflake;
readonly token: string;
type: InteractionTypes;
guildId?: Snowflake;
channelId?: Snowflake;
applicationId?: Snowflake;
locale?: string;
// deno-lint-ignore no-explicit-any
data: any;
user?: User;
member?: Member;
async respond({ type, data }: InteractionResponse) {
const toSend = {
tts: data?.tts,
title: data?.title,
flags: data?.flags,
content: data?.content,
choices: data?.choices,
custom_id: data?.customId,
allowed_mentions: data?.allowedMentions
? {
users: data.allowedMentions.users,
roles: data.allowedMentions.roles,
parse: data.allowedMentions.parse,
replied_user: data.allowedMentions.repliedUser,
}
: { parse: [] },
};
if (this.session.unrepliedInteractions.delete(BigInt(this.id))) {
await this.session.rest.sendRequest<undefined>(
this.session.rest,
{
url: Routes.INTERACTION_ID_TOKEN(this.id, this.token),
method: "POST",
payload: this.session.rest.createRequestBody(this.session.rest, {
method: "POST",
body: {
type: type,
data: toSend,
file: data?.files,
},
headers: {
// remove authorization header
Authorization: "",
},
}),
},
);
return;
}
const result = await this.session.rest.sendRequest<DiscordMessage>(
this.session.rest,
{
url: Routes.WEBHOOK(this.session.applicationId ?? this.session.botId, this.token),
method: "POST",
payload: this.session.rest.createRequestBody(this.session.rest, {
method: "POST",
body: {
...toSend,
file: data?.files,
},
headers: {
// remove authorization header
Authorization: "",
},
}),
},
);
return new Message(this.session, result);
}
inGuild(): this is Interaction & { user: undefined; guildId: Snowflake; member: Member } {
return !!this.guildId;
}
}
export default Interaction;

View File

@ -0,0 +1,32 @@
import type { Session } from "../../session/Session.ts";
import type { DiscordInteraction } from "../../vendor/external.ts";
import { InteractionTypes } from "../../vendor/external.ts";
import CommandInteraction from "./CommandInteraction.ts";
import ComponentInteraction from "./ComponentInteraction.ts";
import PingInteraction from "./PingInteraction.ts";
import AutoCompleteInteraction from "./AutoCompleteInteraction.ts";
import ModalSubmitInteraction from "./ModalSubmitInteraction.ts";
export type Interaction =
| CommandInteraction
| ComponentInteraction
| PingInteraction
| AutoCompleteInteraction
| ModalSubmitInteraction;
export class InteractionFactory {
static from(session: Session, interaction: DiscordInteraction): Interaction {
switch (interaction.type) {
case InteractionTypes.Ping:
return new PingInteraction(session, interaction);
case InteractionTypes.ApplicationCommand:
return new CommandInteraction(session, interaction);
case InteractionTypes.MessageComponent:
return new ComponentInteraction(session, interaction);
case InteractionTypes.ApplicationCommandAutocomplete:
return new AutoCompleteInteraction(session, interaction);
case InteractionTypes.ModalSubmit:
return new ModalSubmitInteraction(session, interaction);
}
}
}

View File

@ -0,0 +1,50 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { DiscordInteraction, InteractionTypes, MessageComponentTypes, DiscordMessageComponents } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import Message from "../Message.ts";
export class ModalSubmitInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.type = data.type as number;
this.componentType = data.data!.component_type!;
this.customId = data.data!.custom_id;
this.targetId = data.data!.target_id;
this.values = data.data!.values;
this.components = data.data?.components?.map(ModalSubmitInteraction.transformComponent);
if (data.message) {
this.message = new Message(session, data.message);
}
}
override type: InteractionTypes.MessageComponent;
componentType: MessageComponentTypes;
customId?: string;
targetId?: Snowflake;
values?: string[];
message?: Message;
components;
static transformComponent(component: DiscordMessageComponents[number]) {
return {
type: component.type,
components: component.components.map((component) => {
return {
customId: component.custom_id,
value: (component as typeof component & { value: string }).value,
};
}),
};
}
inMessage(): this is ModalSubmitInteraction & { message: Message } {
return !!this.message;
}
}
export default ModalSubmitInteraction;

View File

@ -0,0 +1,38 @@
import type { Model } from "../Base.ts";
import type { Snowflake } from "../../util/Snowflake.ts";
import type { Session } from "../../session/Session.ts";
import type { ApplicationCommandTypes, DiscordInteraction, InteractionTypes } from "../../vendor/external.ts";
import { InteractionResponseTypes } from "../../vendor/external.ts";
import BaseInteraction from "./BaseInteraction.ts";
import * as Routes from "../../util/Routes.ts";
export class PingInteraction extends BaseInteraction implements Model {
constructor(session: Session, data: DiscordInteraction) {
super(session, data);
this.type = data.type as number;
this.commandId = data.data!.id;
this.commandName = data.data!.name;
this.commandType = data.data!.type;
this.commandGuildId = data.data!.guild_id;
}
override type: InteractionTypes.Ping;
commandId: Snowflake;
commandName: string;
commandType: ApplicationCommandTypes;
commandGuildId?: Snowflake;
async pong() {
await this.session.rest.runMethod<undefined>(
this.session.rest,
"POST",
Routes.INTERACTION_ID_TOKEN(this.id, this.token),
{
type: InteractionResponseTypes.Pong,
}
);
}
}
export default PingInteraction;