message component interactions
This commit is contained in:
parent
17e74dce45
commit
b97ec3c225
18 changed files with 461 additions and 86 deletions
4
mod.ts
4
mod.ts
|
@ -39,8 +39,11 @@ export { GuildChannelsManager } from './src/managers/guildChannels.ts'
|
|||
export { GuildManager } from './src/managers/guilds.ts'
|
||||
export * from './src/structures/base.ts'
|
||||
export * from './src/structures/slash.ts'
|
||||
export * from './src/structures/interactions.ts'
|
||||
export * from './src/structures/messageComponents.ts'
|
||||
export * from './src/types/slashCommands.ts'
|
||||
export * from './src/types/interactions.ts'
|
||||
export * from './src/types/messageComponents.ts'
|
||||
export { GuildEmojisManager } from './src/managers/guildEmojis.ts'
|
||||
export { MembersManager } from './src/managers/members.ts'
|
||||
export { MessageReactionsManager } from './src/managers/messageReactions.ts'
|
||||
|
@ -192,3 +195,4 @@ export {
|
|||
isVoiceChannel,
|
||||
default as getChannelByType
|
||||
} from './src/utils/channel.ts'
|
||||
export * from './src/utils/interactions.ts'
|
||||
|
|
|
@ -12,7 +12,6 @@ import { EmojisManager } from '../managers/emojis.ts'
|
|||
import { ActivityGame, ClientActivity } from '../types/presence.ts'
|
||||
import type { Extension } from '../commands/extension.ts'
|
||||
import { SlashClient } from '../interactions/slashClient.ts'
|
||||
import type { Interaction } from '../structures/slash.ts'
|
||||
import { ShardManager } from './shard.ts'
|
||||
import { Application } from '../structures/application.ts'
|
||||
import { Invite } from '../structures/invite.ts'
|
||||
|
@ -113,17 +112,6 @@ export class Client extends HarmonyEventEmitter<ClientEvents> {
|
|||
|
||||
/** Client's presence. Startup one if set before connecting */
|
||||
presence: ClientPresence = new ClientPresence()
|
||||
_decoratedEvents?: {
|
||||
[name: string]: (...args: any[]) => void
|
||||
}
|
||||
|
||||
_decoratedSlash?: Array<{
|
||||
name: string
|
||||
guild?: string
|
||||
parent?: string
|
||||
group?: string
|
||||
handler: (interaction: Interaction) => any
|
||||
}>
|
||||
|
||||
_id?: string
|
||||
|
||||
|
@ -175,13 +163,13 @@ export class Client extends HarmonyEventEmitter<ClientEvents> {
|
|||
this.fetchUncachedReactions = true
|
||||
|
||||
if (
|
||||
this._decoratedEvents !== undefined &&
|
||||
Object.keys(this._decoratedEvents).length !== 0
|
||||
(this as any)._decoratedEvents !== undefined &&
|
||||
Object.keys((this as any)._decoratedEvents).length !== 0
|
||||
) {
|
||||
Object.entries(this._decoratedEvents).forEach((entry) => {
|
||||
this.on(entry[0] as keyof ClientEvents, entry[1].bind(this))
|
||||
Object.entries((this as any)._decoratedEvents).forEach((entry) => {
|
||||
this.on(entry[0] as keyof ClientEvents, (entry as any)[1].bind(this))
|
||||
})
|
||||
this._decoratedEvents = undefined
|
||||
;(this as any)._decoratedEvents = undefined
|
||||
}
|
||||
|
||||
this.clientProperties =
|
||||
|
@ -422,19 +410,23 @@ export class Client extends HarmonyEventEmitter<ClientEvents> {
|
|||
}
|
||||
|
||||
/** Event decorator to create an Event handler from function */
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function event(name?: keyof ClientEvents) {
|
||||
return function (
|
||||
client: Client | Extension,
|
||||
prop: keyof ClientEvents | string
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const c = client as any
|
||||
const listener = ((client as unknown) as {
|
||||
[name in keyof ClientEvents]: (...args: ClientEvents[name]) => any
|
||||
})[(prop as unknown) as keyof ClientEvents]
|
||||
if (typeof listener !== 'function')
|
||||
throw new Error('@event decorator requires a function')
|
||||
if (client._decoratedEvents === undefined) client._decoratedEvents = {}
|
||||
|
||||
if (c._decoratedEvents === undefined) c._decoratedEvents = {}
|
||||
const key = name === undefined ? prop : name
|
||||
|
||||
client._decoratedEvents[key] = listener
|
||||
c._decoratedEvents[key] = listener
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,27 +73,25 @@ export class Extension {
|
|||
/** Events registered by this Extension */
|
||||
events: { [name: string]: (...args: any[]) => {} } = {}
|
||||
|
||||
_decoratedCommands?: { [name: string]: Command }
|
||||
_decoratedEvents?: { [name: string]: (...args: any[]) => any }
|
||||
|
||||
constructor(client: CommandClient) {
|
||||
this.client = client
|
||||
if (this._decoratedCommands !== undefined) {
|
||||
Object.entries(this._decoratedCommands).forEach((entry) => {
|
||||
const self = this as any
|
||||
if (self._decoratedCommands !== undefined) {
|
||||
Object.entries(self._decoratedCommands).forEach((entry: any) => {
|
||||
entry[1].extension = this
|
||||
this.commands.add(entry[1])
|
||||
})
|
||||
this._decoratedCommands = undefined
|
||||
self._decoratedCommands = undefined
|
||||
}
|
||||
|
||||
if (
|
||||
this._decoratedEvents !== undefined &&
|
||||
Object.keys(this._decoratedEvents).length !== 0
|
||||
self._decoratedEvents !== undefined &&
|
||||
Object.keys(self._decoratedEvents).length !== 0
|
||||
) {
|
||||
Object.entries(this._decoratedEvents).forEach((entry) => {
|
||||
Object.entries(self._decoratedEvents).forEach((entry: any) => {
|
||||
this.listen(entry[0] as keyof ClientEvents, entry[1].bind(this))
|
||||
})
|
||||
this._decoratedEvents = undefined
|
||||
self._decoratedEvents = undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@ import { Permissions } from '../../utils/permissions.ts'
|
|||
import type { Gateway, GatewayEventHandler } from '../mod.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { Role } from '../../structures/role.ts'
|
||||
import { RolePayload } from '../../types/role.ts'
|
||||
import { InteractionChannelPayload } from '../../types/slashCommands.ts'
|
||||
import { Message } from '../../structures/message.ts'
|
||||
import { TextChannel } from '../../structures/textChannel.ts'
|
||||
import { MessageComponentInteraction } from '../../structures/messageComponents.ts'
|
||||
|
||||
export const interactionCreate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
|
@ -32,13 +37,22 @@ export const interactionCreate: GatewayEventHandler = async (
|
|||
const guild =
|
||||
d.guild_id === undefined
|
||||
? undefined
|
||||
: await gateway.client.guilds.get(d.guild_id)
|
||||
: (await gateway.client.guilds.get(d.guild_id)) ??
|
||||
new Guild(gateway.client, { unavailable: true, id: d.guild_id } as any)
|
||||
|
||||
if (d.member !== undefined)
|
||||
await guild?.members.set(d.member.user.id, d.member)
|
||||
const member =
|
||||
d.member !== undefined
|
||||
? (((await guild?.members.get(d.member.user.id)) as unknown) as Member)
|
||||
? (await guild?.members.get(d.member.user.id))! ??
|
||||
new Member(
|
||||
gateway.client,
|
||||
d.member!,
|
||||
new User(gateway.client, d.member.user),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
guild!,
|
||||
new Permissions(d.member.permissions)
|
||||
)
|
||||
: undefined
|
||||
if (d.user !== undefined) await gateway.client.users.set(d.user.id, d.user)
|
||||
const dmUser =
|
||||
|
@ -47,9 +61,9 @@ export const interactionCreate: GatewayEventHandler = async (
|
|||
const user = member !== undefined ? member.user : dmUser
|
||||
if (user === undefined) return
|
||||
|
||||
const channel =
|
||||
(await gateway.client.channels.get<GuildTextBasedChannel>(d.channel_id)) ??
|
||||
(await gateway.client.channels.fetch<GuildTextBasedChannel>(d.channel_id))
|
||||
const channel = await gateway.client.channels.get<GuildTextBasedChannel>(
|
||||
d.channel_id
|
||||
)
|
||||
|
||||
const resolved: InteractionApplicationCommandResolved = {
|
||||
users: {},
|
||||
|
@ -58,9 +72,11 @@ export const interactionCreate: GatewayEventHandler = async (
|
|||
roles: {}
|
||||
}
|
||||
|
||||
if (d.data?.resolved !== undefined) {
|
||||
for (const [id, data] of Object.entries(d.data.resolved.users ?? {})) {
|
||||
await gateway.client.users.set(id, data)
|
||||
if ((d.data as any)?.resolved !== undefined) {
|
||||
for (const [id, data] of Object.entries(
|
||||
(d.data as any)?.resolved.users ?? {}
|
||||
)) {
|
||||
await gateway.client.users.set(id, data as UserPayload)
|
||||
resolved.users[id] = ((await gateway.client.users.get(
|
||||
id
|
||||
)) as unknown) as User
|
||||
|
@ -68,43 +84,69 @@ export const interactionCreate: GatewayEventHandler = async (
|
|||
resolved.users[id].member = resolved.members[id]
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.resolved.members ?? {})) {
|
||||
for (const [id, data] of Object.entries(
|
||||
(d.data as any)?.resolved.members ?? {}
|
||||
)) {
|
||||
const roles = await guild?.roles.array()
|
||||
let permissions = new Permissions(Permissions.DEFAULT)
|
||||
if (roles !== undefined) {
|
||||
const mRoles = roles.filter(
|
||||
(r) => (data?.roles?.includes(r.id) as boolean) || r.id === guild?.id
|
||||
(r) =>
|
||||
((data as any)?.roles?.includes(r.id) as boolean) ||
|
||||
r.id === guild?.id
|
||||
)
|
||||
permissions = new Permissions(mRoles.map((r) => r.permissions))
|
||||
}
|
||||
data.user = (d.data.resolved.users?.[id] as unknown) as UserPayload
|
||||
;(data as any).user = ((d.data as any).resolved.users?.[
|
||||
id
|
||||
] as unknown) as UserPayload
|
||||
resolved.members[id] = new Member(
|
||||
gateway.client,
|
||||
data,
|
||||
data as any,
|
||||
resolved.users[id],
|
||||
guild as Guild,
|
||||
permissions
|
||||
)
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.resolved.roles ?? {})) {
|
||||
for (const [id, data] of Object.entries(
|
||||
(d.data as any).resolved.roles ?? {}
|
||||
)) {
|
||||
if (guild !== undefined) {
|
||||
await guild.roles.set(id, data)
|
||||
await guild.roles.set(id, data as RolePayload)
|
||||
resolved.roles[id] = ((await guild.roles.get(id)) as unknown) as Role
|
||||
} else {
|
||||
resolved.roles[id] = new Role(
|
||||
gateway.client,
|
||||
data,
|
||||
data as any,
|
||||
(guild as unknown) as Guild
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.resolved.channels ?? {})) {
|
||||
resolved.channels[id] = new InteractionChannel(gateway.client, data)
|
||||
for (const [id, data] of Object.entries(
|
||||
(d.data as any).resolved.channels ?? {}
|
||||
)) {
|
||||
resolved.channels[id] = new InteractionChannel(
|
||||
gateway.client,
|
||||
data as InteractionChannelPayload
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let message: Message | undefined
|
||||
if (d.message !== undefined) {
|
||||
const channel = (await gateway.client.channels.get<TextChannel>(
|
||||
d.message.channel_id
|
||||
))!
|
||||
message = new Message(
|
||||
gateway.client,
|
||||
d.message,
|
||||
channel,
|
||||
new User(gateway.client, d.message.author)
|
||||
)
|
||||
}
|
||||
|
||||
let interaction
|
||||
if (d.type === InteractionType.APPLICATION_COMMAND) {
|
||||
interaction = new SlashCommandInteraction(gateway.client, d, {
|
||||
|
@ -114,12 +156,21 @@ export const interactionCreate: GatewayEventHandler = async (
|
|||
user,
|
||||
resolved
|
||||
})
|
||||
} else if (d.type === InteractionType.MESSAGE_COMPONENT) {
|
||||
interaction = new MessageComponentInteraction(gateway.client, d, {
|
||||
member,
|
||||
guild,
|
||||
channel,
|
||||
user,
|
||||
message
|
||||
})
|
||||
} else {
|
||||
interaction = new Interaction(gateway.client, d, {
|
||||
member,
|
||||
guild,
|
||||
channel,
|
||||
user
|
||||
user,
|
||||
message
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ import { applicationCommandCreate } from './applicationCommandCreate.ts'
|
|||
import { applicationCommandDelete } from './applicationCommandDelete.ts'
|
||||
import { applicationCommandUpdate } from './applicationCommandUpdate.ts'
|
||||
import type { SlashCommand } from '../../interactions/slashCommand.ts'
|
||||
import { MessageComponentInteraction } from '../../structures/messageComponents.ts'
|
||||
|
||||
export const gatewayHandlers: {
|
||||
[eventCode in GatewayEvents]: GatewayEventHandler | undefined
|
||||
|
@ -364,7 +365,12 @@ export type ClientEvents = {
|
|||
* An Interaction was created
|
||||
* @param interaction Created interaction object
|
||||
*/
|
||||
interactionCreate: [interaction: Interaction | SlashCommandInteraction]
|
||||
interactionCreate: [
|
||||
interaction:
|
||||
| Interaction
|
||||
| SlashCommandInteraction
|
||||
| MessageComponentInteraction
|
||||
]
|
||||
|
||||
/**
|
||||
* When debug message was made
|
||||
|
|
|
@ -17,6 +17,7 @@ import { User } from '../structures/user.ts'
|
|||
import { HarmonyEventEmitter } from '../utils/events.ts'
|
||||
import { encodeText, decodeText } from '../utils/encoding.ts'
|
||||
import { SlashCommandsManager } from './slashCommand.ts'
|
||||
import { MessageComponentInteraction } from '../structures/messageComponents.ts'
|
||||
|
||||
export type SlashCommandHandlerCallback = (interaction: Interaction) => unknown
|
||||
export interface SlashCommandHandler {
|
||||
|
@ -77,8 +78,10 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
|
|||
|
||||
this.enabled = options.enabled ?? true
|
||||
|
||||
if (this.client?._decoratedSlash !== undefined) {
|
||||
this.client._decoratedSlash.forEach((e) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const client = this.client as any
|
||||
if (client?._decoratedSlash !== undefined) {
|
||||
client._decoratedSlash.forEach((e: any) => {
|
||||
e.handler = e.handler.bind(this.client)
|
||||
this.handlers.push(e)
|
||||
})
|
||||
|
@ -205,7 +208,10 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
|
|||
|
||||
/** Process an incoming Interaction */
|
||||
private async _process(
|
||||
interaction: Interaction | SlashCommandInteraction
|
||||
interaction:
|
||||
| Interaction
|
||||
| SlashCommandInteraction
|
||||
| MessageComponentInteraction
|
||||
): Promise<void> {
|
||||
if (!this.enabled) return
|
||||
|
||||
|
@ -282,7 +288,7 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
|
|||
member: payload.member as any,
|
||||
guild: payload.guild_id as any,
|
||||
channel: payload.channel_id as any,
|
||||
resolved: ((payload.data
|
||||
resolved: (((payload.data as any)
|
||||
?.resolved as unknown) as InteractionApplicationCommandResolved) ?? {
|
||||
users: {},
|
||||
members: {},
|
||||
|
@ -400,12 +406,14 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
|
|||
/** Decorator to create a Slash Command handler */
|
||||
export function slash(name?: string, guild?: string) {
|
||||
return function (client: Client | SlashClient | SlashModule, prop: string) {
|
||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const c = client as any
|
||||
if (c._decoratedSlash === undefined) c._decoratedSlash = []
|
||||
const item = (client as { [name: string]: any })[prop]
|
||||
if (typeof item !== 'function') {
|
||||
throw new Error('@slash decorator requires a function')
|
||||
} else
|
||||
client._decoratedSlash.push({
|
||||
c._decoratedSlash.push({
|
||||
name: name ?? prop,
|
||||
guild,
|
||||
handler: item
|
||||
|
@ -416,12 +424,14 @@ export function slash(name?: string, guild?: string) {
|
|||
/** Decorator to create a Sub-Slash Command handler */
|
||||
export function subslash(parent: string, name?: string, guild?: string) {
|
||||
return function (client: Client | SlashModule | SlashClient, prop: string) {
|
||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const c = client as any
|
||||
if (c._decoratedSlash === undefined) c._decoratedSlash = []
|
||||
const item = (client as { [name: string]: any })[prop]
|
||||
if (typeof item !== 'function') {
|
||||
throw new Error('@subslash decorator requires a function')
|
||||
} else
|
||||
client._decoratedSlash.push({
|
||||
c._decoratedSlash.push({
|
||||
parent,
|
||||
name: name ?? prop,
|
||||
guild,
|
||||
|
@ -438,12 +448,14 @@ export function groupslash(
|
|||
guild?: string
|
||||
) {
|
||||
return function (client: Client | SlashModule | SlashClient, prop: string) {
|
||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const c = client as any
|
||||
if (c._decoratedSlash === undefined) c._decoratedSlash = []
|
||||
const item = (client as { [name: string]: any })[prop]
|
||||
if (typeof item !== 'function') {
|
||||
throw new Error('@groupslash decorator requires a function')
|
||||
} else
|
||||
client._decoratedSlash.push({
|
||||
c._decoratedSlash.push({
|
||||
group,
|
||||
parent,
|
||||
name: name ?? prop,
|
||||
|
|
19
src/managers/_util.ts
Normal file
19
src/managers/_util.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import {
|
||||
MessageComponentData,
|
||||
MessageComponentPayload
|
||||
} from '../types/messageComponents.ts'
|
||||
|
||||
export function transformComponent(
|
||||
d: MessageComponentData[]
|
||||
): MessageComponentPayload[] {
|
||||
return d.map((e: any) => {
|
||||
if (e.customID !== undefined) {
|
||||
e.custom_id = e.customID
|
||||
delete e.customID
|
||||
}
|
||||
if (e.components !== undefined) {
|
||||
e.components = transformComponent(e.components)
|
||||
}
|
||||
return e
|
||||
})
|
||||
}
|
|
@ -11,6 +11,7 @@ import type {
|
|||
import { CHANNEL } from '../types/endpoint.ts'
|
||||
import getChannelByType from '../utils/channel.ts'
|
||||
import { BaseManager } from './base.ts'
|
||||
import { transformComponent } from './_util.ts'
|
||||
|
||||
export type AllMessageOptions = MessageOptions | Embed
|
||||
|
||||
|
@ -100,7 +101,10 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> {
|
|||
content: content,
|
||||
embed: option?.embed,
|
||||
file: option?.file,
|
||||
components: option?.components,
|
||||
components:
|
||||
option?.components !== undefined
|
||||
? transformComponent(option.components)
|
||||
: undefined,
|
||||
files: option?.files,
|
||||
tts: option?.tts,
|
||||
allowed_mentions: option?.allowedMentions,
|
||||
|
@ -168,6 +172,10 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> {
|
|||
embed: option?.embed !== undefined ? option.embed.toJSON() : undefined,
|
||||
// Cannot upload new files with Message
|
||||
// file: option?.file,
|
||||
components:
|
||||
option?.components !== undefined
|
||||
? transformComponent(option.components)
|
||||
: undefined,
|
||||
tts: option?.tts,
|
||||
allowed_mentions: option?.allowedMentions
|
||||
})
|
||||
|
|
|
@ -13,7 +13,7 @@ export class DiscordAPIError extends Error {
|
|||
this.message =
|
||||
typeof error === 'string'
|
||||
? `${error} `
|
||||
: `\n${error.method.toUpperCase()} ${error.url.slice(7)} returned ${
|
||||
: `\n${error.method.toUpperCase()} ${error.url} returned ${
|
||||
error.status
|
||||
}\n(${error.code ?? 'unknown'}) ${error.message}${
|
||||
fmt.length === 0
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { Client } from '../client/client.ts'
|
||||
import { transformComponent } from '../managers/_util.ts'
|
||||
import {
|
||||
AllowedMentionsPayload,
|
||||
ChannelTypes,
|
||||
|
@ -13,11 +14,14 @@ import {
|
|||
InteractionResponseType,
|
||||
InteractionType
|
||||
} from '../types/interactions.ts'
|
||||
import {
|
||||
InteractionMessageComponentData,
|
||||
MessageComponentData
|
||||
} from '../types/messageComponents.ts'
|
||||
import {
|
||||
InteractionApplicationCommandData,
|
||||
InteractionChannelPayload
|
||||
} from '../types/slashCommands.ts'
|
||||
import { Dict } from '../utils/dict.ts'
|
||||
import { Permissions } from '../utils/permissions.ts'
|
||||
import { SnowflakeBase } from './base.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
|
@ -26,7 +30,6 @@ import { Guild } from './guild.ts'
|
|||
import { GuildTextChannel } from './guildTextChannel.ts'
|
||||
import { Member } from './member.ts'
|
||||
import { Message } from './message.ts'
|
||||
import { Role } from './role.ts'
|
||||
import { TextChannel } from './textChannel.ts'
|
||||
import { User } from './user.ts'
|
||||
|
||||
|
@ -47,6 +50,7 @@ export interface InteractionMessageOptions {
|
|||
allowedMentions?: AllowedMentionsPayload
|
||||
/** Whether the Message Response should be Ephemeral (only visible to User) or not */
|
||||
ephemeral?: boolean
|
||||
components?: MessageComponentData[]
|
||||
}
|
||||
|
||||
export interface InteractionResponse extends InteractionMessageOptions {
|
||||
|
@ -76,13 +80,6 @@ export class InteractionChannel extends SnowflakeBase {
|
|||
}
|
||||
}
|
||||
|
||||
export interface InteractionApplicationCommandResolved {
|
||||
users: Dict<InteractionUser>
|
||||
members: Dict<Member>
|
||||
channels: Dict<InteractionChannel>
|
||||
roles: Dict<Role>
|
||||
}
|
||||
|
||||
export class InteractionUser extends User {
|
||||
member?: Member
|
||||
}
|
||||
|
@ -110,7 +107,8 @@ export class Interaction extends SnowflakeBase {
|
|||
_httpResponded?: boolean
|
||||
applicationID: string
|
||||
/** Data sent with Interaction. Only applies to Application Command */
|
||||
data?: InteractionApplicationCommandData
|
||||
data?: InteractionApplicationCommandData | InteractionMessageComponentData
|
||||
message?: Message
|
||||
|
||||
constructor(
|
||||
client: Client,
|
||||
|
@ -120,6 +118,7 @@ export class Interaction extends SnowflakeBase {
|
|||
guild?: Guild
|
||||
member?: Member
|
||||
user: User
|
||||
message?: Message
|
||||
}
|
||||
) {
|
||||
super(client)
|
||||
|
@ -132,6 +131,7 @@ export class Interaction extends SnowflakeBase {
|
|||
this.data = data.data
|
||||
this.guild = others.guild
|
||||
this.channel = others.channel
|
||||
this.message = others.message
|
||||
}
|
||||
|
||||
/** Respond to an Interaction */
|
||||
|
@ -154,7 +154,11 @@ export class Interaction extends SnowflakeBase {
|
|||
embeds: data.embeds,
|
||||
tts: data.tts ?? false,
|
||||
flags,
|
||||
allowed_mentions: data.allowedMentions ?? undefined
|
||||
allowed_mentions: data.allowedMentions ?? undefined,
|
||||
components:
|
||||
data.components === undefined
|
||||
? undefined
|
||||
: transformComponent(data.components)
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
@ -227,6 +231,7 @@ export class Interaction extends SnowflakeBase {
|
|||
embeds?: Array<Embed | EmbedPayload>
|
||||
flags?: number | number[]
|
||||
allowedMentions?: AllowedMentionsPayload
|
||||
components?: MessageComponentData[]
|
||||
}): Promise<Interaction> {
|
||||
const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original')
|
||||
await this.client.rest.patch(url, {
|
||||
|
@ -236,7 +241,11 @@ export class Interaction extends SnowflakeBase {
|
|||
typeof data.flags === 'object'
|
||||
? data.flags.reduce((p, a) => p | a, 0)
|
||||
: data.flags,
|
||||
allowed_mentions: data.allowedMentions
|
||||
allowed_mentions: data.allowedMentions,
|
||||
components:
|
||||
data.components === undefined
|
||||
? undefined
|
||||
: transformComponent(data.components)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
|
42
src/structures/messageComponents.ts
Normal file
42
src/structures/messageComponents.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
InteractionMessageComponentData,
|
||||
MessageComponentType
|
||||
} from '../types/messageComponents.ts'
|
||||
import { Interaction } from './interactions.ts'
|
||||
import type { Client } from '../client/mod.ts'
|
||||
import { InteractionPayload } from '../types/interactions.ts'
|
||||
import type { Guild } from './guild.ts'
|
||||
import type { GuildTextChannel } from './guildTextChannel.ts'
|
||||
import type { Member } from './member.ts'
|
||||
import type { TextChannel } from './textChannel.ts'
|
||||
import { User } from './user.ts'
|
||||
import { Message } from './message.ts'
|
||||
|
||||
export class MessageComponentInteraction extends Interaction {
|
||||
data: InteractionMessageComponentData
|
||||
declare message: Message
|
||||
|
||||
constructor(
|
||||
client: Client,
|
||||
data: InteractionPayload,
|
||||
others: {
|
||||
channel?: TextChannel | GuildTextChannel
|
||||
guild?: Guild
|
||||
member?: Member
|
||||
user: User
|
||||
message?: Message
|
||||
}
|
||||
) {
|
||||
super(client, data, others)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
this.data = data.data as InteractionMessageComponentData
|
||||
}
|
||||
|
||||
get customID(): string {
|
||||
return this.data.custom_id
|
||||
}
|
||||
|
||||
get componentType(): MessageComponentType {
|
||||
return this.data.component_type
|
||||
}
|
||||
}
|
|
@ -7,7 +7,10 @@ import type { EmojiPayload } from './emoji.ts'
|
|||
import type { MemberPayload } from './guild.ts'
|
||||
import type { InteractionType } from './interactions.ts'
|
||||
import type { UserPayload } from './user.ts'
|
||||
import type { MessageComponentPayload } from './messageComponents.ts'
|
||||
import type {
|
||||
MessageComponentData,
|
||||
MessageComponentPayload
|
||||
} from './messageComponents.ts'
|
||||
|
||||
export interface ChannelPayload {
|
||||
id: string
|
||||
|
@ -189,6 +192,7 @@ export interface MessagePayload {
|
|||
flags?: number
|
||||
stickers?: MessageStickerPayload[]
|
||||
interaction?: MessageInteractionPayload
|
||||
components?: MessageComponentPayload[]
|
||||
}
|
||||
|
||||
export enum AllowedMentionType {
|
||||
|
@ -211,7 +215,7 @@ export interface MessageOptions {
|
|||
files?: MessageAttachment[]
|
||||
allowedMentions?: AllowedMentionsPayload
|
||||
reply?: Message | MessageReference | string
|
||||
components?: MessageComponentPayload[]
|
||||
components?: MessageComponentData[]
|
||||
}
|
||||
|
||||
export interface ChannelMention {
|
||||
|
@ -395,6 +399,7 @@ export interface EditMessagePayload {
|
|||
embed?: EmbedPayload
|
||||
allowed_mentions?: AllowedMentionsPayload
|
||||
flags?: number
|
||||
components?: MessageComponentPayload[]
|
||||
}
|
||||
|
||||
export interface CreateMessagePayload extends EditMessagePayload {
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
import { AllowedMentionsPayload, EmbedPayload } from './channel.ts'
|
||||
import {
|
||||
AllowedMentionsPayload,
|
||||
EmbedPayload,
|
||||
MessagePayload
|
||||
} from './channel.ts'
|
||||
import type { MemberPayload } from './guild.ts'
|
||||
import {
|
||||
InteractionMessageComponentData,
|
||||
MessageComponentData
|
||||
} from './messageComponents.ts'
|
||||
import type { InteractionApplicationCommandData } from './slashCommands.ts'
|
||||
import type { UserPayload } from './user.ts'
|
||||
|
||||
|
@ -7,7 +15,9 @@ export enum InteractionType {
|
|||
/** Ping sent by the API (HTTP-only) */
|
||||
PING = 1,
|
||||
/** Slash Command Interaction */
|
||||
APPLICATION_COMMAND = 2
|
||||
APPLICATION_COMMAND = 2,
|
||||
/** Message Component Interaction */
|
||||
MESSAGE_COMPONENT = 3
|
||||
}
|
||||
|
||||
export interface InteractionMemberPayload extends MemberPayload {
|
||||
|
@ -27,14 +37,17 @@ export interface InteractionPayload {
|
|||
/** ID of the Interaction */
|
||||
id: string
|
||||
/**
|
||||
* Data sent with the interaction. Undefined only when Interaction is not Slash Command.*
|
||||
* Data sent with the interaction. Undefined only when Interaction is PING (http-only).*
|
||||
*/
|
||||
data?: InteractionApplicationCommandData
|
||||
data?: InteractionApplicationCommandData | InteractionMessageComponentData
|
||||
/** ID of the Guild in which Interaction was invoked */
|
||||
guild_id?: string
|
||||
/** ID of the Channel in which Interaction was invoked */
|
||||
channel_id?: string
|
||||
/** Application ID of the Client who received interaction */
|
||||
application_id: string
|
||||
/** Message ID if the Interaction was of type MESSAGE_COMPONENT */
|
||||
message?: MessagePayload
|
||||
}
|
||||
|
||||
export enum InteractionResponseType {
|
||||
|
@ -62,6 +75,7 @@ export interface InteractionResponseDataPayload {
|
|||
/** Allowed Mentions object */
|
||||
allowed_mentions?: AllowedMentionsPayload
|
||||
flags?: number
|
||||
components?: MessageComponentData[]
|
||||
}
|
||||
|
||||
export enum InteractionResponseFlags {
|
||||
|
|
|
@ -17,4 +17,19 @@ export interface MessageComponentPayload {
|
|||
label?: string
|
||||
style?: ButtonStyle
|
||||
url?: string
|
||||
custom_id?: string
|
||||
}
|
||||
|
||||
export interface MessageComponentData {
|
||||
type: MessageComponentType
|
||||
components?: MessageComponentData[]
|
||||
label?: string
|
||||
style?: ButtonStyle
|
||||
url?: string
|
||||
customID?: string
|
||||
}
|
||||
|
||||
export interface InteractionMessageComponentData {
|
||||
custom_id: string
|
||||
component_type: MessageComponentType
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export function simplifyAPIError(errors: any): SimplifiedError {
|
|||
const arrayIndex = !isNaN(Number(obj[0]))
|
||||
if (arrayIndex) obj[0] = `[${obj[0]}]`
|
||||
if (acum !== '' && !arrayIndex) acum += '.'
|
||||
fmt(obj[1], (acum += obj[0]))
|
||||
fmt(obj[1], acum + obj[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
16
src/utils/interactions.ts
Normal file
16
src/utils/interactions.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { InteractionType } from '../../mod.ts'
|
||||
import { Interaction } from '../structures/interactions.ts'
|
||||
import { MessageComponentInteraction } from '../structures/messageComponents.ts'
|
||||
import { SlashCommandInteraction } from '../structures/slash.ts'
|
||||
|
||||
export function isSlashCommandInteraction(
|
||||
d: Interaction
|
||||
): d is SlashCommandInteraction {
|
||||
return d.type === InteractionType.APPLICATION_COMMAND
|
||||
}
|
||||
|
||||
export function isMessageComponentInteraction(
|
||||
d: Interaction
|
||||
): d is MessageComponentInteraction {
|
||||
return d.type === InteractionType.MESSAGE_COMPONENT
|
||||
}
|
184
test/components.ts
Normal file
184
test/components.ts
Normal file
|
@ -0,0 +1,184 @@
|
|||
import {
|
||||
CommandClient,
|
||||
Command,
|
||||
CommandContext,
|
||||
ButtonStyle,
|
||||
MessageComponentType,
|
||||
isMessageComponentInteraction,
|
||||
MessageComponentInteraction,
|
||||
Message
|
||||
} from '../mod.ts'
|
||||
import { TOKEN } from './config.ts'
|
||||
|
||||
const client = new CommandClient({
|
||||
prefix: '.',
|
||||
spacesAfterPrefix: true
|
||||
})
|
||||
|
||||
enum Choice {
|
||||
Rock,
|
||||
Paper,
|
||||
Scissor
|
||||
}
|
||||
|
||||
const games = new Map<
|
||||
string,
|
||||
{ user: number; bot: number; msg: Message; txt: string }
|
||||
>()
|
||||
const components = [
|
||||
{
|
||||
type: MessageComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
style: ButtonStyle.Primary,
|
||||
label: 'Rock',
|
||||
customID: 'rps::Rock'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
style: ButtonStyle.Primary,
|
||||
label: 'Paper',
|
||||
customID: 'rps::Paper'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
style: ButtonStyle.Primary,
|
||||
label: 'Scissor',
|
||||
customID: 'rps::Scissor'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
client.once('ready', () => {
|
||||
console.log('Ready!')
|
||||
})
|
||||
|
||||
client.commands.add(
|
||||
class extends Command {
|
||||
name = 'button'
|
||||
|
||||
execute(ctx: CommandContext): void {
|
||||
ctx.channel.send('Test Buttons', {
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
label: 'Primary',
|
||||
style: ButtonStyle.Primary,
|
||||
customID: '1'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
label: 'Secondary',
|
||||
style: ButtonStyle.Secondary,
|
||||
customID: '2'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
label: 'Destructive',
|
||||
style: ButtonStyle.Destructive,
|
||||
customID: '3'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
label: 'Success',
|
||||
style: ButtonStyle.Success,
|
||||
customID: '4'
|
||||
},
|
||||
{
|
||||
type: MessageComponentType.Button,
|
||||
label: 'Link',
|
||||
style: ButtonStyle.Link,
|
||||
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
client.commands.add(
|
||||
class extends Command {
|
||||
name = 'play'
|
||||
|
||||
execute(ctx: CommandContext): any {
|
||||
if (games.has(ctx.author.id))
|
||||
return ctx.message.reply('You are already playing!')
|
||||
ctx.channel
|
||||
.send('Game starts now!', {
|
||||
components
|
||||
})
|
||||
.then((msg) => {
|
||||
games.set(ctx.author.id, {
|
||||
user: 0,
|
||||
bot: 0,
|
||||
msg,
|
||||
txt: 'Game starts now!'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// client.on('raw', (e, d) => {
|
||||
// if (e === 'INTERACTION_CREATE') console.log(e, d)
|
||||
// })
|
||||
|
||||
client.on('interactionCreate', (i) => {
|
||||
if (isMessageComponentInteraction(i) === true) {
|
||||
const d = i as MessageComponentInteraction
|
||||
|
||||
if (d.customID.startsWith('rps::') === true) {
|
||||
const game = games.get(d.user.id)
|
||||
if (game === undefined) return
|
||||
const choice = d.customID.split('::')[1]
|
||||
const c: number = Number(Choice[choice as any])
|
||||
const rand = Math.floor(Math.random() * 2)
|
||||
|
||||
game.txt += '\n\n'
|
||||
game.txt += `You: ${choice}, Bot: ${Choice[rand]}`
|
||||
let msg
|
||||
if (rand === c) {
|
||||
msg = 'Both chose ' + Choice[rand] + '!'
|
||||
} else if (
|
||||
(rand === 0 && c === 2) ||
|
||||
(rand === 1 && c === 0) ||
|
||||
(rand === 2 && c === 1)
|
||||
) {
|
||||
msg = 'Bot got one point!'
|
||||
game.bot++
|
||||
} else {
|
||||
msg = 'You got one point!'
|
||||
game.user++
|
||||
}
|
||||
game.txt += '\nInfo: ' + msg
|
||||
|
||||
if (game.bot === 5 || game.user === 5) {
|
||||
const won = game.bot === 5 ? 'Bot' : 'You'
|
||||
game.msg.edit(
|
||||
`${won} won!\n\n**Points:** You: ${game.user} | Bot: ${game.bot}`,
|
||||
{
|
||||
components: []
|
||||
}
|
||||
)
|
||||
games.delete(d.user.id)
|
||||
} else {
|
||||
game.msg.edit(
|
||||
`${game.txt}\n\n**Points:** You: ${game.user} | Bot: ${game.bot}`,
|
||||
{
|
||||
components
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.log('Connecting...')
|
||||
client.connect(TOKEN, ['GUILDS', 'GUILD_MESSAGES', 'DIRECT_MESSAGES'])
|
|
@ -8,12 +8,12 @@ import {
|
|||
CommandContext,
|
||||
Extension,
|
||||
Collection,
|
||||
GuildTextChannel
|
||||
} from '../../mod.ts'
|
||||
GuildTextChannel,
|
||||
slash,
|
||||
SlashCommandInteraction
|
||||
} from '../mod.ts'
|
||||
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
||||
import { Manager, Player } from 'https://deno.land/x/lavadeno/mod.ts'
|
||||
import { Interaction } from '../structures/slash.ts'
|
||||
import { slash } from '../client/mod.ts'
|
||||
// import { SlashCommandOptionType } from '../types/slash.ts'
|
||||
|
||||
export const nodes = [
|
||||
|
@ -58,12 +58,12 @@ class MyClient extends CommandClient {
|
|||
}
|
||||
|
||||
@subslash('cmd', 'sub-cmd-no-grp')
|
||||
subCmdNoGroup(d: Interaction): void {
|
||||
subCmdNoGroup(d: SlashCommandInteraction): void {
|
||||
d.respond({ content: 'sub-cmd-no-group worked' })
|
||||
}
|
||||
|
||||
@groupslash('cmd', 'sub-cmd-group', 'sub-cmd')
|
||||
subCmdGroup(d: Interaction): void {
|
||||
subCmdGroup(d: SlashCommandInteraction): void {
|
||||
d.respond({ content: 'sub-cmd-group worked' })
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ class MyClient extends CommandClient {
|
|||
}
|
||||
|
||||
@slash()
|
||||
run(d: Interaction): void {
|
||||
run(d: SlashCommandInteraction): void {
|
||||
console.log(d.name)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue