This commit is contained in:
DjDeveloperr 2021-03-26 12:28:06 +05:30
commit 1738204406
3 changed files with 74 additions and 41 deletions

View File

@ -1,7 +1,11 @@
import { Guild } from '../structures/guild.ts'
import { Interaction } from '../structures/slash.ts'
import {
Interaction,
InteractionApplicationCommandResolved
} from '../structures/slash.ts'
import {
InteractionPayload,
InteractionResponsePayload,
InteractionType,
SlashCommandChoice,
SlashCommandOption,
@ -14,6 +18,7 @@ import { Client } from './client.ts'
import { RESTManager } from './rest.ts'
import { SlashModule } from './slashModule.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 {
slash: SlashCommandsManager
@ -375,6 +380,7 @@ export interface SlashOptions {
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
@ -539,16 +545,17 @@ export class SlashClient {
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: {
headers: Headers
method: string
body: Deno.Reader
respond: (options: {
status?: number
headers?: Headers
body?: string | Uint8Array
}) => Promise<void>
}): Promise<boolean | Interaction> {
}): Promise<false | Interaction> {
if (req.method.toLowerCase() !== 'post') return false
const signature = req.headers.get('x-signature-ed25519')
@ -561,7 +568,31 @@ export class SlashClient {
try {
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
} catch (e) {
return false

View File

@ -7,12 +7,14 @@ import {
} from '../types/channel.ts'
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
import {
InteractionApplicationCommandData,
InteractionApplicationCommandOption,
InteractionChannelPayload,
InteractionPayload,
InteractionResponseFlags,
InteractionResponsePayload,
InteractionResponseType,
InteractionType,
SlashCommandOptionType
} from '../types/slash.ts'
import { Dict } from '../utils/dict.ts'
@ -26,7 +28,6 @@ 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[]
@ -86,18 +87,30 @@ export class InteractionUser extends User {
}
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
/** 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
_savedHook?: Webhook
_respond?: (data: InteractionResponsePayload) => unknown
/** 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
_httpRespond?: (d: InteractionResponsePayload) => unknown
_httpResponded?: boolean
constructor(
client: Client,
@ -137,7 +150,8 @@ export class Interaction extends SnowflakeBase {
if (op === undefined || op.value === undefined) return undefined as any
if (op.type === SlashCommandOptionType.USER) {
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
} else if (op.type === SlashCommandOptionType.ROLE)
return this.resolved.roles[op.value] as any
@ -172,19 +186,24 @@ export class Interaction extends SnowflakeBase {
: undefined
}
await this.client.rest.post(
INTERACTION_CALLBACK(this.id, this.token),
payload
)
if (this._httpRespond !== undefined && this._httpResponded !== true) {
this._httpResponded = true
await this._httpRespond(payload)
} else
await this.client.rest.post(
INTERACTION_CALLBACK(this.id, this.token),
payload
)
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> {
async defer(ephemeral = false): Promise<Interaction> {
await this.respond({
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE
type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE,
flags: ephemeral ? 1 << 6 : 0
})
this.deferred = true
return this

View File

@ -18,27 +18,10 @@ await slash.commands.bulkEdit([
const options = { port: 8000 }
console.log('Listen on port: ' + options.port.toString())
listenAndServe(options, async (req) => {
const verify = await slash.verifyServerRequest(req)
if (verify === false)
return req.respond({ status: 401, body: 'not authorized' })
const d = await slash.verifyServerRequest(req)
if (d === false) return req.respond({ status: 401, body: 'not authorized' })
const respond = async (d: any): Promise<void> =>
req.respond({
status: 200,
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!'
}
})
console.log(d)
if (d.type === 1) return d.respond({ type: 1 })
d.reply('Pong!')
})