wtf
This commit is contained in:
commit
2ca5005517
12 changed files with 429 additions and 217 deletions
1
mod.ts
1
mod.ts
|
@ -136,3 +136,4 @@ export { UserFlags } from './src/types/userFlags.ts'
|
|||
export type { VoiceStatePayload } from './src/types/voice.ts'
|
||||
export type { WebhookPayload } from './src/types/webhook.ts'
|
||||
export * from './src/models/collectors.ts'
|
||||
export type { Dict } from './src/utils/dict.ts'
|
||||
|
|
|
@ -1,29 +1,110 @@
|
|||
import { Guild } from '../../structures/guild.ts'
|
||||
import { Member } from '../../structures/member.ts'
|
||||
import { Interaction } from '../../structures/slash.ts'
|
||||
import { Role } from '../../structures/role.ts'
|
||||
import {
|
||||
Interaction,
|
||||
InteractionApplicationCommandResolved,
|
||||
InteractionChannel
|
||||
} from '../../structures/slash.ts'
|
||||
import { GuildTextChannel } from '../../structures/textChannel.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { InteractionPayload } from '../../types/slash.ts'
|
||||
import { UserPayload } from '../../types/user.ts'
|
||||
import { Permissions } from '../../utils/permissions.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
export const interactionCreate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: InteractionPayload
|
||||
) => {
|
||||
const guild = await gateway.client.guilds.get(d.guild_id)
|
||||
if (guild === undefined) return
|
||||
// NOTE(DjDeveloperr): Mason once mentioned that channel_id can be optional in Interaction.
|
||||
// This case can be seen in future proofing Interactions, and one he mentioned was
|
||||
// that bots will be able to add custom context menus. In that case, Interaction will not have it.
|
||||
// Ref: https://github.com/discord/discord-api-docs/pull/2568/files#r569025697
|
||||
if (d.channel_id === undefined) return
|
||||
|
||||
await guild.members.set(d.member.user.id, d.member)
|
||||
const member = ((await guild.members.get(
|
||||
d.member.user.id
|
||||
)) as unknown) as Member
|
||||
const guild =
|
||||
d.guild_id === undefined
|
||||
? undefined
|
||||
: await gateway.client.guilds.get(d.guild_id)
|
||||
|
||||
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)
|
||||
: undefined
|
||||
if (d.user !== undefined) await gateway.client.users.set(d.user.id, d.user)
|
||||
const dmUser =
|
||||
d.user !== undefined ? await gateway.client.users.get(d.user.id) : undefined
|
||||
|
||||
const user = member !== undefined ? member.user : dmUser
|
||||
if (user === undefined) return
|
||||
|
||||
const channel =
|
||||
(await gateway.client.channels.get<GuildTextChannel>(d.channel_id)) ??
|
||||
(await gateway.client.channels.fetch<GuildTextChannel>(d.channel_id))
|
||||
|
||||
const resolved: InteractionApplicationCommandResolved = {
|
||||
users: {},
|
||||
channels: {},
|
||||
members: {},
|
||||
roles: {}
|
||||
}
|
||||
|
||||
if (d.data?.resolved !== undefined) {
|
||||
for (const [id, data] of Object.entries(d.data.resolved.users ?? {})) {
|
||||
await gateway.client.users.set(id, data)
|
||||
resolved.users[id] = ((await gateway.client.users.get(
|
||||
id
|
||||
)) as unknown) as User
|
||||
if (resolved.members[id] !== undefined)
|
||||
resolved.users[id].member = resolved.members[id]
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.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
|
||||
)
|
||||
permissions = new Permissions(mRoles.map((r) => r.permissions))
|
||||
}
|
||||
data.user = (d.data.resolved.users?.[id] as unknown) as UserPayload
|
||||
resolved.members[id] = new Member(
|
||||
gateway.client,
|
||||
data,
|
||||
resolved.users[id],
|
||||
guild as Guild,
|
||||
permissions
|
||||
)
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.resolved.roles ?? {})) {
|
||||
if (guild !== undefined) {
|
||||
await guild.roles.set(id, data)
|
||||
resolved.roles[id] = ((await guild.roles.get(id)) as unknown) as Role
|
||||
} else {
|
||||
resolved.roles[id] = new Role(
|
||||
gateway.client,
|
||||
data,
|
||||
(guild as unknown) as Guild
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [id, data] of Object.entries(d.data.resolved.channels ?? {})) {
|
||||
resolved.channels[id] = new InteractionChannel(gateway.client, data)
|
||||
}
|
||||
}
|
||||
|
||||
const interaction = new Interaction(gateway.client, d, {
|
||||
member,
|
||||
guild,
|
||||
channel
|
||||
channel,
|
||||
user,
|
||||
resolved
|
||||
})
|
||||
gateway.client.emit('interactionCreate', interaction)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
import { GatewayResponse } from '../types/gatewayResponse.ts'
|
||||
import {
|
||||
GatewayOpcodes,
|
||||
GatewayIntents,
|
||||
GatewayCloseCodes,
|
||||
IdentityPayload,
|
||||
StatusUpdatePayload,
|
||||
|
@ -57,8 +56,6 @@ export type GatewayTypedEvents = {
|
|||
*/
|
||||
export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
||||
websocket?: WebSocket
|
||||
token?: string
|
||||
intents?: GatewayIntents[]
|
||||
connected = false
|
||||
initialized = false
|
||||
heartbeatInterval = 0
|
||||
|
@ -157,7 +154,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
|
||||
const handler = gatewayHandlers[t]
|
||||
|
||||
if (handler !== undefined) {
|
||||
if (handler !== undefined && d !== null) {
|
||||
handler(this, d)
|
||||
}
|
||||
}
|
||||
|
@ -269,8 +266,9 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
}
|
||||
|
||||
private async sendIdentify(forceNewSession?: boolean): Promise<void> {
|
||||
if (typeof this.token !== 'string') throw new Error('Token not specified')
|
||||
if (typeof this.intents !== 'object')
|
||||
if (typeof this.client.token !== 'string')
|
||||
throw new Error('Token not specified')
|
||||
if (typeof this.client.intents !== 'object')
|
||||
throw new Error('Intents not specified')
|
||||
|
||||
if (this.client.fetchGatewayInfo === true) {
|
||||
|
@ -300,7 +298,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
}
|
||||
|
||||
const payload: IdentityPayload = {
|
||||
token: this.token,
|
||||
token: this.client.token,
|
||||
properties: {
|
||||
$os: this.client.clientProperties.os ?? Deno.build.os,
|
||||
$browser: this.client.clientProperties.browser ?? 'harmony',
|
||||
|
@ -311,7 +309,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
this.shards === undefined
|
||||
? [0, 1]
|
||||
: [this.shards[0] ?? 0, this.shards[1] ?? 1],
|
||||
intents: this.intents.reduce(
|
||||
intents: this.client.intents.reduce(
|
||||
(previous, current) => previous | current,
|
||||
0
|
||||
),
|
||||
|
@ -327,9 +325,8 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
}
|
||||
|
||||
private async sendResume(): Promise<void> {
|
||||
if (typeof this.token !== 'string') throw new Error('Token not specified')
|
||||
if (typeof this.intents !== 'object')
|
||||
throw new Error('Intents not specified')
|
||||
if (typeof this.client.token !== 'string')
|
||||
throw new Error('Token not specified')
|
||||
|
||||
if (this.sessionID === undefined) {
|
||||
this.sessionID = await this.cache.get(
|
||||
|
@ -348,7 +345,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
|
|||
const resumePayload = {
|
||||
op: GatewayOpcodes.RESUME,
|
||||
d: {
|
||||
token: this.token,
|
||||
token: this.client.token,
|
||||
session_id: this.sessionID,
|
||||
seq: this.sequenceID ?? null
|
||||
}
|
||||
|
|
|
@ -79,8 +79,6 @@ export class ShardManager extends HarmonyEventEmitter<ShardManagerEvents> {
|
|||
const shardCount = await this.getShardCount()
|
||||
|
||||
const gw = new Gateway(this.client, [Number(id), shardCount])
|
||||
gw.token = this.client.token
|
||||
gw.intents = this.client.intents
|
||||
this.list.set(id.toString(), gw)
|
||||
|
||||
gw.initWebsocket()
|
||||
|
|
|
@ -155,6 +155,7 @@ function buildOptionsArray(
|
|||
)
|
||||
}
|
||||
|
||||
/** Slash Command Builder */
|
||||
export class SlashBuilder {
|
||||
data: SlashCommandPartial
|
||||
|
||||
|
@ -200,6 +201,7 @@ export class SlashBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/** Manages Slash Commands, allows fetching/modifying/deleting/creating Slash Commands. */
|
||||
export class SlashCommandsManager {
|
||||
slash: SlashClient
|
||||
rest: RESTManager
|
||||
|
@ -351,7 +353,7 @@ export class SlashCommandsManager {
|
|||
}
|
||||
}
|
||||
|
||||
export type SlashCommandHandlerCallback = (interaction: Interaction) => any
|
||||
export type SlashCommandHandlerCallback = (interaction: Interaction) => unknown
|
||||
export interface SlashCommandHandler {
|
||||
name: string
|
||||
guild?: string
|
||||
|
@ -360,6 +362,7 @@ export interface SlashCommandHandler {
|
|||
handler: SlashCommandHandlerCallback
|
||||
}
|
||||
|
||||
/** Options for SlashClient */
|
||||
export interface SlashOptions {
|
||||
id?: string | (() => string)
|
||||
client?: Client
|
||||
|
@ -369,9 +372,7 @@ export interface SlashOptions {
|
|||
publicKey?: string
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
|
||||
/** Slash Client represents an Interactions Client which can be used without Harmony Client. */
|
||||
export class SlashClient {
|
||||
id: string | (() => string)
|
||||
client?: Client
|
||||
|
@ -472,12 +473,20 @@ export class SlashClient {
|
|||
const groupMatched =
|
||||
e.group !== undefined && e.parent !== undefined
|
||||
? i.options
|
||||
.find((o) => o.name === e.group)
|
||||
.find(
|
||||
(o) =>
|
||||
o.name === e.group &&
|
||||
o.type === SlashCommandOptionType.SUB_COMMAND_GROUP
|
||||
)
|
||||
?.options?.find((o) => o.name === e.name) !== undefined
|
||||
: true
|
||||
const subMatched =
|
||||
e.group === undefined && e.parent !== undefined
|
||||
? i.options.find((o) => o.name === e.name) !== undefined
|
||||
? i.options.find(
|
||||
(o) =>
|
||||
o.name === e.name &&
|
||||
o.type === SlashCommandOptionType.SUB_COMMAND
|
||||
) !== undefined
|
||||
: true
|
||||
const nameMatched1 = e.name === i.name
|
||||
const parentMatched = hasGroupOrParent ? e.parent === i.name : true
|
||||
|
@ -488,11 +497,15 @@ export class SlashClient {
|
|||
})
|
||||
}
|
||||
|
||||
/** Process an incoming Slash Command (interaction) */
|
||||
/** Process an incoming Interaction */
|
||||
private _process(interaction: Interaction): void {
|
||||
if (!this.enabled) return
|
||||
|
||||
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
|
||||
if (
|
||||
interaction.type !== InteractionType.APPLICATION_COMMAND ||
|
||||
interaction.data === undefined
|
||||
)
|
||||
return
|
||||
|
||||
const cmd = this._getCommand(interaction)
|
||||
if (cmd?.group !== undefined)
|
||||
|
|
|
@ -2,10 +2,10 @@ import { Client } from '../models/client.ts'
|
|||
import { Snowflake } from '../utils/snowflake.ts'
|
||||
|
||||
export class Base {
|
||||
client: Client
|
||||
client!: Client
|
||||
|
||||
constructor(client: Client, _data?: any) {
|
||||
this.client = client
|
||||
Object.defineProperty(this, 'client', { value: client, enumerable: false })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
import { Client } from '../models/client.ts'
|
||||
import { MessageOptions } from '../types/channel.ts'
|
||||
import {
|
||||
AllowedMentionsPayload,
|
||||
ChannelTypes,
|
||||
EmbedPayload,
|
||||
MessageOptions
|
||||
} from '../types/channel.ts'
|
||||
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
||||
import {
|
||||
InteractionData,
|
||||
InteractionOption,
|
||||
InteractionApplicationCommandData,
|
||||
InteractionApplicationCommandOption,
|
||||
InteractionChannelPayload,
|
||||
InteractionPayload,
|
||||
InteractionResponseFlags,
|
||||
InteractionResponsePayload,
|
||||
InteractionResponseType
|
||||
InteractionResponseType,
|
||||
InteractionType,
|
||||
SlashCommandOptionType
|
||||
} from '../types/slash.ts'
|
||||
import { Dict } from '../utils/dict.ts'
|
||||
import { Permissions } from '../utils/permissions.ts'
|
||||
import { SnowflakeBase } from './base.ts'
|
||||
import { Channel } from './channel.ts'
|
||||
import { Embed } from './embed.ts'
|
||||
import { Guild } from './guild.ts'
|
||||
import { Member } from './member.ts'
|
||||
import { Message } from './message.ts'
|
||||
import { Role } from './role.ts'
|
||||
import { GuildTextChannel, TextChannel } from './textChannel.ts'
|
||||
import { User } from './user.ts'
|
||||
import { Webhook } from './webhook.ts'
|
||||
|
||||
interface WebhookMessageOptions extends MessageOptions {
|
||||
embeds?: Embed[]
|
||||
|
@ -25,72 +37,133 @@ interface WebhookMessageOptions extends MessageOptions {
|
|||
|
||||
type AllWebhookMessageOptions = string | WebhookMessageOptions
|
||||
|
||||
export interface InteractionResponse {
|
||||
type?: InteractionResponseType
|
||||
/** Interaction Message related Options */
|
||||
export interface InteractionMessageOptions {
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
embeds?: EmbedPayload[]
|
||||
tts?: boolean
|
||||
flags?: number
|
||||
temp?: boolean
|
||||
allowedMentions?: {
|
||||
parse?: string
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
everyone?: boolean
|
||||
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
|
||||
}
|
||||
|
||||
/** Represents a Channel Object for an Option in Slash Command */
|
||||
export class InteractionChannel extends SnowflakeBase {
|
||||
/** Name of the Channel */
|
||||
name: string
|
||||
/** Channel Type */
|
||||
type: ChannelTypes
|
||||
permissions: Permissions
|
||||
|
||||
constructor(client: Client, data: InteractionChannelPayload) {
|
||||
super(client)
|
||||
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 {
|
||||
/** This will be `SlashClient` in case of `SlashClient#verifyServerRequest` */
|
||||
client: Client
|
||||
type: number
|
||||
/** Type of Interaction */
|
||||
type: InteractionType
|
||||
/** Interaction Token */
|
||||
token: string
|
||||
/** Interaction ID */
|
||||
id: string
|
||||
data: InteractionData
|
||||
channel: GuildTextChannel
|
||||
guild: Guild
|
||||
member: Member
|
||||
_savedHook?: Webhook
|
||||
_respond?: (data: InteractionResponsePayload) => unknown
|
||||
/** Data sent with Interaction. Only applies to Application Command */
|
||||
data?: InteractionApplicationCommandData
|
||||
/** Channel in which Interaction was initiated */
|
||||
channel?: TextChannel | GuildTextChannel
|
||||
/** Guild in which Interaction was initiated */
|
||||
guild?: Guild
|
||||
/** Member object of who initiated the Interaction */
|
||||
member?: Member
|
||||
/** User object of who invoked Interaction */
|
||||
user: User
|
||||
/** Whether we have responded to Interaction or not */
|
||||
responded: boolean = false
|
||||
/** Resolved data for Snowflakes in Slash Command Arguments */
|
||||
resolved: InteractionApplicationCommandResolved
|
||||
/** Whether response was deferred or not */
|
||||
deferred: boolean = false
|
||||
|
||||
constructor(
|
||||
client: Client,
|
||||
data: InteractionPayload,
|
||||
others: {
|
||||
channel: GuildTextChannel
|
||||
guild: Guild
|
||||
member: Member
|
||||
channel?: TextChannel | GuildTextChannel
|
||||
guild?: Guild
|
||||
member?: Member
|
||||
user: User
|
||||
resolved: InteractionApplicationCommandResolved
|
||||
}
|
||||
) {
|
||||
super(client)
|
||||
this.client = client
|
||||
this.type = data.type
|
||||
this.token = data.token
|
||||
this.member = others.member
|
||||
this.id = data.id
|
||||
this.user = others.user
|
||||
this.data = data.data
|
||||
this.guild = others.guild
|
||||
this.channel = others.channel
|
||||
this.resolved = others.resolved
|
||||
}
|
||||
|
||||
get user(): User {
|
||||
return this.member.user
|
||||
/** Name of the Command Used (may change with future additions to Interactions!) */
|
||||
get name(): string | undefined {
|
||||
return this.data?.name
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this.data.name
|
||||
get options(): InteractionApplicationCommandOption[] {
|
||||
return this.data?.options ?? []
|
||||
}
|
||||
|
||||
get options(): InteractionOption[] {
|
||||
return this.data.options ?? []
|
||||
}
|
||||
|
||||
option<T = any>(name: string): T {
|
||||
return this.options.find((e) => e.name === name)?.value
|
||||
/** Get an option by name */
|
||||
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)
|
||||
return this.resolved.users[op.value] as any
|
||||
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
|
||||
}
|
||||
|
||||
/** Respond to an Interaction */
|
||||
async respond(data: InteractionResponse): Promise<Interaction> {
|
||||
if (this.responded) throw new Error('Already responded to Interaction')
|
||||
let flags = 0
|
||||
if (data.ephemeral === true) flags |= InteractionResponseFlags.EPHEMERAL
|
||||
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
|
||||
}
|
||||
const payload: InteractionResponsePayload = {
|
||||
type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data:
|
||||
|
@ -101,8 +174,8 @@ export class Interaction extends SnowflakeBase {
|
|||
content: data.content ?? '',
|
||||
embeds: data.embeds,
|
||||
tts: data.tts ?? false,
|
||||
flags: data.temp === true ? 64 : data.flags ?? undefined,
|
||||
allowed_mentions: (data.allowedMentions ?? undefined) as any
|
||||
flags,
|
||||
allowed_mentions: data.allowedMentions ?? undefined
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
@ -111,6 +184,55 @@ export class Interaction extends SnowflakeBase {
|
|||
INTERACTION_CALLBACK(this.id, this.token),
|
||||
payload
|
||||
)
|
||||
this.responded = true
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/** 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> {
|
||||
await this.respond({
|
||||
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE
|
||||
})
|
||||
this.deferred = true
|
||||
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 })
|
||||
|
||||
if (this.deferred && this.responded) {
|
||||
await this.editResponse({
|
||||
content: options.content,
|
||||
embeds: options.embeds,
|
||||
flags: options.flags,
|
||||
allowedMentions: options.allowedMentions
|
||||
})
|
||||
} else
|
||||
await this.respond(
|
||||
Object.assign(options, {
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE
|
||||
})
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
@ -118,7 +240,9 @@ export class Interaction extends SnowflakeBase {
|
|||
/** Edit the original Interaction response */
|
||||
async editResponse(data: {
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
embeds?: EmbedPayload[]
|
||||
flags?: number | number[]
|
||||
allowedMentions?: AllowedMentionsPayload
|
||||
}): Promise<Interaction> {
|
||||
const url = WEBHOOK_MESSAGE(
|
||||
this.client.user?.id as string,
|
||||
|
@ -127,7 +251,12 @@ export class Interaction extends SnowflakeBase {
|
|||
)
|
||||
await this.client.rest.patch(url, {
|
||||
content: data.content ?? '',
|
||||
embeds: data.embeds ?? []
|
||||
embeds: data.embeds ?? [],
|
||||
flags:
|
||||
typeof data.flags === 'object'
|
||||
? data.flags.reduce((p, a) => p | a, 0)
|
||||
: data.flags,
|
||||
allowed_mentions: data.allowedMentions
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
@ -234,6 +363,7 @@ export class Interaction extends SnowflakeBase {
|
|||
return this
|
||||
}
|
||||
|
||||
/** Delete a follow-up Message */
|
||||
async deleteMessage(msg: Message | string): Promise<Interaction> {
|
||||
await this.client.rest.delete(
|
||||
WEBHOOK_MESSAGE(
|
||||
|
|
|
@ -201,7 +201,7 @@ client.on('messageCreate', async (msg: Message) => {
|
|||
)
|
||||
.join('\n\n')}`
|
||||
)
|
||||
} else if (msg.content === '!getPermissions') {
|
||||
} else if (msg.content === '!perms') {
|
||||
if (msg.channel.type !== ChannelTypes.GUILD_TEXT) {
|
||||
return msg.channel.send("This isn't a guild text channel!")
|
||||
}
|
||||
|
@ -210,30 +210,11 @@ client.on('messageCreate', async (msg: Message) => {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
msg.member as Member
|
||||
)
|
||||
msg.channel.send(`Your permissions:\n${permissions.toArray().join('\n')}`)
|
||||
} else if (msg.content === '!addAllRoles') {
|
||||
const roles = await msg.guild?.roles.array()
|
||||
if (roles !== undefined) {
|
||||
roles.forEach(async (role) => {
|
||||
await msg.member?.roles.add(role)
|
||||
console.log(role)
|
||||
})
|
||||
}
|
||||
} else if (msg.content === '!createAndAddRole') {
|
||||
if (msg.guild !== undefined) {
|
||||
const role = await msg.guild.roles.create({
|
||||
name: 'asdf',
|
||||
permissions: 0
|
||||
})
|
||||
await msg.member?.roles.add(role)
|
||||
}
|
||||
} else if (msg.content === '!roles') {
|
||||
let buf = 'Roles:'
|
||||
if (msg.member === undefined) return
|
||||
for await (const role of msg.member.roles) {
|
||||
buf += `\n${role.name}`
|
||||
}
|
||||
msg.reply(buf)
|
||||
msg.channel.send(
|
||||
Object.entries(permissions.serialize())
|
||||
.map((e) => `${e[0]}: ${e[1] === true ? '`✅`' : '`❌`'}`)
|
||||
.join('\n')
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,100 +1,56 @@
|
|||
import { Client, Intents, event, slash } from '../../mod.ts'
|
||||
import { Embed } from '../structures/embed.ts'
|
||||
import {
|
||||
Client,
|
||||
Intents,
|
||||
event,
|
||||
slash,
|
||||
SlashCommandOptionType as Type
|
||||
} from '../../mod.ts'
|
||||
import { Interaction } from '../structures/slash.ts'
|
||||
import { TOKEN } from './config.ts'
|
||||
|
||||
export class MyClient extends Client {
|
||||
@event()
|
||||
ready(): void {
|
||||
@event() ready(): void {
|
||||
console.log(`Logged in as ${this.user?.tag}!`)
|
||||
this.slash.commands.bulkEdit([{ name: 'send', description: 'idk' }])
|
||||
}
|
||||
|
||||
@event('debug')
|
||||
debugEvt(txt: string): void {
|
||||
console.log(txt)
|
||||
}
|
||||
|
||||
@slash()
|
||||
send(d: Interaction): void {
|
||||
d.respond({
|
||||
content: d.data.options?.find((e) => e.name === 'content')?.value
|
||||
})
|
||||
}
|
||||
|
||||
@slash()
|
||||
async eval(d: Interaction): Promise<void> {
|
||||
if (
|
||||
d.user.id !== '422957901716652033' &&
|
||||
d.user.id !== '682849186227552266'
|
||||
) {
|
||||
d.respond({
|
||||
content: 'This command can only be used by owner!'
|
||||
})
|
||||
} else {
|
||||
const code = d.data.options?.find((e) => e.name === 'code')
|
||||
?.value as string
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
let evaled = eval(code)
|
||||
if (evaled instanceof Promise) evaled = await evaled
|
||||
if (typeof evaled === 'object') evaled = Deno.inspect(evaled)
|
||||
let res = `${evaled}`.substring(0, 1990)
|
||||
while (client.token !== undefined && res.includes(client.token)) {
|
||||
res = res.replace(client.token, '[REMOVED]')
|
||||
this.slash.commands.bulkEdit(
|
||||
[
|
||||
{
|
||||
name: 'test',
|
||||
description: 'Test command.',
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
type: Type.USER,
|
||||
description: 'User'
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
type: Type.ROLE,
|
||||
description: 'Role'
|
||||
},
|
||||
{
|
||||
name: 'channel',
|
||||
type: Type.CHANNEL,
|
||||
description: 'Channel'
|
||||
},
|
||||
{
|
||||
name: 'string',
|
||||
type: Type.STRING,
|
||||
description: 'String'
|
||||
}
|
||||
]
|
||||
}
|
||||
d.respond({
|
||||
content: '```js\n' + `${res}` + '\n```'
|
||||
}).catch(() => {})
|
||||
} catch (e) {
|
||||
d.respond({
|
||||
content: '```js\n' + `${e.stack}` + '\n```'
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
'807935370556866560'
|
||||
)
|
||||
this.slash.commands.bulkEdit([])
|
||||
}
|
||||
|
||||
@slash()
|
||||
async hug(d: Interaction): Promise<void> {
|
||||
const id = d.data.options?.find((e) => e.name === 'user')?.value as string
|
||||
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
|
||||
const url = await fetch('https://nekos.life/api/v2/img/hug')
|
||||
.then((r) => r.json())
|
||||
.then((e) => e.url)
|
||||
|
||||
d.respond({
|
||||
embeds: [
|
||||
new Embed()
|
||||
.setTitle(`${d.user.username} hugged ${user?.username}!`)
|
||||
.setImage({ url })
|
||||
.setColor(0x2f3136)
|
||||
]
|
||||
})
|
||||
@slash() test(d: Interaction): void {
|
||||
console.log(d.resolved)
|
||||
}
|
||||
|
||||
@slash()
|
||||
async kiss(d: Interaction): Promise<void> {
|
||||
const id = d.data.options?.find((e) => e.name === 'user')?.value as string
|
||||
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
|
||||
const url = await fetch('https://nekos.life/api/v2/img/kiss')
|
||||
.then((r) => r.json())
|
||||
.then((e) => e.url)
|
||||
|
||||
d.respond({
|
||||
embeds: [
|
||||
new Embed()
|
||||
.setTitle(`${d.user.username} kissed ${user?.username}!`)
|
||||
.setImage({ url })
|
||||
.setColor(0x2f3136)
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
@slash('ping')
|
||||
pingCmd(d: Interaction): void {
|
||||
d.respond({
|
||||
content: `Pong!`
|
||||
})
|
||||
@event() raw(evt: string, d: any): void {
|
||||
if (evt === 'INTERACTION_CREATE') console.log(evt, d?.data?.resolved)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,16 +156,25 @@ export interface MessagePayload {
|
|||
stickers?: MessageStickerPayload[]
|
||||
}
|
||||
|
||||
export enum AllowedMentionType {
|
||||
Roles = 'roles',
|
||||
Users = 'users',
|
||||
Everyone = 'everyone'
|
||||
}
|
||||
|
||||
export interface AllowedMentionsPayload {
|
||||
parse?: AllowedMentionType[]
|
||||
users?: string[]
|
||||
roles?: string[]
|
||||
replied_user?: boolean
|
||||
}
|
||||
|
||||
export interface MessageOptions {
|
||||
tts?: boolean
|
||||
embed?: Embed
|
||||
file?: MessageAttachment
|
||||
files?: MessageAttachment[]
|
||||
allowedMentions?: {
|
||||
parse?: 'everyone' | 'users' | 'roles'
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
}
|
||||
allowedMentions?: AllowedMentionsPayload
|
||||
}
|
||||
|
||||
export interface ChannelMention {
|
||||
|
|
|
@ -1,22 +1,47 @@
|
|||
import { EmbedPayload } from './channel.ts'
|
||||
import { Dict } from '../utils/dict.ts'
|
||||
import {
|
||||
AllowedMentionsPayload,
|
||||
ChannelTypes,
|
||||
EmbedPayload
|
||||
} from './channel.ts'
|
||||
import { MemberPayload } from './guild.ts'
|
||||
import { RolePayload } from './role.ts'
|
||||
import { UserPayload } from './user.ts'
|
||||
|
||||
export interface InteractionOption {
|
||||
export interface InteractionApplicationCommandOption {
|
||||
/** Option name */
|
||||
name: string
|
||||
/** Type of Option */
|
||||
type: SlashCommandOptionType
|
||||
/** Value of the option */
|
||||
value?: any
|
||||
/** Sub options */
|
||||
options?: any[]
|
||||
options?: InteractionApplicationCommandOption[]
|
||||
}
|
||||
|
||||
export interface InteractionData {
|
||||
export interface InteractionChannelPayload {
|
||||
id: string
|
||||
name: string
|
||||
permissions: string
|
||||
type: ChannelTypes
|
||||
}
|
||||
|
||||
export interface InteractionApplicationCommandResolvedPayload {
|
||||
users?: Dict<UserPayload>
|
||||
members?: Dict<MemberPayload>
|
||||
channels?: Dict<InteractionChannelPayload>
|
||||
roles?: Dict<RolePayload>
|
||||
}
|
||||
|
||||
export interface InteractionApplicationCommandData {
|
||||
/** Name of the Slash Command */
|
||||
name: string
|
||||
/** Unique ID of the Slash Command */
|
||||
id: string
|
||||
/** Options (arguments) sent with Interaction */
|
||||
options: InteractionOption[]
|
||||
options: InteractionApplicationCommandOption[]
|
||||
/** Resolved data for options in Slash Command */
|
||||
resolved?: InteractionApplicationCommandResolvedPayload
|
||||
}
|
||||
|
||||
export enum InteractionType {
|
||||
|
@ -26,27 +51,30 @@ export enum InteractionType {
|
|||
APPLICATION_COMMAND = 2
|
||||
}
|
||||
|
||||
export interface InteractionMemberPayload extends MemberPayload {
|
||||
/** Permissions of the Member who initiated Interaction (Guild-only) */
|
||||
permissions: string
|
||||
}
|
||||
|
||||
export interface InteractionPayload {
|
||||
/** Type of the Interaction */
|
||||
type: InteractionType
|
||||
/** Token of the Interaction to respond */
|
||||
token: string
|
||||
/** Member object of user who invoked */
|
||||
member: MemberPayload & {
|
||||
/** Total permissions of the member in the channel, including overrides */
|
||||
permissions: string
|
||||
}
|
||||
member?: InteractionMemberPayload
|
||||
/** User who initiated Interaction (only in DMs) */
|
||||
user?: UserPayload
|
||||
/** ID of the Interaction */
|
||||
id: string
|
||||
/**
|
||||
* Data sent with the interaction
|
||||
* **This can be undefined only when Interaction is not a Slash Command**
|
||||
* Data sent with the interaction. Undefined only when Interaction is not Slash Command.*
|
||||
*/
|
||||
data: InteractionData
|
||||
data?: InteractionApplicationCommandData
|
||||
/** ID of the Guild in which Interaction was invoked */
|
||||
guild_id: string
|
||||
guild_id?: string
|
||||
/** ID of the Channel in which Interaction was invoked */
|
||||
channel_id: string
|
||||
channel_id?: string
|
||||
}
|
||||
|
||||
export interface SlashCommandChoice {
|
||||
|
@ -57,7 +85,9 @@ export interface SlashCommandChoice {
|
|||
}
|
||||
|
||||
export enum SlashCommandOptionType {
|
||||
/** A sub command that is either a part of a root command or Sub Command Group */
|
||||
SUB_COMMAND = 1,
|
||||
/** A sub command group that is present in root command's options */
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
|
@ -68,58 +98,71 @@ export enum SlashCommandOptionType {
|
|||
}
|
||||
|
||||
export interface SlashCommandOption {
|
||||
/** Name of the option. */
|
||||
name: string
|
||||
/** Description not required in Sub-Command or Sub-Command-Group */
|
||||
/** Description of the Option. Not required in Sub-Command-Group */
|
||||
description?: string
|
||||
/** Option type */
|
||||
type: SlashCommandOptionType
|
||||
/** Whether the option is required or not, false by default */
|
||||
required?: boolean
|
||||
default?: boolean
|
||||
/** Optional choices out of which User can choose value */
|
||||
choices?: SlashCommandChoice[]
|
||||
/** Nested options for Sub-Command or Sub-Command-Groups */
|
||||
options?: SlashCommandOption[]
|
||||
}
|
||||
|
||||
/** Represents the Slash Command (Application Command) payload sent for creating/bulk editing. */
|
||||
export interface SlashCommandPartial {
|
||||
/** Name of the Slash Command */
|
||||
name: string
|
||||
/** Description of the Slash Command */
|
||||
description: string
|
||||
/** Options (arguments, sub commands or group) of the Slash Command */
|
||||
options?: SlashCommandOption[]
|
||||
}
|
||||
|
||||
/** Represents a fully qualified Slash Command (Application Command) payload. */
|
||||
export interface SlashCommandPayload extends SlashCommandPartial {
|
||||
/** ID of the Slash Command */
|
||||
id: string
|
||||
/** Application ID */
|
||||
application_id: string
|
||||
}
|
||||
|
||||
export enum InteractionResponseType {
|
||||
/** Just ack a ping, Http-only. */
|
||||
PONG = 1,
|
||||
/** Do nothing, just acknowledge the Interaction */
|
||||
/** @deprecated **DEPRECATED:** Do nothing, just acknowledge the Interaction */
|
||||
ACKNOWLEDGE = 2,
|
||||
/** Send a channel message without "<User> used /<Command> with <Bot>" */
|
||||
/** @deprecated **DEPRECATED:** Send a channel message without "<User> used /<Command> with <Bot>" */
|
||||
CHANNEL_MESSAGE = 3,
|
||||
/** Send a channel message with "<User> used /<Command> with <Bot>" */
|
||||
/** Send a channel message as response. */
|
||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||
/** Send nothing further, but send "<User> used /<Command> with <Bot>" */
|
||||
ACK_WITH_SOURCE = 5
|
||||
/** Let the user know bot is processing ("thinking") and you can edit the response later */
|
||||
DEFERRED_CHANNEL_MESSAGE = 5
|
||||
}
|
||||
|
||||
export interface InteractionResponsePayload {
|
||||
/** Type of the response */
|
||||
type: InteractionResponseType
|
||||
/** Data to be sent with response. Optional for types: Pong, Acknowledge, Ack with Source */
|
||||
data?: InteractionResponseDataPayload
|
||||
}
|
||||
|
||||
export interface InteractionResponseDataPayload {
|
||||
tts?: boolean
|
||||
/** Text content of the Response (Message) */
|
||||
content: string
|
||||
/** Upto 10 Embed Objects to send with Response */
|
||||
embeds?: EmbedPayload[]
|
||||
allowed_mentions?: {
|
||||
parse?: 'everyone' | 'users' | 'roles'
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
}
|
||||
/** Allowed Mentions object */
|
||||
allowed_mentions?: AllowedMentionsPayload
|
||||
flags?: number
|
||||
}
|
||||
|
||||
export enum InteractionResponseFlags {
|
||||
/** A Message which is only visible to Interaction User, and is not saved on backend */
|
||||
/** A Message which is only visible to Interaction User. */
|
||||
EPHEMERAL = 1 << 6
|
||||
}
|
||||
|
|
3
src/utils/dict.ts
Normal file
3
src/utils/dict.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export interface Dict<T> {
|
||||
[name: string]: T
|
||||
}
|
Loading…
Reference in a new issue