mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-03 05:26:07 +00:00
resolving conflicts and wip interactions closes #19
This commit is contained in:
commit
91db746acb
@ -1,8 +1,5 @@
|
|||||||
{
|
{
|
||||||
"fmt": {
|
"fmt": {
|
||||||
"files": {
|
|
||||||
"exclude": ["vendor"]
|
|
||||||
},
|
|
||||||
"options": {
|
"options": {
|
||||||
"indentWidth": 4,
|
"indentWidth": 4,
|
||||||
"lineWidth": 120
|
"lineWidth": 120
|
||||||
|
@ -33,6 +33,7 @@ import type {
|
|||||||
import type { Snowflake } from "../util/Snowflake.ts";
|
import type { Snowflake } from "../util/Snowflake.ts";
|
||||||
import type { Session } from "../session/Session.ts";
|
import type { Session } from "../session/Session.ts";
|
||||||
import type { Channel } from "../structures/channels/ChannelFactory.ts";
|
import type { Channel } from "../structures/channels/ChannelFactory.ts";
|
||||||
|
import type { Interaction } from "../structures/interactions/InteractionFactory.ts";
|
||||||
import ChannelFactory from "../structures/channels/ChannelFactory.ts";
|
import ChannelFactory from "../structures/channels/ChannelFactory.ts";
|
||||||
import GuildChannel from "../structures/channels/GuildChannel.ts";
|
import GuildChannel from "../structures/channels/GuildChannel.ts";
|
||||||
import ThreadChannel from "../structures/channels/ThreadChannel.ts";
|
import ThreadChannel from "../structures/channels/ThreadChannel.ts";
|
||||||
@ -40,9 +41,9 @@ import ThreadMember from "../structures/ThreadMember.ts";
|
|||||||
import Member from "../structures/Member.ts";
|
import Member from "../structures/Member.ts";
|
||||||
import Message from "../structures/Message.ts";
|
import Message from "../structures/Message.ts";
|
||||||
import User from "../structures/User.ts";
|
import User from "../structures/User.ts";
|
||||||
import Guild from "../structures/guilds/Guild.ts";
|
import Integration from "../structures/Integration.ts"
|
||||||
import Interaction from "../structures/interactions/Interaction.ts";
|
import Guild from "../structures/guilds/Guild.ts";
|
||||||
import { Integration } from "../structures/Integration.ts"
|
import InteractionFactory from "../structures/interactions/InteractionFactory.ts";
|
||||||
|
|
||||||
export type RawHandler<T> = (...args: [Session, number, T]) => void;
|
export type RawHandler<T> = (...args: [Session, number, T]) => void;
|
||||||
export type Handler<T extends unknown[]> = (...args: T) => unknown;
|
export type Handler<T extends unknown[]> = (...args: T) => unknown;
|
||||||
@ -110,12 +111,7 @@ export const GUILD_ROLE_DELETE: RawHandler<DiscordGuildRoleDelete> = (session, _
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const INTERACTION_CREATE: RawHandler<DiscordInteraction> = (session, _shardId, interaction) => {
|
export const INTERACTION_CREATE: RawHandler<DiscordInteraction> = (session, _shardId, interaction) => {
|
||||||
session.unrepliedInteractions.add(BigInt(interaction.id));
|
session.emit("interactionCreate", InteractionFactory.from(session, interaction));
|
||||||
|
|
||||||
// could be improved
|
|
||||||
setTimeout(() => session.unrepliedInteractions.delete(BigInt(interaction.id)), 15 * 60 * 1000);
|
|
||||||
|
|
||||||
session.emit("interactionCreate", new Interaction(session, interaction));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CHANNEL_CREATE: RawHandler<DiscordChannel> = (session, _shardId, channel) => {
|
export const CHANNEL_CREATE: RawHandler<DiscordChannel> = (session, _shardId, channel) => {
|
||||||
|
9
mod.ts
9
mod.ts
@ -42,7 +42,14 @@ export * from "./structures/builders/MessageButton.ts";
|
|||||||
export * from "./structures/builders/MessageSelectMenu.ts";
|
export * from "./structures/builders/MessageSelectMenu.ts";
|
||||||
export * from "./structures/builders/SelectMenuOptionBuilder.ts";
|
export * from "./structures/builders/SelectMenuOptionBuilder.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";
|
export * from "./session/Session.ts";
|
||||||
|
|
||||||
|
@ -39,8 +39,6 @@ export class Session extends EventEmitter {
|
|||||||
rest: ReturnType<typeof createRestManager>;
|
rest: ReturnType<typeof createRestManager>;
|
||||||
gateway: ReturnType<typeof createGatewayManager>;
|
gateway: ReturnType<typeof createGatewayManager>;
|
||||||
|
|
||||||
unrepliedInteractions: Set<bigint> = new Set();
|
|
||||||
|
|
||||||
#botId: Snowflake;
|
#botId: Snowflake;
|
||||||
#applicationId?: Snowflake;
|
#applicationId?: Snowflake;
|
||||||
|
|
||||||
|
@ -76,3 +76,5 @@ export class Integration implements Model {
|
|||||||
account: IntegrationAccount;
|
account: IntegrationAccount;
|
||||||
application?: IntegrationApplication;
|
application?: IntegrationApplication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Integration;
|
||||||
|
@ -48,6 +48,7 @@ export interface CreateMessage {
|
|||||||
allowedMentions?: AllowedMentions;
|
allowedMentions?: AllowedMentions;
|
||||||
files?: FileContent[];
|
files?: FileContent[];
|
||||||
messageReference?: CreateMessageReference;
|
messageReference?: CreateMessageReference;
|
||||||
|
tts?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -270,6 +271,7 @@ export class Message implements Model {
|
|||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
embeds: options.embeds,
|
embeds: options.embeds,
|
||||||
|
tts: options.tts,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import type { Model } from "./Base.ts";
|
import type { Model } from "./Base.ts";
|
||||||
import type { Session } from "../session/Session.ts";
|
import type { Session } from "../session/Session.ts";
|
||||||
import type { Snowflake } from "../util/Snowflake.ts";
|
import type { Snowflake } from "../util/Snowflake.ts";
|
||||||
import type { DiscordWebhook, WebhookTypes } from "../vendor/external.ts";
|
import type { DiscordMessage, DiscordWebhook, WebhookTypes } from "../vendor/external.ts";
|
||||||
|
import type { WebhookOptions } from "../util/Routes.ts";
|
||||||
|
import type { CreateMessage } from "./Message.ts";
|
||||||
import { iconHashToBigInt } from "../util/hash.ts";
|
import { iconHashToBigInt } from "../util/hash.ts";
|
||||||
import User from "./User.ts";
|
import User from "./User.ts";
|
||||||
|
import Message from "./Message.ts";
|
||||||
|
import * as Routes from "../util/Routes.ts";
|
||||||
|
|
||||||
export class Webhook implements Model {
|
export class Webhook implements Model {
|
||||||
constructor(session: Session, data: DiscordWebhook) {
|
constructor(session: Session, data: DiscordWebhook) {
|
||||||
@ -42,6 +46,62 @@ export class Webhook implements Model {
|
|||||||
channelId?: Snowflake;
|
channelId?: Snowflake;
|
||||||
guildId?: Snowflake;
|
guildId?: Snowflake;
|
||||||
user?: User;
|
user?: User;
|
||||||
|
|
||||||
|
async execute(options?: WebhookOptions & CreateMessage & { avatarUrl?: string, username?: string }) {
|
||||||
|
if (!this.token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
content: options?.content,
|
||||||
|
embeds: options?.embeds,
|
||||||
|
tts: options?.tts,
|
||||||
|
allowed_mentions: options?.allowedMentions,
|
||||||
|
// @ts-ignore: TODO: component builder or something
|
||||||
|
components: options?.components,
|
||||||
|
file: options?.files,
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = await this.session.rest.sendRequest<DiscordMessage>(this.session.rest, {
|
||||||
|
url: Routes.WEBHOOK(this.id, this.token!, {
|
||||||
|
wait: options?.wait,
|
||||||
|
threadId: options?.threadId,
|
||||||
|
}),
|
||||||
|
method: "POST",
|
||||||
|
payload: this.session.rest.createRequestBody(this.session.rest, {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (options?.wait ?? true) ? new Message(this.session, message) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const message = await this.session.rest.runMethod<DiscordWebhook>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.WEBHOOK_TOKEN(this.id, this.token),
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Webhook(this.session, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchMessage(messageId: Snowflake) {
|
||||||
|
if (!this.token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await this.session.rest.runMethod<DiscordMessage>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.WEBHOOK_MESSAGE(this.id, this.token, messageId),
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Message(this.session, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Webhook;
|
export default Webhook;
|
||||||
|
@ -7,6 +7,7 @@ import type VoiceChannel from "./VoiceChannel.ts";
|
|||||||
import type DMChannel from "./DMChannel.ts";
|
import type DMChannel from "./DMChannel.ts";
|
||||||
import type NewsChannel from "./NewsChannel.ts";
|
import type NewsChannel from "./NewsChannel.ts";
|
||||||
import type ThreadChannel from "./ThreadChannel.ts";
|
import type ThreadChannel from "./ThreadChannel.ts";
|
||||||
|
import type StageChannel from "./StageChannel.ts";
|
||||||
import { ChannelTypes } from "../../vendor/external.ts";
|
import { ChannelTypes } from "../../vendor/external.ts";
|
||||||
import { textBasedChannels } from "./TextChannel.ts";
|
import { textBasedChannels } from "./TextChannel.ts";
|
||||||
|
|
||||||
@ -43,6 +44,10 @@ export abstract class BaseChannel implements Model {
|
|||||||
return this.type === ChannelTypes.GuildPublicThread || this.type === ChannelTypes.GuildPrivateThread;
|
return this.type === ChannelTypes.GuildPublicThread || this.type === ChannelTypes.GuildPrivateThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isStage(): this is StageChannel {
|
||||||
|
return this.type === ChannelTypes.GuildStageVoice;
|
||||||
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return `<#${this.id}>`;
|
return `<#${this.id}>`;
|
||||||
}
|
}
|
||||||
|
40
structures/interactions/AutoCompleteInteraction.ts
Normal file
40
structures/interactions/AutoCompleteInteraction.ts
Normal 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 "./CommandInteraction.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;
|
84
structures/interactions/BaseInteraction.ts
Normal file
84
structures/interactions/BaseInteraction.ts
Normal 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;
|
150
structures/interactions/CommandInteraction.ts
Normal file
150
structures/interactions/CommandInteraction.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
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 type { MessageFlags } from "../../util/shared/flags.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";
|
||||||
|
import Webhook from "../Webhook.ts";
|
||||||
|
import * as Routes from "../../util/Routes.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 Pick<CreateMessage, "allowedMentions" | "content" | "embeds" | "files"> {
|
||||||
|
customId?: string;
|
||||||
|
title?: string;
|
||||||
|
// components?: MessageComponents;
|
||||||
|
flags?: MessageFlags;
|
||||||
|
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;
|
||||||
|
|
||||||
|
async sendFollowUp(options: InteractionApplicationCommandCallbackData): Promise<Message> {
|
||||||
|
const message = await Webhook.prototype.execute.call({
|
||||||
|
id: this.applicationId!,
|
||||||
|
token: this.token,
|
||||||
|
session: this.session,
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
return message!;
|
||||||
|
}
|
||||||
|
|
||||||
|
async respond({ type, data: options }: InteractionResponse): Promise<Message | undefined> {
|
||||||
|
const data = {
|
||||||
|
content: options?.content,
|
||||||
|
custom_id: options?.customId,
|
||||||
|
file: options?.files,
|
||||||
|
allowed_mentions: options?.allowedMentions,
|
||||||
|
flags: options?.flags,
|
||||||
|
chocies: options?.choices,
|
||||||
|
embeds: options?.embeds,
|
||||||
|
title: options?.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.respond) {
|
||||||
|
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, data, file: options?.files },
|
||||||
|
headers: { "Authorization": "" },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.responded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendFollowUp(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommandInteraction;
|
233
structures/interactions/CommandInteractionOptionResolver.ts
Normal file
233
structures/interactions/CommandInteractionOptionResolver.ts
Normal 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;
|
45
structures/interactions/ComponentInteraction.ts
Normal file
45
structures/interactions/ComponentInteraction.ts
Normal 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;
|
@ -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;
|
|
34
structures/interactions/InteractionFactory.ts
Normal file
34
structures/interactions/InteractionFactory.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InteractionFactory;
|
50
structures/interactions/ModalSubmitInteraction.ts
Normal file
50
structures/interactions/ModalSubmitInteraction.ts
Normal 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;
|
38
structures/interactions/PingInteraction.ts
Normal file
38
structures/interactions/PingInteraction.ts
Normal 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;
|
@ -144,11 +144,26 @@ export function INTERACTION_ID_TOKEN(interactionId: Snowflake, token: string) {
|
|||||||
return `/interactions/${interactionId}/${token}/callback`;
|
return `/interactions/${interactionId}/${token}/callback`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WEBHOOK(webhookId: Snowflake, token: string, options?: { wait?: boolean; threadId?: Snowflake }) {
|
export function WEBHOOK_MESSAGE(webhookId: Snowflake, token: string, messageId: Snowflake) {
|
||||||
let url = `/webhooks/${webhookId}/${token}?`;
|
return `/webhooks/${webhookId}/${token}/messages/${messageId}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (options?.wait !== undefined) url += `wait=${options.wait}`;
|
export function WEBHOOK_TOKEN(webhookId: Snowflake, token?: string) {
|
||||||
if (options?.threadId) url += `threadId=${options.threadId}`;
|
if (!token) return `/webhooks/${webhookId}`;
|
||||||
|
return `/webhooks/${webhookId}/${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebhookOptions {
|
||||||
|
wait?: boolean;
|
||||||
|
threadId?: Snowflake;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WEBHOOK(webhookId: Snowflake, token: string, options?: WebhookOptions) {
|
||||||
|
let url = `/webhooks/${webhookId}/${token}`;
|
||||||
|
|
||||||
|
if (options?.wait) url += `?wait=${options.wait}`;
|
||||||
|
if (options?.threadId) url += `?threadId=${options.threadId}`;
|
||||||
|
if (options?.wait && options.threadId) url += `?wait=${options.wait}&threadId=${options.threadId}`;
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user