encoding util

This commit is contained in:
DjDeveloperr 2021-04-04 10:21:39 +05:30
parent 38b11f4076
commit 2b46b38908
4 changed files with 91 additions and 68 deletions

View file

@ -18,6 +18,7 @@ import { delay } from '../utils/delay.ts'
import { VoiceChannel } from '../structures/guildVoiceChannel.ts' import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
import { Guild } from '../structures/guild.ts' import { Guild } from '../structures/guild.ts'
import { HarmonyEventEmitter } from '../utils/events.ts' import { HarmonyEventEmitter } from '../utils/events.ts'
import { decodeText } from '../utils/encoding.ts'
export interface RequestMembersOptions { export interface RequestMembersOptions {
limit?: number limit?: number
@ -89,7 +90,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
} }
if (data instanceof Uint8Array) { if (data instanceof Uint8Array) {
data = unzlib(data) data = unzlib(data)
data = new TextDecoder('utf-8').decode(data) data = decodeText(data)
} }
const { op, d, s, t }: GatewayResponse = JSON.parse(data) const { op, d, s, t }: GatewayResponse = JSON.parse(data)
@ -385,8 +386,8 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
channel === undefined channel === undefined
? null ? null
: typeof channel === 'string' : typeof channel === 'string'
? channel ? channel
: channel?.id, : channel?.id,
self_mute: voiceOptions.mute === undefined ? false : voiceOptions.mute, self_mute: voiceOptions.mute === undefined ? false : voiceOptions.mute,
self_deaf: voiceOptions.deaf === undefined ? false : voiceOptions.deaf self_deaf: voiceOptions.deaf === undefined ? false : voiceOptions.deaf
} }
@ -427,7 +428,11 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
} }
close(code: number = 1000, reason?: string): void { close(code: number = 1000, reason?: string): void {
this.debug(`Closing with code ${code}${reason !== undefined && reason !== '' ? ` and reason ${reason}` : ''}`) this.debug(
`Closing with code ${code}${
reason !== undefined && reason !== '' ? ` and reason ${reason}` : ''
}`
)
return this.websocket?.close(code, reason) return this.websocket?.close(code, reason)
} }

View file

