2020-12-10 04:36:36 +00:00
|
|
|
import { Client } from '../models/client.ts'
|
2021-02-10 12:29:21 +00:00
|
|
|
import {
|
|
|
|
AllowedMentionsPayload,
|
2021-02-12 11:37:38 +00:00
|
|
|
ChannelTypes,
|
2021-02-10 12:29:21 +00:00
|
|
|
EmbedPayload,
|
|
|
|
MessageOptions
|
|
|
|
} from '../types/channel.ts'
|
2020-12-11 10:17:05 +00:00
|
|
|
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
2020-12-10 04:36:36 +00:00
|
|
|
import {
|
2021-02-10 12:29:21 +00:00
|
|
|
InteractionApplicationCommandData,
|
|
|
|
InteractionApplicationCommandOption,
|
2021-02-12 11:37:38 +00:00
|
|
|
InteractionChannelPayload,
|
2020-12-10 04:36:36 +00:00
|
|
|
InteractionPayload,
|
2021-02-10 12:29:21 +00:00
|
|
|
InteractionResponseFlags,
|
2020-12-10 04:36:36 +00:00
|
|
|
InteractionResponsePayload,
|
2021-02-10 12:29:21 +00:00
|
|
|
InteractionResponseType,
|
2021-02-12 11:37:38 +00:00
|
|
|
InteractionType,
|
|
|
|
SlashCommandOptionType
|
2020-12-10 04:36:36 +00:00
|
|
|
} from '../types/slash.ts'
|
2021-02-15 08:08:07 +00:00
|
|
|
import { Dict } from '../utils/dict.ts'
|
2021-02-12 11:37:38 +00:00
|
|
|
import { Permissions } from '../utils/permissions.ts'
|
2021-01-25 11:28:46 +00:00
|
|
|
import { SnowflakeBase } from './base.ts'
|
2021-02-12 11:37:38 +00:00
|
|
|
import { Channel } from './channel.ts'
|
2020-12-10 04:36:36 +00:00
|
|
|
import { Embed } from './embed.ts'
|
2020-12-10 06:55:52 +00:00
|
|
|
import { Guild } from './guild.ts'
|
|
|
|
import { Member } from './member.ts'
|
2020-12-11 10:17:05 +00:00
|
|
|
import { Message } from './message.ts'
|
2021-02-12 11:37:38 +00:00
|
|
|
import { Role } from './role.ts'
|
2020-12-11 10:17:05 +00:00
|
|
|
import { GuildTextChannel, TextChannel } from './textChannel.ts'
|
2020-12-10 06:55:52 +00:00
|
|
|
import { User } from './user.ts'
|
2020-12-11 10:17:05 +00:00
|
|
|
|
2021-01-24 14:20:49 +00:00
|
|
|
interface WebhookMessageOptions extends MessageOptions {
|
2020-12-11 10:17:05 +00:00
|
|
|
embeds?: Embed[]
|
|
|
|
name?: string
|
|
|
|
avatar?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type AllWebhookMessageOptions = string | WebhookMessageOptions
|
2020-12-10 04:36:36 +00:00
|
|
|
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Interaction Message related Options */
|
|
|
|
export interface InteractionMessageOptions {
|
2020-12-10 04:36:36 +00:00
|
|
|
content?: string
|
2021-02-10 12:29:21 +00:00
|
|
|
embeds?: EmbedPayload[]
|
2020-12-10 04:36:36 +00:00
|
|
|
tts?: boolean
|
2021-02-10 12:29:21 +00:00
|
|
|
flags?: number | InteractionResponseFlags[]
|
|
|
|
allowedMentions?: AllowedMentionsPayload
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface InteractionResponse extends InteractionMessageOptions {
|
|
|
|
/** Type of Interaction Response */
|
|
|
|
type?: InteractionResponseType
|
|
|
|
/** Whether the Message Response should be Ephemeral (only visible to User) or not */
|
|
|
|
ephemeral?: boolean
|
2020-12-10 04:36:36 +00:00
|
|
|
}
|
|
|
|
|
2021-02-12 11:37:38 +00:00
|
|
|
/** Represents a Channel Object for an Option in Slash Command */
|
|
|
|
export class InteractionChannel extends SnowflakeBase {
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Name of the Channel */
|
2021-02-12 11:37:38 +00:00
|
|
|
name: string
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Channel Type */
|
2021-02-12 11:37:38 +00:00
|
|
|
type: ChannelTypes
|
|
|
|
permissions: Permissions
|
|
|
|
|
|
|
|
constructor(client: Client, data: InteractionChannelPayload) {
|
|
|
|
super(client)
|
2021-03-12 08:01:48 +00:00
|
|
|
this.id = data.id
|
2021-02-12 11:37:38 +00:00
|
|
|
this.name = data.name
|
|
|
|
this.type = data.type
|
|
|
|
this.permissions = new Permissions(data.permissions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Resolve to actual Channel object if present in Cache */
|
|
|
|
async resolve<T = Channel>(): Promise<T | undefined> {
|
|
|
|
return this.client.channels.get<T>(this.id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface InteractionApplicationCommandResolved {
|
|
|
|
users: Dict<InteractionUser>
|
|
|
|
members: Dict<Member>
|
|
|
|
channels: Dict<InteractionChannel>
|
|
|
|
roles: Dict<Role>
|
|
|
|
}
|
|
|
|
|
|
|
|
export class InteractionUser extends User {
|
|
|
|
member?: Member
|
|
|
|
}
|
|
|
|
|
2021-01-25 11:28:46 +00:00
|
|
|
export class Interaction extends SnowflakeBase {
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Type of Interaction */
|
|
|
|
type: InteractionType
|
|
|
|
/** Interaction Token */
|
2020-12-10 04:36:36 +00:00
|
|
|
token: string
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Interaction ID */
|
2020-12-10 04:36:36 +00:00
|
|
|
id: string
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Data sent with Interaction. Only applies to Application Command */
|
2021-02-10 12:29:21 +00:00
|
|
|
data?: InteractionApplicationCommandData
|
|
|
|
/** Channel in which Interaction was initiated */
|
|
|
|
channel?: TextChannel | GuildTextChannel
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Guild in which Interaction was initiated */
|
2021-02-10 12:29:21 +00:00
|
|
|
guild?: Guild
|
|
|
|
/** Member object of who initiated the Interaction */
|
|
|
|
member?: Member
|
|
|
|
/** User object of who invoked Interaction */
|
|
|
|
user: User
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Whether we have responded to Interaction or not */
|
2021-02-10 12:29:21 +00:00
|
|
|
responded: boolean = false
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Resolved data for Snowflakes in Slash Command Arguments */
|
2021-02-12 11:37:38 +00:00
|
|
|
resolved: InteractionApplicationCommandResolved
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Whether response was deferred or not */
|
|
|
|
deferred: boolean = false
|
2020-12-10 04:36:36 +00:00
|
|
|
|
2020-12-10 06:55:52 +00:00
|
|
|
constructor(
|
|
|
|
client: Client,
|
|
|
|
data: InteractionPayload,
|
|
|
|
others: {
|
2021-02-10 12:29:21 +00:00
|
|
|
channel?: TextChannel | GuildTextChannel
|
|
|
|
guild?: Guild
|
|
|
|
member?: Member
|
|
|
|
user: User
|
2021-02-12 11:37:38 +00:00
|
|
|
resolved: InteractionApplicationCommandResolved
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
) {
|
2021-01-25 11:28:46 +00:00
|
|
|
super(client)
|
2020-12-10 04:36:36 +00:00
|
|
|
this.type = data.type
|
|
|
|
this.token = data.token
|
2020-12-10 06:55:52 +00:00
|
|
|
this.member = others.member
|
2020-12-10 04:36:36 +00:00
|
|
|
this.id = data.id
|
2021-02-10 12:29:21 +00:00
|
|
|
this.user = others.user
|
2020-12-10 04:36:36 +00:00
|
|
|
this.data = data.data
|
2020-12-10 06:55:52 +00:00
|
|
|
this.guild = others.guild
|
|
|
|
this.channel = others.channel
|
2021-02-12 11:37:38 +00:00
|
|
|
this.resolved = others.resolved
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Name of the Command Used (may change with future additions to Interactions!) */
|
|
|
|
get name(): string | undefined {
|
|
|
|
return this.data?.name
|
2020-12-10 04:36:36 +00:00
|
|
|
}
|
|
|
|
|
2021-02-10 12:29:21 +00:00
|
|
|
get options(): InteractionApplicationCommandOption[] {
|
|
|
|
return this.data?.options ?? []
|
2020-12-16 10:42:52 +00:00
|
|
|
}
|
|
|
|
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Get an option by name */
|
2021-02-12 11:37:38 +00:00
|
|
|
option<T>(name: string): T {
|
|
|
|
const op = this.options.find((e) => e.name === name)
|
|
|
|
if (op === undefined || op.value === undefined) return undefined as any
|
|
|
|
if (op.type === SlashCommandOptionType.USER)
|
2021-03-12 09:31:14 +00:00
|
|
|
const u: InteractionUser = this.resolved.users[op.value] as any
|
|
|
|
if (this.resolved.members[op.value] !== undefined) u.member = this.resolved.members[op.value] as any
|
|
|
|
return u
|
2021-02-12 11:37:38 +00:00
|
|
|
else if (op.type === SlashCommandOptionType.ROLE)
|
|
|
|
return this.resolved.roles[op.value] as any
|
|
|
|
else if (op.type === SlashCommandOptionType.CHANNEL)
|
|
|
|
return this.resolved.channels[op.value] as any
|
|
|
|
else return op.value
|
2020-12-11 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
2020-12-16 10:42:52 +00:00
|
|
|
/** Respond to an Interaction */
|
2020-12-10 04:36:36 +00:00
|
|
|
async respond(data: InteractionResponse): Promise<Interaction> {
|
2021-02-10 12:29:21 +00:00
|
|
|
if (this.responded) throw new Error('Already responded to Interaction')
|
|
|
|
let flags = 0
|
2021-02-17 08:55:04 +00:00
|
|
|
if (data.ephemeral === true) flags |= InteractionResponseFlags.EPHEMERAL
|
2021-02-10 12:29:21 +00:00
|
|
|
if (data.flags !== undefined) {
|
|
|
|
if (Array.isArray(data.flags))
|
|
|
|
flags = data.flags.reduce((p, a) => p | a, flags)
|
|
|
|
else if (typeof data.flags === 'number') flags |= data.flags
|
|
|
|
}
|
2020-12-10 04:36:36 +00:00
|
|
|
const payload: InteractionResponsePayload = {
|
2021-02-17 08:55:04 +00:00
|
|
|
type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
2020-12-10 04:36:36 +00:00
|
|
|
data:
|
|
|
|
data.type === undefined ||
|
|
|
|
data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE ||
|
|
|
|
data.type === InteractionResponseType.CHANNEL_MESSAGE
|
|
|
|
? {
|
|
|
|
content: data.content ?? '',
|
|
|
|
embeds: data.embeds,
|
|
|
|
tts: data.tts ?? false,
|
2021-02-10 12:29:21 +00:00
|
|
|
flags,
|
|
|
|
allowed_mentions: data.allowedMentions ?? undefined
|
2020-12-10 04:36:36 +00:00
|
|
|
}
|
|
|
|
: undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
await this.client.rest.post(
|
|
|
|
INTERACTION_CALLBACK(this.id, this.token),
|
|
|
|
payload
|
|
|
|
)
|
2021-02-10 12:29:21 +00:00
|
|
|
this.responded = true
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-02-17 08:55:04 +00:00
|
|
|
/** Defer the Interaction i.e. let the user know bot is processing and will respond later. You only have 15 minutes to edit the response! */
|
|
|
|
async defer(): Promise<Interaction> {
|
2021-02-10 12:29:21 +00:00
|
|
|
await this.respond({
|
2021-02-17 08:55:04 +00:00
|
|
|
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE
|
2021-02-10 12:29:21 +00:00
|
|
|
})
|
2021-02-17 08:55:04 +00:00
|
|
|
this.deferred = true
|
2021-02-10 12:29:21 +00:00
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Reply with a Message to the Interaction */
|
|
|
|
async reply(content: string): Promise<Interaction>
|
|
|
|
async reply(options: InteractionMessageOptions): Promise<Interaction>
|
|
|
|
async reply(
|
|
|
|
content: string,
|
|
|
|
options: InteractionMessageOptions
|
|
|
|
): Promise<Interaction>
|
|
|
|
async reply(
|
|
|
|
content: string | InteractionMessageOptions,
|
|
|
|
messageOptions?: InteractionMessageOptions
|
|
|
|
): Promise<Interaction> {
|
|
|
|
let options: InteractionMessageOptions | undefined =
|
|
|
|
typeof content === 'object' ? content : messageOptions
|
|
|
|
if (
|
|
|
|
typeof content === 'object' &&
|
|
|
|
messageOptions !== undefined &&
|
|
|
|
options !== undefined
|
|
|
|
)
|
|
|
|
Object.assign(options, messageOptions)
|
|
|
|
if (options === undefined) options = {}
|
|
|
|
if (typeof content === 'string') Object.assign(options, { content })
|
|
|
|
|
2021-02-17 08:55:04 +00:00
|
|
|
if (this.deferred && this.responded) {
|
|
|
|
await this.editResponse({
|
|
|
|
content: options.content,
|
|
|
|
embeds: options.embeds,
|
|
|
|
flags: options.flags,
|
|
|
|
allowedMentions: options.allowedMentions
|
2021-02-10 12:29:21 +00:00
|
|
|
})
|
2021-02-17 08:55:04 +00:00
|
|
|
} else
|
|
|
|
await this.respond(
|
|
|
|
Object.assign(options, {
|
|
|
|
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE
|
|
|
|
})
|
|
|
|
)
|
2020-12-10 04:36:36 +00:00
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
2020-12-11 10:17:05 +00:00
|
|
|
|
2020-12-16 10:42:52 +00:00
|
|
|
/** Edit the original Interaction response */
|
2020-12-11 10:17:05 +00:00
|
|
|
async editResponse(data: {
|
|
|
|
content?: string
|
2021-02-17 08:55:04 +00:00
|
|
|
embeds?: EmbedPayload[]
|
|
|
|
flags?: number | number[]
|
|
|
|
allowedMentions?: AllowedMentionsPayload
|
2020-12-11 10:17:05 +00:00
|
|
|
}): Promise<Interaction> {
|
|
|
|
const url = WEBHOOK_MESSAGE(
|
|
|
|
this.client.user?.id as string,
|
|
|
|
this.token,
|
|
|
|
'@original'
|
|
|
|
)
|
|
|
|
await this.client.rest.patch(url, {
|
|
|
|
content: data.content ?? '',
|
2021-02-17 08:55:04 +00:00
|
|
|
embeds: data.embeds ?? [],
|
|
|
|
flags:
|
|
|
|
typeof data.flags === 'object'
|
|
|
|
? data.flags.reduce((p, a) => p | a, 0)
|
|
|
|
: data.flags,
|
|
|
|
allowed_mentions: data.allowedMentions
|
2020-12-11 10:17:05 +00:00
|
|
|
})
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:42:52 +00:00
|
|
|
/** Delete the original Interaction Response */
|
2020-12-12 12:27:35 +00:00
|
|
|
async deleteResponse(): Promise<Interaction> {
|
|
|
|
const url = WEBHOOK_MESSAGE(
|
|
|
|
this.client.user?.id as string,
|
|
|
|
this.token,
|
|
|
|
'@original'
|
|
|
|
)
|
|
|
|
await this.client.rest.delete(url)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2020-12-11 10:17:05 +00:00
|
|
|
get url(): string {
|
|
|
|
return `https://discord.com/api/v8/webhooks/${this.client.user?.id}/${this.token}`
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:42:52 +00:00
|
|
|
/** Send a followup message */
|
2020-12-11 10:17:05 +00:00
|
|
|
async send(
|
|
|
|
text?: string | AllWebhookMessageOptions,
|
|
|
|
option?: AllWebhookMessageOptions
|
|
|
|
): Promise<Message> {
|
|
|
|
if (typeof text === 'object') {
|
|
|
|
option = text
|
|
|
|
text = undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
if (text === undefined && option === undefined) {
|
|
|
|
throw new Error('Either text or option is necessary.')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (option instanceof Embed)
|
|
|
|
option = {
|
|
|
|
embeds: [option]
|
|
|
|
}
|
|
|
|
|
|
|
|
const payload: any = {
|
|
|
|
content: text,
|
|
|
|
embeds:
|
|
|
|
(option as WebhookMessageOptions)?.embed !== undefined
|
|
|
|
? [(option as WebhookMessageOptions).embed]
|
|
|
|
: (option as WebhookMessageOptions)?.embeds !== undefined
|
|
|
|
? (option as WebhookMessageOptions).embeds
|
|
|
|
: undefined,
|
|
|
|
file: (option as WebhookMessageOptions)?.file,
|
|
|
|
tts: (option as WebhookMessageOptions)?.tts,
|
|
|
|
allowed_mentions: (option as WebhookMessageOptions)?.allowedMentions
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((option as WebhookMessageOptions)?.name !== undefined) {
|
|
|
|
payload.username = (option as WebhookMessageOptions)?.name
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((option as WebhookMessageOptions)?.avatar !== undefined) {
|
|
|
|
payload.avatar = (option as WebhookMessageOptions)?.avatar
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
payload.embeds !== undefined &&
|
|
|
|
payload.embeds instanceof Array &&
|
|
|
|
payload.embeds.length > 10
|
|
|
|
)
|
|
|
|
throw new Error(
|
|
|
|
`Cannot send more than 10 embeds through Interaction Webhook`
|
|
|
|
)
|
|
|
|
|
|
|
|
const resp = await this.client.rest.post(`${this.url}?wait=true`, payload)
|
|
|
|
|
|
|
|
const res = new Message(
|
|
|
|
this.client,
|
|
|
|
resp,
|
|
|
|
(this as unknown) as TextChannel,
|
|
|
|
(this as unknown) as User
|
|
|
|
)
|
|
|
|
await res.mentions.fromPayload(resp)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:42:52 +00:00
|
|
|
/** Edit a Followup message */
|
2020-12-11 10:17:05 +00:00
|
|
|
async editMessage(
|
|
|
|
msg: Message | string,
|
|
|
|
data: {
|
|
|
|
content?: string
|
|
|
|
embeds?: Embed[]
|
|
|
|
file?: any
|
|
|
|
allowed_mentions?: {
|
|
|
|
parse?: string
|
|
|
|
roles?: string[]
|
|
|
|
users?: string[]
|
|
|
|
everyone?: boolean
|
|
|
|
}
|
|
|
|
}
|
|
|
|
): Promise<Interaction> {
|
|
|
|
await this.client.rest.patch(
|
|
|
|
WEBHOOK_MESSAGE(
|
|
|
|
this.client.user?.id as string,
|
|
|
|
this.token ?? this.client.token,
|
|
|
|
typeof msg === 'string' ? msg : msg.id
|
|
|
|
),
|
|
|
|
data
|
|
|
|
)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-02-10 12:29:21 +00:00
|
|
|
/** Delete a follow-up Message */
|
2020-12-11 10:17:05 +00:00
|
|
|
async deleteMessage(msg: Message | string): Promise<Interaction> {
|
|
|
|
await this.client.rest.delete(
|
|
|
|
WEBHOOK_MESSAGE(
|
|
|
|
this.client.user?.id as string,
|
|
|
|
this.token ?? this.client.token,
|
|
|
|
typeof msg === 'string' ? msg : msg.id
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return this
|
|
|
|
}
|
2020-12-10 04:36:36 +00:00
|
|
|
}
|