2020-12-10 06:55:52 +00:00
|
|
|
import { Guild } from '../structures/guild.ts'
|
|
|
|
import { Interaction } from '../structures/slash.ts'
|
|
|
|
import {
|
2020-12-12 12:27:35 +00:00
|
|
|
InteractionType,
|
2020-12-21 13:48:46 +00:00
|
|
|
SlashCommandChoice,
|
2020-12-10 06:55:52 +00:00
|
|
|
SlashCommandOption,
|
2020-12-21 13:48:46 +00:00
|
|
|
SlashCommandOptionType,
|
2020-12-10 06:55:52 +00:00
|
|
|
SlashCommandPartial,
|
|
|
|
SlashCommandPayload
|
|
|
|
} from '../types/slash.ts'
|
|
|
|
import { Collection } from '../utils/collection.ts'
|
|
|
|
import { Client } from './client.ts'
|
2020-12-20 09:45:49 +00:00
|
|
|
import { RESTManager } from './rest.ts'
|
2020-12-22 06:58:45 +00:00
|
|
|
import { SlashModule } from './slashModule.ts'
|
|
|
|
import { verify as edverify } from 'https://deno.land/x/ed25519/mod.ts'
|
|
|
|
import { Buffer } from 'https://deno.land/std@0.80.0/node/buffer.ts'
|
2021-01-01 05:55:23 +00:00
|
|
|
import type {
|
2020-12-22 06:58:45 +00:00
|
|
|
Request as ORequest,
|
|
|
|
Response as OResponse
|
|
|
|
} from 'https://deno.land/x/opine@1.0.0/src/types.ts'
|
2021-01-01 05:55:23 +00:00
|
|
|
import type { Context } from 'https://deno.land/x/oak@v6.4.0/mod.ts'
|
2020-12-10 06:55:52 +00:00
|
|
|
|
|
|
|
export class SlashCommand {
|
2020-12-10 09:10:00 +00:00
|
|
|
slash: SlashCommandsManager
|
2020-12-10 06:55:52 +00:00
|
|
|
id: string
|
|
|
|
applicationID: string
|
|
|
|
name: string
|
|
|
|
description: string
|
|
|
|
options: SlashCommandOption[]
|
2020-12-10 09:10:00 +00:00
|
|
|
_guild?: string
|
2020-12-10 06:55:52 +00:00
|
|
|
|
2020-12-10 09:10:00 +00:00
|
|
|
constructor(manager: SlashCommandsManager, data: SlashCommandPayload) {
|
|
|
|
this.slash = manager
|
2020-12-10 06:55:52 +00:00
|
|
|
this.id = data.id
|
|
|
|
this.applicationID = data.application_id
|
|
|
|
this.name = data.name
|
|
|
|
this.description = data.description
|
2020-12-16 10:42:52 +00:00
|
|
|
this.options = data.options ?? []
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
2020-12-10 09:10:00 +00:00
|
|
|
|
|
|
|
async delete(): Promise<void> {
|
|
|
|
await this.slash.delete(this.id, this._guild)
|
|
|
|
}
|
|
|
|
|
|
|
|
async edit(data: SlashCommandPartial): Promise<void> {
|
|
|
|
await this.slash.edit(this.id, data, this._guild)
|
|
|
|
}
|
2020-12-22 06:58:45 +00:00
|
|
|
|
|
|
|
/** Create a handler for this Slash Command */
|
|
|
|
handle(
|
|
|
|
func: SlashCommandHandlerCallback,
|
|
|
|
options?: { parent?: string; group?: string }
|
|
|
|
): SlashCommand {
|
|
|
|
this.slash.slash.handle({
|
|
|
|
name: this.name,
|
|
|
|
parent: options?.parent,
|
|
|
|
group: options?.group,
|
|
|
|
guild: this._guild,
|
|
|
|
handler: func
|
|
|
|
})
|
|
|
|
return this
|
|
|
|
}
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
2020-12-21 13:48:46 +00:00
|
|
|
export interface CreateOptions {
|
|
|
|
name: string
|
|
|
|
description?: string
|
|
|
|
options?: Array<SlashCommandOption | SlashOptionCallable>
|
|
|
|
choices?: Array<SlashCommandChoice | string>
|
|
|
|
}
|
|
|
|
|
|
|
|
function createSlashOption(
|
|
|
|
type: SlashCommandOptionType,
|
|
|
|
data: CreateOptions
|
|
|
|
): SlashCommandOption {
|
|
|
|
return {
|
|
|
|
name: data.name,
|
|
|
|
type,
|
|
|
|
description:
|
|
|
|
type === 0 || type === 1
|
|
|
|
? undefined
|
|
|
|
: data.description ?? 'No description.',
|
|
|
|
options: data.options?.map((e) =>
|
2020-12-22 06:58:45 +00:00
|
|
|
typeof e === 'function' ? e(SlashOption) : e
|
2020-12-21 13:48:46 +00:00
|
|
|
),
|
|
|
|
choices:
|
|
|
|
data.choices === undefined
|
|
|
|
? undefined
|
|
|
|
: data.choices.map((e) =>
|
|
|
|
typeof e === 'string' ? { name: e, value: e } : e
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
2020-12-22 06:58:45 +00:00
|
|
|
export class SlashOption {
|
2020-12-21 13:48:46 +00:00
|
|
|
static string(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.STRING, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.BOOLEAN, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static subCommand(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.SUB_COMMAND, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static subCommandGroup(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.SUB_COMMAND_GROUP, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static role(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.ROLE, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static channel(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.CHANNEL, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static user(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.USER, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
static number(data: CreateOptions): SlashCommandOption {
|
|
|
|
return createSlashOption(SlashCommandOptionType.INTEGER, data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-22 06:58:45 +00:00
|
|
|
export type SlashOptionCallable = (o: typeof SlashOption) => SlashCommandOption
|
2020-12-21 13:48:46 +00:00
|
|
|
|
|
|
|
export type SlashBuilderOptionsData =
|
|
|
|
| Array<SlashCommandOption | SlashOptionCallable>
|
|
|
|
| {
|
|
|
|
[name: string]:
|
|
|
|
| {
|
|
|
|
description: string
|
|
|
|
type: SlashCommandOptionType
|
|
|
|
options?: SlashCommandOption[]
|
|
|
|
choices?: SlashCommandChoice[]
|
|
|
|
}
|
|
|
|
| SlashOptionCallable
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildOptionsArray(
|
|
|
|
options: SlashBuilderOptionsData
|
|
|
|
): SlashCommandOption[] {
|
|
|
|
return Array.isArray(options)
|
2020-12-22 06:58:45 +00:00
|
|
|
? options.map((op) => (typeof op === 'function' ? op(SlashOption) : op))
|
2020-12-21 13:48:46 +00:00
|
|
|
: Object.entries(options).map((entry) =>
|
|
|
|
typeof entry[1] === 'function'
|
2020-12-22 06:58:45 +00:00
|
|
|
? entry[1](SlashOption)
|
2020-12-21 13:48:46 +00:00
|
|
|
: Object.assign(entry[1], { name: entry[0] })
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SlashBuilder {
|
|
|
|
data: SlashCommandPartial
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
name?: string,
|
|
|
|
description?: string,
|
|
|
|
options?: SlashBuilderOptionsData
|
|
|
|
) {
|
|
|
|
this.data = {
|
|
|
|
name: name ?? '',
|
|
|
|
description: description ?? 'No description.',
|
|
|
|
options: options === undefined ? [] : buildOptionsArray(options)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
name(name: string): SlashBuilder {
|
|
|
|
this.data.name = name
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
description(desc: string): SlashBuilder {
|
|
|
|
this.data.description = desc
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
option(option: SlashOptionCallable | SlashCommandOption): SlashBuilder {
|
|
|
|
if (this.data.options === undefined) this.data.options = []
|
|
|
|
this.data.options.push(
|
2020-12-22 06:58:45 +00:00
|
|
|
typeof option === 'function' ? option(SlashOption) : option
|
2020-12-21 13:48:46 +00:00
|
|
|
)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
options(options: SlashBuilderOptionsData): SlashBuilder {
|
|
|
|
this.data.options = buildOptionsArray(options)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
export(): SlashCommandPartial {
|
|
|
|
if (this.data.name === '')
|
|
|
|
throw new Error('Name was not provided in Slash Builder')
|
|
|
|
return this.data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 09:10:00 +00:00
|
|
|
export class SlashCommandsManager {
|
2020-12-10 06:55:52 +00:00
|
|
|
slash: SlashClient
|
2021-01-24 18:36:19 +00:00
|
|
|
rest: RESTManager
|
2020-12-20 09:45:49 +00:00
|
|
|
|
|
|
|
constructor(client: SlashClient) {
|
|
|
|
this.slash = client
|
2021-01-24 18:36:19 +00:00
|
|
|
this.rest = client.rest
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Get all Global Slash Commands */
|
|
|
|
async all(): Promise<Collection<string, SlashCommand>> {
|
|
|
|
const col = new Collection<string, SlashCommand>()
|
|
|
|
|
2020-12-21 13:48:46 +00:00
|
|
|
const res = (await this.rest.api.applications[
|
|
|
|
this.slash.getID()
|
|
|
|
].commands.get()) as SlashCommandPayload[]
|
2020-12-10 06:55:52 +00:00
|
|
|
if (!Array.isArray(res)) return col
|
|
|
|
|
|
|
|
for (const raw of res) {
|
2020-12-10 09:10:00 +00:00
|
|
|
const cmd = new SlashCommand(this, raw)
|
|
|
|
col.set(raw.id, cmd)
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return col
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get a Guild's Slash Commands */
|
|
|
|
async guild(
|
|
|
|
guild: Guild | string
|
|
|
|
): Promise<Collection<string, SlashCommand>> {
|
|
|
|
const col = new Collection<string, SlashCommand>()
|
|
|
|
|
2020-12-21 13:48:46 +00:00
|
|
|
const res = (await this.rest.api.applications[this.slash.getID()].guilds[
|
|
|
|
typeof guild === 'string' ? guild : guild.id
|
|
|
|
].commands.get()) as SlashCommandPayload[]
|
2020-12-10 06:55:52 +00:00
|
|
|
if (!Array.isArray(res)) return col
|
|
|
|
|
|
|
|
for (const raw of res) {
|
2020-12-10 09:10:00 +00:00
|
|
|
const cmd = new SlashCommand(this, raw)
|
|
|
|
cmd._guild = typeof guild === 'string' ? guild : guild.id
|
|
|
|
col.set(raw.id, cmd)
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return col
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create a Slash Command (global or Guild) */
|
|
|
|
async create(
|
|
|
|
data: SlashCommandPartial,
|
|
|
|
guild?: Guild | string
|
|
|
|
): Promise<SlashCommand> {
|
2020-12-21 13:48:46 +00:00
|
|
|
const route =
|
2020-12-10 06:55:52 +00:00
|
|
|
guild === undefined
|
2020-12-21 13:48:46 +00:00
|
|
|
? this.rest.api.applications[this.slash.getID()].commands
|
|
|
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
2020-12-10 06:55:52 +00:00
|
|
|
typeof guild === 'string' ? guild : guild.id
|
2020-12-21 13:48:46 +00:00
|
|
|
].commands
|
|
|
|
|
|
|
|
const payload = await route.post(data)
|
2020-12-10 06:55:52 +00:00
|
|
|
|
2020-12-10 09:10:00 +00:00
|
|
|
const cmd = new SlashCommand(this, payload)
|
|
|
|
cmd._guild =
|
|
|
|
typeof guild === 'string' || guild === undefined ? guild : guild.id
|
|
|
|
|
|
|
|
return cmd
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 09:10:00 +00:00
|
|
|
/** Edit a Slash Command (global or Guild) */
|
2020-12-10 06:55:52 +00:00
|
|
|
async edit(
|
|
|
|
id: string,
|
2020-12-10 09:10:00 +00:00
|
|
|
data: SlashCommandPartial,
|
|
|
|
guild?: Guild | string
|
|
|
|
): Promise<SlashCommandsManager> {
|
2020-12-21 13:48:46 +00:00
|
|
|
const route =
|
2020-12-10 06:55:52 +00:00
|
|
|
guild === undefined
|
2020-12-21 13:48:46 +00:00
|
|
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
|
|
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
|
|
|
typeof guild === 'string' ? guild : guild.id
|
|
|
|
].commands[id]
|
|
|
|
|
|
|
|
await route.patch(data)
|
2020-12-10 06:55:52 +00:00
|
|
|
return this
|
|
|
|
}
|
2020-12-10 09:10:00 +00:00
|
|
|
|
|
|
|
/** Delete a Slash Command (global or Guild) */
|
|
|
|
async delete(
|
|
|
|
id: string,
|
|
|
|
guild?: Guild | string
|
|
|
|
): Promise<SlashCommandsManager> {
|
2020-12-21 13:48:46 +00:00
|
|
|
const route =
|
2020-12-10 09:10:00 +00:00
|
|
|
guild === undefined
|
2020-12-21 13:48:46 +00:00
|
|
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
|
|
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
|
|
|
typeof guild === 'string' ? guild : guild.id
|
|
|
|
].commands[id]
|
|
|
|
|
|
|
|
await route.delete()
|
2020-12-10 09:10:00 +00:00
|
|
|
return this
|
|
|
|
}
|
2020-12-20 09:45:49 +00:00
|
|
|
|
|
|
|
/** Get a Slash Command (global or Guild) */
|
|
|
|
async get(id: string, guild?: Guild | string): Promise<SlashCommand> {
|
2020-12-21 13:48:46 +00:00
|
|
|
const route =
|
2020-12-20 09:45:49 +00:00
|
|
|
guild === undefined
|
2020-12-21 13:48:46 +00:00
|
|
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
|
|
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
|
|
|
typeof guild === 'string' ? guild : guild.id
|
|
|
|
].commands[id]
|
|
|
|
|
|
|
|
const data = await route.get()
|
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
return new SlashCommand(this, data)
|
|
|
|
}
|
2021-01-24 14:20:49 +00:00
|
|
|
|
|
|
|
/** Bulk Edit Global or Guild Slash Commands */
|
|
|
|
async bulkEdit(
|
|
|
|
cmds: Array<SlashCommandPartial | SlashCommandPayload>,
|
|
|
|
guild?: Guild | string
|
|
|
|
): Promise<SlashCommandsManager> {
|
|
|
|
const route =
|
|
|
|
guild === undefined
|
|
|
|
? this.rest.api.applications[this.slash.getID()].commands
|
|
|
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
|
|
|
typeof guild === 'string' ? guild : guild.id
|
|
|
|
].commands
|
|
|
|
|
|
|
|
await route.put(cmds)
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
2020-12-10 09:10:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export type SlashCommandHandlerCallback = (interaction: Interaction) => any
|
|
|
|
export interface SlashCommandHandler {
|
|
|
|
name: string
|
|
|
|
guild?: string
|
2020-12-16 10:00:13 +00:00
|
|
|
parent?: string
|
2020-12-16 13:05:26 +00:00
|
|
|
group?: string
|
2020-12-10 09:10:00 +00:00
|
|
|
handler: SlashCommandHandlerCallback
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
export interface SlashOptions {
|
|
|
|
id?: string | (() => string)
|
|
|
|
client?: Client
|
|
|
|
enabled?: boolean
|
|
|
|
token?: string
|
|
|
|
rest?: RESTManager
|
2020-12-22 06:58:45 +00:00
|
|
|
publicKey?: string
|
2020-12-20 09:45:49 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 06:55:52 +00:00
|
|
|
export class SlashClient {
|
2020-12-20 09:45:49 +00:00
|
|
|
id: string | (() => string)
|
|
|
|
client?: Client
|
|
|
|
token?: string
|
2020-12-10 06:55:52 +00:00
|
|
|
enabled: boolean = true
|
2020-12-10 09:10:00 +00:00
|
|
|
commands: SlashCommandsManager
|
|
|
|
handlers: SlashCommandHandler[] = []
|
2020-12-20 09:45:49 +00:00
|
|
|
rest: RESTManager
|
2020-12-22 06:58:45 +00:00
|
|
|
modules: SlashModule[] = []
|
|
|
|
publicKey?: string
|
|
|
|
|
|
|
|
_decoratedSlash?: Array<{
|
|
|
|
name: string
|
|
|
|
guild?: string
|
|
|
|
parent?: string
|
|
|
|
group?: string
|
|
|
|
handler: (interaction: Interaction) => any
|
|
|
|
}>
|
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
constructor(options: SlashOptions) {
|
|
|
|
let id = options.id
|
|
|
|
if (options.token !== undefined) id = atob(options.token?.split('.')[0])
|
|
|
|
if (id === undefined)
|
|
|
|
throw new Error('ID could not be found. Pass at least client or token')
|
|
|
|
this.id = id
|
|
|
|
this.client = options.client
|
|
|
|
this.token = options.token
|
2020-12-22 06:58:45 +00:00
|
|
|
this.publicKey = options.publicKey
|
2020-12-10 06:55:52 +00:00
|
|
|
|
2021-01-24 18:49:08 +00:00
|
|
|
this.enabled = options.enabled ?? true
|
2020-12-10 06:55:52 +00:00
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
if (this.client?._decoratedSlash !== undefined) {
|
2020-12-10 09:10:00 +00:00
|
|
|
this.client._decoratedSlash.forEach((e) => {
|
2021-01-24 18:36:19 +00:00
|
|
|
e.handler = e.handler.bind(this.client)
|
2020-12-10 09:10:00 +00:00
|
|
|
this.handlers.push(e)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-22 06:58:45 +00:00
|
|
|
if (this._decoratedSlash !== undefined) {
|
|
|
|
this._decoratedSlash.forEach((e) => {
|
2021-01-24 18:36:19 +00:00
|
|
|
e.handler = e.handler.bind(this.client)
|
2020-12-22 06:58:45 +00:00
|
|
|
this.handlers.push(e)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
this.rest =
|
|
|
|
options.client === undefined
|
|
|
|
? options.rest === undefined
|
|
|
|
? new RESTManager({
|
|
|
|
token: this.token
|
|
|
|
})
|
|
|
|
: options.rest
|
|
|
|
: options.client.rest
|
|
|
|
|
|
|
|
this.client?.on('interactionCreate', (interaction) =>
|
2020-12-16 13:05:26 +00:00
|
|
|
this._process(interaction)
|
2020-12-10 06:55:52 +00:00
|
|
|
)
|
2021-01-24 18:36:19 +00:00
|
|
|
|
|
|
|
this.commands = new SlashCommandsManager(this)
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|
|
|
|
|
2020-12-20 09:45:49 +00:00
|
|
|
getID(): string {
|
|
|
|
return typeof this.id === 'string' ? this.id : this.id()
|
|
|
|
}
|
|
|
|
|
2020-12-10 09:10:00 +00:00
|
|
|
/** Adds a new Slash Command Handler */
|
2020-12-16 13:11:01 +00:00
|
|
|
handle(handler: SlashCommandHandler): SlashClient {
|
|
|
|
this.handlers.push(handler)
|
2020-12-10 06:55:52 +00:00
|
|
|
return this
|
|
|
|
}
|
2020-12-10 09:10:00 +00:00
|
|
|
|
2021-01-01 05:55:23 +00:00
|
|
|
/** Load a Slash Module */
|
2020-12-23 09:56:02 +00:00
|
|
|
loadModule(module: SlashModule): SlashClient {
|
|
|
|
this.modules.push(module)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-01-01 05:55:23 +00:00
|
|
|
/** Get all Handlers. Including Slash Modules */
|
2020-12-22 06:58:45 +00:00
|
|
|
getHandlers(): SlashCommandHandler[] {
|
|
|
|
let res = this.handlers
|
|
|
|
for (const mod of this.modules) {
|
2020-12-23 09:56:02 +00:00
|
|
|
if (mod === undefined) continue
|
|
|
|
res = [
|
|
|
|
...res,
|
|
|
|
...mod.commands.map((cmd) => {
|
|
|
|
cmd.handler = cmd.handler.bind(mod)
|
|
|
|
return cmd
|
|
|
|
})
|
|
|
|
]
|
2020-12-22 06:58:45 +00:00
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2021-01-01 05:55:23 +00:00
|
|
|
/** Get Handler for an Interaction. Supports nested sub commands and sub command groups. */
|
2020-12-16 13:05:26 +00:00
|
|
|
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
|
2020-12-22 06:58:45 +00:00
|
|
|
return this.getHandlers().find((e) => {
|
2020-12-16 13:05:26 +00:00
|
|
|
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
|
|
|
|
const groupMatched =
|
|
|
|
e.group !== undefined && e.parent !== undefined
|
|
|
|
? i.options
|
|
|
|
.find((o) => o.name === e.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
|
|
|
|
: true
|
|
|
|
const nameMatched1 = e.name === i.name
|
|
|
|
const parentMatched = hasGroupOrParent ? e.parent === i.name : true
|
|
|
|
const nameMatched = hasGroupOrParent ? parentMatched : nameMatched1
|
|
|
|
|
|
|
|
const matched = groupMatched && subMatched && nameMatched
|
|
|
|
return matched
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-12 12:27:35 +00:00
|
|
|
/** Process an incoming Slash Command (interaction) */
|
2020-12-16 13:05:26 +00:00
|
|
|
private _process(interaction: Interaction): void {
|
2020-12-10 09:10:00 +00:00
|
|
|
if (!this.enabled) return
|
|
|
|
|
2020-12-12 12:27:35 +00:00
|
|
|
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
|
|
|
|
|
2020-12-16 13:05:26 +00:00
|
|
|
const cmd = this._getCommand(interaction)
|
2021-01-01 05:55:23 +00:00
|
|
|
if (cmd?.group !== undefined)
|
|
|
|
interaction.data.options = interaction.data.options[0].options ?? []
|
|
|
|
if (cmd?.parent !== undefined)
|
|
|
|
interaction.data.options = interaction.data.options[0].options ?? []
|
2020-12-10 09:10:00 +00:00
|
|
|
|
|
|
|
if (cmd === undefined) return
|
|
|
|
|
|
|
|
cmd.handler(interaction)
|
|
|
|
}
|
2020-12-22 06:58:45 +00:00
|
|
|
|
|
|
|
async verifyKey(
|
|
|
|
rawBody: string | Uint8Array | Buffer,
|
|
|
|
signature: string,
|
|
|
|
timestamp: string
|
|
|
|
): Promise<boolean> {
|
|
|
|
if (this.publicKey === undefined)
|
|
|
|
throw new Error('Public Key is not present')
|
|
|
|
return edverify(
|
|
|
|
signature,
|
|
|
|
Buffer.concat([
|
|
|
|
Buffer.from(timestamp, 'utf-8'),
|
|
|
|
Buffer.from(
|
|
|
|
rawBody instanceof Uint8Array
|
|
|
|
? new TextDecoder().decode(rawBody)
|
|
|
|
: rawBody
|
|
|
|
)
|
|
|
|
]),
|
|
|
|
this.publicKey
|
|
|
|
).catch(() => false)
|
|
|
|
}
|
|
|
|
|
|
|
|
async verifyOpineRequest(req: ORequest): Promise<boolean> {
|
|
|
|
const signature = req.headers.get('x-signature-ed25519')
|
|
|
|
const timestamp = req.headers.get('x-signature-timestamp')
|
|
|
|
const contentLength = req.headers.get('content-length')
|
|
|
|
|
|
|
|
if (signature === null || timestamp === null || contentLength === null)
|
|
|
|
return false
|
|
|
|
|
|
|
|
const body = new Uint8Array(parseInt(contentLength))
|
|
|
|
await req.body.read(body)
|
|
|
|
|
|
|
|
const verified = await this.verifyKey(body, signature, timestamp)
|
|
|
|
if (!verified) return false
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Middleware to verify request in Opine framework. */
|
|
|
|
async verifyOpineMiddleware(
|
|
|
|
req: ORequest,
|
|
|
|
res: OResponse,
|
|
|
|
next: CallableFunction
|
|
|
|
): Promise<any> {
|
|
|
|
const verified = await this.verifyOpineRequest(req)
|
|
|
|
if (!verified) return res.setStatus(401).end()
|
|
|
|
|
|
|
|
await next()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: create verifyOakMiddleware too
|
|
|
|
/** Method to verify Request from Oak server "Context". */
|
|
|
|
async verifyOakRequest(ctx: Context): Promise<any> {
|
|
|
|
const signature = ctx.request.headers.get('x-signature-ed25519')
|
|
|
|
const timestamp = ctx.request.headers.get('x-signature-timestamp')
|
|
|
|
const contentLength = ctx.request.headers.get('content-length')
|
|
|
|
|
|
|
|
if (
|
|
|
|
signature === null ||
|
|
|
|
timestamp === null ||
|
|
|
|
contentLength === null ||
|
|
|
|
ctx.request.hasBody !== true
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
const body = await ctx.request.body().value
|
|
|
|
|
|
|
|
const verified = await this.verifyKey(body as any, signature, timestamp)
|
|
|
|
if (!verified) return false
|
|
|
|
return true
|
|
|
|
}
|
2020-12-10 06:55:52 +00:00
|
|
|
}
|