This commit is contained in:
commit
1738204406
|
@ -1,7 +1,11 @@
|
||||||
import { Guild } from '../structures/guild.ts'
|
import { Guild } from '../structures/guild.ts'
|
||||||
import { Interaction } from '../structures/slash.ts'
|
import {
|
||||||
|
Interaction,
|
||||||
|
InteractionApplicationCommandResolved
|
||||||
|
} from '../structures/slash.ts'
|
||||||
import {
|
import {
|
||||||
InteractionPayload,
|
InteractionPayload,
|
||||||
|
InteractionResponsePayload,
|
||||||
InteractionType,
|
InteractionType,
|
||||||
SlashCommandChoice,
|
SlashCommandChoice,
|
||||||
SlashCommandOption,
|
SlashCommandOption,
|
||||||
|
@ -14,6 +18,7 @@ import { Client } from './client.ts'
|
||||||
import { RESTManager } from './rest.ts'
|
import { RESTManager } from './rest.ts'
|
||||||
import { SlashModule } from './slashModule.ts'
|
import { SlashModule } from './slashModule.ts'
|
||||||
import { verify as edverify } from 'https://deno.land/x/ed25519@1.0.1/mod.ts'
|
import { verify as edverify } from 'https://deno.land/x/ed25519@1.0.1/mod.ts'
|
||||||
|
import { User } from '../structures/user.ts'
|
||||||
|
|
||||||
export class SlashCommand {
|
export class SlashCommand {
|
||||||
slash: SlashCommandsManager
|
slash: SlashCommandsManager
|
||||||
|
@ -375,6 +380,7 @@ export interface SlashOptions {
|
||||||
const encoder = new TextEncoder()
|
const encoder = new TextEncoder()
|
||||||
const decoder = new TextDecoder('utf-8')
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
|
||||||
|
/** Slash Client represents an Interactions Client which can be used without Harmony Client. */
|
||||||
export class SlashClient {
|
export class SlashClient {
|
||||||
id: string | (() => string)
|
id: string | (() => string)
|
||||||
client?: Client
|
client?: Client
|
||||||
|
@ -539,16 +545,17 @@ export class SlashClient {
|
||||||
return edverify(signature, fullBody, this.publicKey).catch(() => false)
|
return edverify(signature, fullBody, this.publicKey).catch(() => false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verify [Deno Std HTTP Server Request](https://deno.land/std/http/server.ts) and return Interaction */
|
/** Verify [Deno Std HTTP Server Request](https://deno.land/std/http/server.ts) and return Interaction. **Data present in Interaction returned by this method is very different from actual typings as there is no real `Client` behind the scenes to cache things.** */
|
||||||
async verifyServerRequest(req: {
|
async verifyServerRequest(req: {
|
||||||
headers: Headers
|
headers: Headers
|
||||||
method: string
|
method: string
|
||||||
body: Deno.Reader
|
body: Deno.Reader
|
||||||
respond: (options: {
|
respond: (options: {
|
||||||
status?: number
|
status?: number
|
||||||
|
headers?: Headers
|
||||||
body?: string | Uint8Array
|
body?: string | Uint8Array
|
||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
}): Promise<boolean | Interaction> {
|
}): Promise<false | Interaction> {
|
||||||
if (req.method.toLowerCase() !== 'post') return false
|
if (req.method.toLowerCase() !== 'post') return false
|
||||||
|
|
||||||
const signature = req.headers.get('x-signature-ed25519')
|
const signature = req.headers.get('x-signature-ed25519')
|
||||||
|
@ -561,7 +568,31 @@ export class SlashClient {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload: InteractionPayload = JSON.parse(decoder.decode(rawbody))
|
const payload: InteractionPayload = JSON.parse(decoder.decode(rawbody))
|
||||||
const res = new Interaction(this as any, payload, {})
|
|
||||||
|
// TODO: Maybe fix all this hackery going on here?
|
||||||
|
const res = new Interaction(this as any, payload, {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
user: new User(this as any, (payload.member?.user ?? payload.user)!),
|
||||||
|
member: payload.member as any,
|
||||||
|
guild: payload.guild_id as any,
|
||||||
|
channel: payload.channel_id as any,
|
||||||
|
resolved: ((payload.data
|
||||||
|
?.resolved as unknown) as InteractionApplicationCommandResolved) ?? {
|
||||||
|
users: {},
|
||||||
|
members: {},
|
||||||
|
roles: {},
|
||||||
|
channels: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
res._httpRespond = async (d: InteractionResponsePayload) =>
|
||||||
|
await req.respond({
|
||||||
|
status: 200,
|
||||||
|
headers: new Headers({
|
||||||
|
'content-type': 'application/json'
|
||||||
|
}),
|
||||||
|
body: JSON.stringify(d)
|
||||||
|
})
|
||||||
|
|
||||||
return res
|
return res
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -7,12 +7,14 @@ import {
|
||||||
} from '../types/channel.ts'
|
} from '../types/channel.ts'
|
||||||
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
||||||
import {
|
import {
|
||||||
|
InteractionApplicationCommandData,
|
||||||
InteractionApplicationCommandOption,
|
InteractionApplicationCommandOption,
|
||||||
InteractionChannelPayload,
|
InteractionChannelPayload,
|
||||||
InteractionPayload,
|
InteractionPayload,
|
||||||
InteractionResponseFlags,
|
InteractionResponseFlags,
|
||||||
InteractionResponsePayload,
|
InteractionResponsePayload,
|
||||||
InteractionResponseType,
|
InteractionResponseType,
|
||||||
|
InteractionType,
|
||||||
SlashCommandOptionType
|
SlashCommandOptionType
|
||||||
} from '../types/slash.ts'
|
} from '../types/slash.ts'
|
||||||
import { Dict } from '../utils/dict.ts'
|
import { Dict } from '../utils/dict.ts'
|
||||||
|
@ -26,7 +28,6 @@ import { Message } from './message.ts'
|
||||||
import { Role } from './role.ts'
|
import { Role } from './role.ts'
|
||||||
import { GuildTextChannel, TextChannel } from './textChannel.ts'
|
import { GuildTextChannel, TextChannel } from './textChannel.ts'
|
||||||
import { User } from './user.ts'
|
import { User } from './user.ts'
|
||||||
import { Webhook } from './webhook.ts'
|
|
||||||
|
|
||||||
interface WebhookMessageOptions extends MessageOptions {
|
interface WebhookMessageOptions extends MessageOptions {
|
||||||
embeds?: Embed[]
|
embeds?: Embed[]
|
||||||
|
@ -86,18 +87,30 @@ export class InteractionUser extends User {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Interaction extends SnowflakeBase {
|
export class Interaction extends SnowflakeBase {
|
||||||
/** This will be `SlashClient` in case of `SlashClient#verifyServerRequest` */
|
/** Type of Interaction */
|
||||||
client!: Client
|
type: InteractionType
|
||||||
type: number
|
/** Interaction Token */
|
||||||
token: string
|
token: string
|
||||||
/** Interaction ID */
|
/** Interaction ID */
|
||||||
id: string
|
id: string
|
||||||
data: InteractionData
|
/** Data sent with Interaction. Only applies to Application Command */
|
||||||
channel: GuildTextChannel
|
data?: InteractionApplicationCommandData
|
||||||
|
/** Channel in which Interaction was initiated */
|
||||||
|
channel?: TextChannel | GuildTextChannel
|
||||||
|
/** Guild in which Interaction was initiated */
|
||||||
guild?: Guild
|
guild?: Guild
|
||||||
|
/** Member object of who initiated the Interaction */
|
||||||
member?: Member
|
member?: Member
|
||||||
_savedHook?: Webhook
|
/** User object of who invoked Interaction */
|
||||||
_respond?: (data: InteractionResponsePayload) => unknown
|
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
|
||||||
|
_httpRespond?: (d: InteractionResponsePayload) => unknown
|
||||||
|
_httpResponded?: boolean
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
@ -137,7 +150,8 @@ export class Interaction extends SnowflakeBase {
|
||||||
if (op === undefined || op.value === undefined) return undefined as any
|
if (op === undefined || op.value === undefined) return undefined as any
|
||||||
if (op.type === SlashCommandOptionType.USER) {
|
if (op.type === SlashCommandOptionType.USER) {
|
||||||
const u: InteractionUser = this.resolved.users[op.value] as any
|
const u: InteractionUser = this.resolved.users[op.value] as any
|
||||||
if (this.resolved.members[op.value] !== undefined) u.member = this.resolved.members[op.value]
|
if (this.resolved.members[op.value] !== undefined)
|
||||||
|
u.member = this.resolved.members[op.value]
|
||||||
return u as any
|
return u as any
|
||||||
} else if (op.type === SlashCommandOptionType.ROLE)
|
} else if (op.type === SlashCommandOptionType.ROLE)
|
||||||
return this.resolved.roles[op.value] as any
|
return this.resolved.roles[op.value] as any
|
||||||
|
@ -172,19 +186,24 @@ export class Interaction extends SnowflakeBase {
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.client.rest.post(
|
if (this._httpRespond !== undefined && this._httpResponded !== true) {
|
||||||
INTERACTION_CALLBACK(this.id, this.token),
|
this._httpResponded = true
|
||||||
payload
|
await this._httpRespond(payload)
|
||||||
)
|
} else
|
||||||
|
await this.client.rest.post(
|
||||||
|
INTERACTION_CALLBACK(this.id, this.token),
|
||||||
|
payload
|
||||||
|
)
|
||||||
this.responded = true
|
this.responded = true
|
||||||
|
|
||||||
return this
|
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! */
|
/** 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> {
|
async defer(ephemeral = false): Promise<Interaction> {
|
||||||
await this.respond({
|
await this.respond({
|
||||||
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE
|
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE,
|
||||||
|
flags: ephemeral ? 1 << 6 : 0
|
||||||
})
|
})
|
||||||
this.deferred = true
|
this.deferred = true
|
||||||
return this
|
return this
|
||||||
|
|
|
@ -18,27 +18,10 @@ await slash.commands.bulkEdit([
|
||||||
const options = { port: 8000 }
|
const options = { port: 8000 }
|
||||||
console.log('Listen on port: ' + options.port.toString())
|
console.log('Listen on port: ' + options.port.toString())
|
||||||
listenAndServe(options, async (req) => {
|
listenAndServe(options, async (req) => {
|
||||||
const verify = await slash.verifyServerRequest(req)
|
const d = await slash.verifyServerRequest(req)
|
||||||
if (verify === false)
|
if (d === false) return req.respond({ status: 401, body: 'not authorized' })
|
||||||
return req.respond({ status: 401, body: 'not authorized' })
|
|
||||||
|
|
||||||
const respond = async (d: any): Promise<void> =>
|
console.log(d)
|
||||||
req.respond({
|
if (d.type === 1) return d.respond({ type: 1 })
|
||||||
status: 200,
|
d.reply('Pong!')
|
||||||
body: JSON.stringify(d),
|
|
||||||
headers: new Headers({
|
|
||||||
'content-type': 'application/json'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const body = JSON.parse(
|
|
||||||
new TextDecoder('utf-8').decode(await Deno.readAll(req.body))
|
|
||||||
)
|
|
||||||
if (body.type === 1) return await respond({ type: 1 })
|
|
||||||
await respond({
|
|
||||||
type: 4,
|
|
||||||
data: {
|
|
||||||
content: 'Pong!'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue