harmony/src/structures/slash.ts

393 lines
12 KiB
TypeScript
Raw Normal View History

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'
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'
2021-03-26 07:08:32 +00:00
import { GuildTextChannel } from './guildTextChannel.ts'
2020-12-10 06:55:52 +00:00
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'
import { 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
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
}
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-03-14 09:47:19 +00:00
guild?: Guild
2021-02-10 12:29:21 +00:00
/** Member object of who initiated the Interaction */
2021-03-14 09:47:19 +00:00
member?: Member
2021-02-10 12:29:21 +00:00
/** 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
2021-03-14 10:16:44 +00:00
_httpRespond?: (d: InteractionResponsePayload) => unknown
_httpResponded?: boolean
2021-03-29 12:41:21 +00:00
applicationID: string
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
}
) {
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-03-29 12:41:21 +00:00
this.applicationID = data.application_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
2021-03-12 09:35:43 +00:00
if (op.type === SlashCommandOptionType.USER) {
2021-03-12 09:31:14 +00:00
const u: InteractionUser = this.resolved.users[op.value] as any
2021-03-26 06:58:06 +00:00
if (this.resolved.members[op.value] !== undefined)
u.member = this.resolved.members[op.value]
2021-03-12 09:42:38 +00:00
return u as any
2021-03-12 09:35:43 +00:00
} else if (op.type === SlashCommandOptionType.ROLE)
2021-02-12 11:37:38 +00:00
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 ||
2021-03-29 04:09:37 +00:00
data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE ||
data.type === InteractionResponseType.CHANNEL_MESSAGE
2020-12-10 04:36:36 +00:00
? {
2021-03-29 04:09:37 +00:00
content: data.content ?? '',
embeds: data.embeds,
tts: data.tts ?? false,
flags,
allowed_mentions: data.allowedMentions ?? undefined
}
2020-12-10 04:36:36 +00:00
: undefined
}
2021-03-14 10:16:44 +00:00
if (this._httpRespond !== undefined && this._httpResponded !== true) {
this._httpResponded = true
await this._httpRespond(payload)
} else
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! */
2021-03-26 06:58:06 +00:00
async defer(ephemeral = false): Promise<Interaction> {
2021-02-10 12:29:21 +00:00
await this.respond({
2021-03-26 06:58:06 +00:00
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE,
flags: ephemeral ? 1 << 6 : 0
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(
2021-03-29 12:41:21 +00:00
this.applicationID,
2020-12-11 10:17:05 +00:00
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 */
async deleteResponse(): Promise<Interaction> {
const url = WEBHOOK_MESSAGE(
2021-03-29 12:41:21 +00:00
this.applicationID,
this.token,
'@original'
)
await this.client.rest.delete(url)
return this
}
2020-12-11 10:17:05 +00:00
get url(): string {
2021-03-29 12:41:21 +00:00
return `https://discord.com/api/v8/webhooks/${this.applicationID}/${this.token}`
2020-12-11 10:17:05 +00:00
}
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
2021-03-29 04:09:37 +00:00
? (option as WebhookMessageOptions).embeds
: undefined,
2020-12-11 10:17:05 +00:00
file: (option as WebhookMessageOptions)?.file,
2021-03-29 04:09:37 +00:00
files: (option as WebhookMessageOptions)?.files,
2020-12-11 10:17:05 +00:00
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(
2021-03-29 12:41:21 +00:00
this.applicationID,
2020-12-11 10:17:05 +00:00
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(
2021-03-29 12:41:21 +00:00
this.applicationID,
2020-12-11 10:17:05 +00:00
this.token ?? this.client.token,
typeof msg === 'string' ? msg : msg.id
)
)
return this
}
2020-12-10 04:36:36 +00:00
}