message component interactions

This commit is contained in:
DjDeveloperr 2021-04-23 11:07:55 +05:30
parent 17e74dce45
commit b97ec3c225
18 changed files with 461 additions and 86 deletions

4
mod.ts
View File

@ -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'

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
})
}

View File

@ -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

View File

@ -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
View 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
})
}

View File

@ -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
})

View File

@ -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

View File

@ -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
}

View 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
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}

View File

@ -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
View 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
View 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'])

View File

@ -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)
}