@ -19,7 +19,8 @@ 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' import { User } from '../structures/user.ts'
import { HarmonyEventEmitter } from "../utils/events.ts" import { HarmonyEventEmitter } from '../utils/events.ts'
import { encodeText, decodeText } from '../utils/encoding.ts'
export class SlashCommand { export class SlashCommand {
slash: SlashCommandsManager slash: SlashCommandsManager
@ -94,8 +95,8 @@ function createSlashOption(
data.choices === undefined data.choices === undefined
? undefined ? undefined
: data.choices.map((e) => : data.choices.map((e) =>
typeof e === 'string' ? { name: e, value: e } : e typeof e === 'string' ? { name: e, value: e } : e
) )
} }
} }
@ -139,15 +140,15 @@ export type SlashOptionCallable = (o: typeof SlashOption) => SlashCommandOption
export type SlashBuilderOptionsData = export type SlashBuilderOptionsData =
| Array<SlashCommandOption | SlashOptionCallable> | Array<SlashCommandOption | SlashOptionCallable>
| { | {
[name: string]: [name: string]:
| { | {
description: string description: string
type: SlashCommandOptionType type: SlashCommandOptionType
options?: SlashCommandOption[] options?: SlashCommandOption[]
choices?: SlashCommandChoice[] choices?: SlashCommandChoice[]
}
| SlashOptionCallable
} }
| SlashOptionCallable
}
function buildOptionsArray( function buildOptionsArray(
options: SlashBuilderOptionsData options: SlashBuilderOptionsData
@ -155,10 +156,10 @@ function buildOptionsArray(
return Array.isArray(options) return Array.isArray(options)
? options.map((op) => (typeof op === 'function' ? op(SlashOption) : op)) ? options.map((op) => (typeof op === 'function' ? op(SlashOption) : op))
: Object.entries(options).map((entry) => : Object.entries(options).map((entry) =>
typeof entry[1] === 'function' typeof entry[1] === 'function'
? entry[1](SlashOption) ? entry[1](SlashOption)
: Object.assign(entry[1], { name: entry[0] }) : Object.assign(entry[1], { name: entry[0] })
) )
} }
/** Slash Command Builder */ /** Slash Command Builder */
@ -268,8 +269,8 @@ export class SlashCommandsManager {
guild === undefined guild === undefined
? this.rest.api.applications[this.slash.getID()].commands ? this.rest.api.applications[this.slash.getID()].commands
: this.rest.api.applications[this.slash.getID()].guilds[ : this.rest.api.applications[this.slash.getID()].guilds[
typeof guild === 'string' ? guild : guild.id typeof guild === 'string' ? guild : guild.id
].commands ].commands
const payload = await route.post(data) const payload = await route.post(data)
@ -277,8 +278,8 @@ export class SlashCommandsManager {
typeof guild === 'object' typeof guild === 'object'
? guild ? guild
: guild === undefined : guild === undefined
? undefined ? undefined
: await this.slash.client?.guilds.get(guild) : await this.slash.client?.guilds.get(guild)
const cmd = new SlashCommand(this, payload, _guild) const cmd = new SlashCommand(this, payload, _guild)
cmd._guild = cmd._guild =
@ -297,8 +298,8 @@ export class SlashCommandsManager {
guild === undefined guild === undefined
? this.rest.api.applications[this.slash.getID()].commands[id] ? this.rest.api.applications[this.slash.getID()].commands[id]
: this.rest.api.applications[this.slash.getID()].guilds[ : this.rest.api.applications[this.slash.getID()].guilds[
typeof guild === 'string' ? guild : guild.id typeof guild === 'string' ? guild : guild.id
].commands[id] ].commands[id]
await route.patch(data) await route.patch(data)
return this return this
@ -313,8 +314,8 @@ export class SlashCommandsManager {
guild === undefined guild === undefined
? this.rest.api.applications[this.slash.getID()].commands[id] ? this.rest.api.applications[this.slash.getID()].commands[id]
: this.rest.api.applications[this.slash.getID()].guilds[ : this.rest.api.applications[this.slash.getID()].guilds[
typeof guild === 'string' ? guild : guild.id typeof guild === 'string' ? guild : guild.id
].commands[id] ].commands[id]
await route.delete() await route.delete()
return this return this
@ -326,8 +327,8 @@ export class SlashCommandsManager {
guild === undefined guild === undefined
? this.rest.api.applications[this.slash.getID()].commands[id] ? this.rest.api.applications[this.slash.getID()].commands[id]
: this.rest.api.applications[this.slash.getID()].guilds[ : this.rest.api.applications[this.slash.getID()].guilds[
typeof guild === 'string' ? guild : guild.id typeof guild === 'string' ? guild : guild.id
].commands[id] ].commands[id]
const data = await route.get() const data = await route.get()
@ -335,8 +336,8 @@ export class SlashCommandsManager {
typeof guild === 'object' typeof guild === 'object'
? guild ? guild
: guild === undefined : guild === undefined
? undefined ? undefined
: await this.slash.client?.guilds.get(guild) : await this.slash.client?.guilds.get(guild)
return new SlashCommand(this, data, _guild) return new SlashCommand(this, data, _guild)
} }
@ -350,8 +351,8 @@ export class SlashCommandsManager {
guild === undefined guild === undefined
? this.rest.api.applications[this.slash.getID()].commands ? this.rest.api.applications[this.slash.getID()].commands
: this.rest.api.applications[this.slash.getID()].guilds[ : this.rest.api.applications[this.slash.getID()].guilds[
typeof guild === 'string' ? guild : guild.id typeof guild === 'string' ? guild : guild.id
].commands ].commands
await route.put(cmds) await route.put(cmds)
@ -378,9 +379,6 @@ export interface SlashOptions {
publicKey?: string publicKey?: string
} }
const encoder = new TextEncoder()
const decoder = new TextDecoder('utf-8')
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type SlashClientEvents = { export type SlashClientEvents = {
interaction: [Interaction] interaction: [Interaction]
@ -439,13 +437,14 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
options.client === undefined options.client === undefined
? options.rest === undefined ? options.rest === undefined
? new RESTManager({ ? new RESTManager({
token: this.token token: this.token
}) })
: options.rest : options.rest
: options.client.rest : options.client.rest
this.client?.on('interactionCreate', async (interaction) => this.client?.on(
await this._process(interaction) 'interactionCreate',
async (interaction) => await this._process(interaction)
) )
this.commands = new SlashCommandsManager(this) this.commands = new SlashCommandsManager(this)
@ -490,20 +489,20 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
const groupMatched = const groupMatched =
e.group !== undefined && e.parent !== undefined e.group !== undefined && e.parent !== undefined
? i.options ? i.options
.find( .find(
(o) => (o) =>
o.name === e.group && o.name === e.group &&
o.type === SlashCommandOptionType.SUB_COMMAND_GROUP o.type === SlashCommandOptionType.SUB_COMMAND_GROUP
) )
?.options?.find((o) => o.name === e.name) !== undefined ?.options?.find((o) => o.name === e.name) !== undefined
: true : true
const subMatched = const subMatched =
e.group === undefined && e.parent !== undefined e.group === undefined && e.parent !== undefined
? i.options.find( ? i.options.find(
(o) => (o) =>
o.name === e.name && o.name === e.name &&
o.type === SlashCommandOptionType.SUB_COMMAND o.type === SlashCommandOptionType.SUB_COMMAND
) !== undefined ) !== undefined
: true : true
const nameMatched1 = e.name === i.name const nameMatched1 = e.name === i.name
const parentMatched = hasGroupOrParent ? e.parent === i.name : true const parentMatched = hasGroupOrParent ? e.parent === i.name : true
@ -533,7 +532,9 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
if (cmd === undefined) return if (cmd === undefined) return
await this.emit('interaction', interaction) await this.emit('interaction', interaction)
try { await cmd.handler(interaction) } catch (e) { try {
await cmd.handler(interaction)
} catch (e) {
await this.emit('interactionError', e) await this.emit('interactionError', e)
} }
} }
@ -548,10 +549,8 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
throw new Error('Public Key is not present') throw new Error('Public Key is not present')
const fullBody = new Uint8Array([ const fullBody = new Uint8Array([
...(typeof timestamp === 'string' ...(typeof timestamp === 'string' ? encodeText(timestamp) : timestamp),
? encoder.encode(timestamp) ...(typeof rawBody === 'string' ? encodeText(rawBody) : rawBody)
: timestamp),
...(typeof rawBody === 'string' ? encoder.encode(rawBody) : rawBody)
]) ])
return edverify(signature, fullBody, this.publicKey).catch(() => false) return edverify(signature, fullBody, this.publicKey).catch(() => false)
@ -561,7 +560,7 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
async verifyServerRequest(req: { async verifyServerRequest(req: {
headers: Headers headers: Headers
method: string method: string
body: Deno.Reader | Uint8Array, body: Deno.Reader | Uint8Array
respond: (options: { respond: (options: {
status?: number status?: number
headers?: Headers headers?: Headers
@ -574,12 +573,13 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
const timestamp = req.headers.get('x-signature-timestamp') const timestamp = req.headers.get('x-signature-timestamp')
if (signature === null || timestamp === null) return false if (signature === null || timestamp === null) return false
const rawbody = req.body instanceof Uint8Array ? req.body : await Deno.readAll(req.body) const rawbody =
req.body instanceof Uint8Array ? req.body : await Deno.readAll(req.body)
const verify = await this.verifyKey(rawbody, signature, timestamp) const verify = await this.verifyKey(rawbody, signature, timestamp)
if (!verify) return false if (!verify) return false
try { try {
const payload: InteractionPayload = JSON.parse(decoder.decode(rawbody)) const payload: InteractionPayload = JSON.parse(decodeText(rawbody))
// TODO: Maybe fix all this hackery going on here? // TODO: Maybe fix all this hackery going on here?
const res = new Interaction(this as any, payload, { const res = new Interaction(this as any, payload, {
@ -600,7 +600,8 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
await req.respond({ await req.respond({
status: 200, status: 200,
headers: new Headers({ headers: new Headers({
'content-type': d instanceof FormData ? 'multipart/form-data' : 'application/json' 'content-type':
d instanceof FormData ? 'multipart/form-data' : 'application/json'
}), }),
body: d instanceof FormData ? d : JSON.stringify(d) body: d instanceof FormData ? d : JSON.stringify(d)
}) })
@ -612,7 +613,13 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
} }
/** Verify FetchEvent (for Service Worker usage) and return Interaction if valid */ /** Verify FetchEvent (for Service Worker usage) and return Interaction if valid */
async verifyFetchEvent({ request: req, respondWith }: { respondWith: CallableFunction, request: Request }): Promise<false | Interaction> { async verifyFetchEvent({
request: req,
respondWith
}: {
respondWith: CallableFunction
request: Request
}): Promise<false | Interaction> {
if (req.bodyUsed === true) throw new Error('Request Body already used') if (req.bodyUsed === true) throw new Error('Request Body already used')
if (req.body === null) return false if (req.body === null) return false
const body = (await req.body.getReader().read()).value const body = (await req.body.getReader().read()).value
@ -623,11 +630,13 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> {
body, body,
method: req.method, method: req.method,
respond: async (options) => { respond: async (options) => {
await respondWith(new Response(options.body, { await respondWith(
headers: options.headers, new Response(options.body, {
status: options.status, headers: options.headers,
})) status: options.status
}, })
)
}
}) })
} }

View file

@ -21,6 +21,7 @@ import { MessageReactionsManager } from '../managers/messageReactions.ts'
import { MessageSticker } from './messageSticker.ts' import { MessageSticker } from './messageSticker.ts'
import { Emoji } from './emoji.ts' import { Emoji } from './emoji.ts'
import { InteractionType } from '../types/slash.ts' import { InteractionType } from '../types/slash.ts'
import { encodeText } from '../utils/encoding.ts'
type AllMessageOptions = MessageOptions | Embed type AllMessageOptions = MessageOptions | Embed
@ -217,8 +218,6 @@ export class Message extends SnowflakeBase {
} }
} }
const encoder = new TextEncoder()
/** Message Attachment that can be sent while Creating Message */ /** Message Attachment that can be sent while Creating Message */
export class MessageAttachment { export class MessageAttachment {
name: string name: string
@ -228,7 +227,7 @@ export class MessageAttachment {
this.name = name this.name = name
this.blob = this.blob =
typeof blob === 'string' typeof blob === 'string'
? new Blob([encoder.encode(blob)]) ? new Blob([encodeText(blob)])
: blob instanceof Uint8Array : blob instanceof Uint8Array
? new Blob([blob]) ? new Blob([blob])
: blob : blob

10
src/utils/encoding.ts Normal file
View file

@ -0,0 +1,10 @@
const encoder = new TextEncoder()
const decoder = new TextDecoder('utf-8')
export function encodeText(str: string): Uint8Array {
return encoder.encode(str)
}
export function decodeText(bytes: Uint8Array): string {
return decoder.decode(bytes)
}