diff --git a/README.md b/README.md
index c987f5c..2b4d26b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
An easy to use Discord API Library for Deno
-
+
@@ -156,7 +156,7 @@ Documentation is available for `main` (branch) and `stable` (release).
## Found a bug or want support? Join our discord server!
-[![Widget for the Discord Server](https://discord.com/api/guilds/783319033205751809/widget.png?style=banner1)](https://discord.gg/harmonyland)
+[![Widget for the Discord Server](https://discord.com/api/guilds/783319033205751809/widget.png?style=banner1)](https://discord.gg/harmony)
## Maintainer
diff --git a/deploy.ts b/deploy.ts
index 7684b41..9e5ad9a 100644
--- a/deploy.ts
+++ b/deploy.ts
@@ -1,15 +1,20 @@
+import { Interaction } from './src/structures/interactions.ts '
import {
SlashCommandsManager,
SlashClient,
SlashCommandHandlerCallback,
SlashCommandHandler
} from './src/interactions/mod.ts'
-import { InteractionResponseType, InteractionType } from './src/types/slash.ts'
+import {
+ InteractionResponseType,
+ InteractionType
+} from './src/types/interactions.ts'
export interface DeploySlashInitOptions {
env?: boolean
publicKey?: string
token?: string
+ path?: string
}
/** Current Slash Client being used to handle commands */
@@ -37,8 +42,12 @@ let commands: SlashCommandsManager
*
* @param options Initialization options
*/
-export function init(options: { env: boolean }): void
-export function init(options: { publicKey: string; token?: string }): void
+export function init(options: { env: boolean; path?: string }): void
+export function init(options: {
+ publicKey: string
+ token?: string
+ path?: string
+}): void
export function init(options: DeploySlashInitOptions): void {
if (client !== undefined) throw new Error('Already initialized')
if (options.env === true) {
@@ -60,6 +69,9 @@ export function init(options: DeploySlashInitOptions): void {
respondWith: CallableFunction
request: Request
}): Promise => {
+ if (options.path !== undefined) {
+ if (new URL(evt.request.url).pathname !== options.path) return
+ }
try {
// we have to wrap because there are some weird scope errors
const d = await client.verifyFetchEvent({
@@ -124,8 +136,16 @@ export function handle(
client.handle(cmd, handler)
}
+export function interactions(cb: (i: Interaction) => any): void {
+ client.on('interaction', cb)
+}
+
export { commands, client }
-export * from './src/types/slash.ts'
+export * from './src/types/slashCommands.ts'
+export * from './src/types/interactions.ts'
export * from './src/structures/slash.ts'
export * from './src/interactions/mod.ts'
export * from './src/types/channel.ts'
+export * from './src/structures/interactions.ts'
+export * from './src/structures/message.ts'
+export * from './src/structures/embed.ts'
diff --git a/mod.ts b/mod.ts
index f2a0842..901d22a 100644
--- a/mod.ts
+++ b/mod.ts
@@ -39,7 +39,9 @@ export { GuildChannelsManager } from './src/managers/guildChannels.ts'
export { GuildManager } from './src/managers/guilds.ts'
export * from './src/structures/base.ts'
export * from './src/structures/slash.ts'
-export * from './src/types/slash.ts'
+export * from './src/structures/interactions.ts'
+export * from './src/types/slashCommands.ts'
+export * from './src/types/interactions.ts'
export { GuildEmojisManager } from './src/managers/guildEmojis.ts'
export { MembersManager } from './src/managers/members.ts'
export { MessageReactionsManager } from './src/managers/messageReactions.ts'
@@ -191,3 +193,5 @@ export {
isVoiceChannel,
default as getChannelByType
} from './src/utils/channel.ts'
+export * from './src/utils/interactions.ts'
+export * from "./src/utils/command.ts"
diff --git a/src/commands/client.ts b/src/commands/client.ts
index 22b0117..9442f4f 100644
--- a/src/commands/client.ts
+++ b/src/commands/client.ts
@@ -9,6 +9,7 @@ import {
CommandsManager,
parseCommand
} from './command.ts'
+import { parseArgs } from '../utils/command.ts'
import { Extension, ExtensionsManager } from './extension.ts'
type PrefixReturnType = string | string[] | Promise
@@ -239,7 +240,7 @@ export class CommandClient extends Client implements CommandClientOptions {
client: this,
name: parsed.name,
prefix,
- args: parsed.args,
+ args: parseArgs(command.args, parsed.args),
argString: parsed.argString,
message: msg,
author: msg.author,
diff --git a/src/commands/command.ts b/src/commands/command.ts
index 987eaee..8601d70 100644
--- a/src/commands/command.ts
+++ b/src/commands/command.ts
@@ -6,7 +6,7 @@ import { Collection } from '../utils/collection.ts'
import type { CommandClient } from './client.ts'
import type { Extension } from './extension.ts'
import { join, walk } from '../../deps.ts'
-
+import type { Args } from '../utils/command.ts'
export interface CommandContext {
/** The Client object */
client: CommandClient
@@ -23,7 +23,7 @@ export interface CommandContext {
/** Name of Command which was used */
name: string
/** Array of Arguments used with Command */
- args: string[]
+ args: Record | null
/** Complete Raw String of Arguments */
argString: string
/** Guild which the command has called */
@@ -46,7 +46,7 @@ export interface CommandOptions {
/** Usage Example of Command, only Arguments (without Prefix and Name) */
examples?: string | string[]
/** Does the Command take Arguments? Maybe number of required arguments? Or list of arguments? */
- args?: number | boolean | string[]
+ args?: Args[]
/** Permissions(s) required by both User and Bot in order to use Command */
permissions?: string | string[]
/** Permission(s) required for using Command */
@@ -81,7 +81,7 @@ export class Command implements CommandOptions {
extension?: Extension
usage?: string | string[]
examples?: string | string[]
- args?: number | boolean | string[]
+ args?: Args[]
permissions?: string | string[]
userPermissions?: string | string[]
botPermissions?: string | string[]
diff --git a/src/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts
index 3d4c5d4..43a80bb 100644
--- a/src/gateway/handlers/interactionCreate.ts
+++ b/src/gateway/handlers/interactionCreate.ts
@@ -1,17 +1,31 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
import { Guild } from '../../structures/guild.ts'
import { Member } from '../../structures/member.ts'
import {
- Interaction,
InteractionApplicationCommandResolved,
- InteractionChannel
+ SlashCommandInteraction
} from '../../structures/slash.ts'
+import {
+ Interaction,
+ InteractionChannel
+} from '../../structures/interactions.ts'
import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts'
-import { InteractionPayload } from '../../types/slash.ts'
+import {
+ InteractionPayload,
+ InteractionType
+} from '../../types/interactions.ts'
import { UserPayload } from '../../types/user.ts'
import { Permissions } from '../../utils/permissions.ts'
import type { Gateway, GatewayEventHandler } from '../mod.ts'
import { User } from '../../structures/user.ts'
import { Role } from '../../structures/role.ts'
+import { RolePayload } from '../../types/role.ts'
+import {
+ InteractionApplicationCommandData,
+ InteractionChannelPayload
+} from '../../types/slashCommands.ts'
+import { Message } from '../../structures/message.ts'
+import { TextChannel } from '../../structures/textChannel.ts'
export const interactionCreate: GatewayEventHandler = async (
gateway: Gateway,
@@ -26,13 +40,22 @@ export const interactionCreate: GatewayEventHandler = async (
const guild =
d.guild_id === undefined
? undefined
- : await gateway.client.guilds.get(d.guild_id)
+ : (await gateway.client.guilds.get(d.guild_id)) ??
+ new Guild(gateway.client, { unavailable: true, id: d.guild_id } as any)
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)
+ ? (await guild?.members.get(d.member.user.id))! ??
+ new Member(
+ gateway.client,
+ d.member!,
+ new User(gateway.client, d.member.user),
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ guild!,
+ new Permissions(d.member.permissions)
+ )
: undefined
if (d.user !== undefined) await gateway.client.users.set(d.user.id, d.user)
const dmUser =
@@ -41,9 +64,9 @@ export const interactionCreate: GatewayEventHandler = async (
const user = member !== undefined ? member.user : dmUser
if (user === undefined) return
- const channel =
- (await gateway.client.channels.get(d.channel_id)) ??
- (await gateway.client.channels.fetch(d.channel_id))
+ const channel = await gateway.client.channels.get(
+ d.channel_id
+ )
const resolved: InteractionApplicationCommandResolved = {
users: {},
@@ -52,9 +75,11 @@ export const interactionCreate: GatewayEventHandler = async (
roles: {}
}
- if (d.data?.resolved !== undefined) {
- for (const [id, data] of Object.entries(d.data.resolved.users ?? {})) {
- await gateway.client.users.set(id, data)
+ if ((d.data as InteractionApplicationCommandData)?.resolved !== undefined) {
+ for (const [id, data] of Object.entries(
+ (d.data as any)?.resolved.users ?? {}
+ )) {
+ await gateway.client.users.set(id, data as UserPayload)
resolved.users[id] = ((await gateway.client.users.get(
id
)) as unknown) as User
@@ -62,49 +87,87 @@ export const interactionCreate: GatewayEventHandler = async (
resolved.users[id].member = resolved.members[id]
}
- for (const [id, data] of Object.entries(d.data.resolved.members ?? {})) {
+ for (const [id, data] of Object.entries(
+ (d.data as InteractionApplicationCommandData)?.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
+ (r) =>
+ ((data as any)?.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
+ ;(data as any).user = ((d.data as any).resolved.users?.[
+ id
+ ] as unknown) as UserPayload
resolved.members[id] = new Member(
gateway.client,
- data,
+ data as any,
resolved.users[id],
guild as Guild,
permissions
)
}
- for (const [id, data] of Object.entries(d.data.resolved.roles ?? {})) {
+ for (const [id, data] of Object.entries(
+ (d.data as InteractionApplicationCommandData).resolved?.roles ?? {}
+ )) {
if (guild !== undefined) {
- await guild.roles.set(id, data)
+ await guild.roles.set(id, data as RolePayload)
resolved.roles[id] = ((await guild.roles.get(id)) as unknown) as Role
} else {
resolved.roles[id] = new Role(
gateway.client,
- data,
+ data as any,
(guild as unknown) as Guild
)
}
}
- for (const [id, data] of Object.entries(d.data.resolved.channels ?? {})) {
- resolved.channels[id] = new InteractionChannel(gateway.client, data)
+ for (const [id, data] of Object.entries(
+ (d.data as InteractionApplicationCommandData).resolved?.channels ?? {}
+ )) {
+ resolved.channels[id] = new InteractionChannel(
+ gateway.client,
+ data as InteractionChannelPayload
+ )
}
}
- const interaction = new Interaction(gateway.client, d, {
- member,
- guild,
- channel,
- user,
- resolved
- })
+ let message: Message | undefined
+ if (d.message !== undefined) {
+ const channel = (await gateway.client.channels.get(
+ d.message.channel_id
+ ))!
+ message = new Message(
+ gateway.client,
+ d.message,
+ channel,
+ new User(gateway.client, d.message.author)
+ )
+ }
+
+ let interaction
+ if (d.type === InteractionType.APPLICATION_COMMAND) {
+ interaction = new SlashCommandInteraction(gateway.client, d, {
+ member,
+ guild,
+ channel,
+ user,
+ resolved
+ })
+ } else {
+ interaction = new Interaction(gateway.client, d, {
+ member,
+ guild,
+ channel,
+ user,
+ message
+ })
+ }
+
gateway.client.emit('interactionCreate', interaction)
}
diff --git a/src/gateway/handlers/mod.ts b/src/gateway/handlers/mod.ts
index 1c299f3..dc649e8 100644
--- a/src/gateway/handlers/mod.ts
+++ b/src/gateway/handlers/mod.ts
@@ -59,7 +59,8 @@ import type {
EveryTextChannelTypes
} from '../../utils/channel.ts'
import { interactionCreate } from './interactionCreate.ts'
-import type { Interaction } from '../../structures/slash.ts'
+import type { Interaction } from '../../structures/interactions.ts'
+import type { SlashCommandInteraction } from '../../structures/slash.ts'
import type { CommandContext } from '../../commands/command.ts'
import type { RequestMethods } from '../../rest/types.ts'
import type { PartialInvitePayload } from '../../types/invite.ts'
@@ -358,11 +359,12 @@ export type ClientEvents = {
* @param channel Channel of which Webhooks were updated
*/
webhooksUpdate: [guild: Guild, channel: GuildTextBasedChannel]
+
/**
* An Interaction was created
* @param interaction Created interaction object
*/
- interactionCreate: [interaction: Interaction]
+ interactionCreate: [interaction: Interaction | SlashCommandInteraction]
/**
* When debug message was made
diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts
index 02f60c1..5c6bdf1 100644
--- a/src/interactions/slashClient.ts
+++ b/src/interactions/slashClient.ts
@@ -1,13 +1,14 @@
import {
- Interaction,
+ SlashCommandInteraction,
InteractionApplicationCommandResolved
} from '../structures/slash.ts'
+import { Interaction } from '../structures/interactions.ts'
import {
InteractionPayload,
InteractionResponsePayload,
- InteractionType,
- SlashCommandOptionType
-} from '../types/slash.ts'
+ InteractionType
+} from '../types/interactions.ts'
+import { SlashCommandOptionType } from '../types/slashCommands.ts'
import type { Client } from '../client/mod.ts'
import { RESTManager } from '../rest/mod.ts'
import { SlashModule } from './slashModule.ts'
@@ -17,7 +18,9 @@ import { HarmonyEventEmitter } from '../utils/events.ts'
import { encodeText, decodeText } from '../utils/encoding.ts'
import { SlashCommandsManager } from './slashCommand.ts'
-export type SlashCommandHandlerCallback = (interaction: Interaction) => unknown
+export type SlashCommandHandlerCallback = (
+ interaction: SlashCommandInteraction
+) => unknown
export interface SlashCommandHandler {
name: string
guild?: string
@@ -185,7 +188,9 @@ export class SlashClient extends HarmonyEventEmitter {
}
/** Get Handler for an Interaction. Supports nested sub commands and sub command groups. */
- private _getCommand(i: Interaction): SlashCommandHandler | undefined {
+ private _getCommand(
+ i: SlashCommandInteraction
+ ): SlashCommandHandler | undefined {
return this.getHandlers().find((e) => {
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
const groupMatched =
@@ -216,28 +221,28 @@ export class SlashClient extends HarmonyEventEmitter {
}
/** Process an incoming Interaction */
- private async _process(interaction: Interaction): Promise {
+ private async _process(
+ interaction: Interaction | SlashCommandInteraction
+ ): Promise {
if (!this.enabled) return
- if (
- interaction.type !== InteractionType.APPLICATION_COMMAND ||
- interaction.data === undefined
- )
- return
+ if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
const cmd =
- this._getCommand(interaction) ??
+ this._getCommand(interaction as SlashCommandInteraction) ??
this.getHandlers().find((e) => e.name === '*')
if (cmd?.group !== undefined)
- interaction.data.options = interaction.data.options[0].options ?? []
+ (interaction as SlashCommandInteraction).data.options =
+ (interaction as SlashCommandInteraction).data.options[0].options ?? []
if (cmd?.parent !== undefined)
- interaction.data.options = interaction.data.options[0].options ?? []
+ (interaction as SlashCommandInteraction).data.options =
+ (interaction as SlashCommandInteraction).data.options[0].options ?? []
if (cmd === undefined) return
await this.emit('interaction', interaction)
try {
- await cmd.handler(interaction)
+ await cmd.handler(interaction as SlashCommandInteraction)
} catch (e) {
await this.emit('interactionError', e)
}
@@ -286,20 +291,31 @@ export class SlashClient extends HarmonyEventEmitter {
const payload: InteractionPayload = JSON.parse(decodeText(rawbody))
// 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: {}
- }
- })
+ let res
+ if (payload.type === InteractionType.APPLICATION_COMMAND) {
+ res = new SlashCommandInteraction(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 as any)
+ ?.resolved as unknown) as InteractionApplicationCommandResolved) ?? {
+ users: {},
+ members: {},
+ roles: {},
+ channels: {}
+ }
+ })
+ } else {
+ 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
+ })
+ }
res._httpRespond = async (d: InteractionResponsePayload | FormData) =>
await req.respond({
status: 200,
diff --git a/src/interactions/slashCommand.ts b/src/interactions/slashCommand.ts
index c05181c..faa5675 100644
--- a/src/interactions/slashCommand.ts
+++ b/src/interactions/slashCommand.ts
@@ -6,7 +6,7 @@ import {
SlashCommandOptionType,
SlashCommandPartial,
SlashCommandPayload
-} from '../types/slash.ts'
+} from '../types/slashCommands.ts'
import { Collection } from '../utils/collection.ts'
import type { SlashClient, SlashCommandHandlerCallback } from './slashClient.ts'
diff --git a/src/managers/_util.ts b/src/managers/_util.ts
index e7f0809..e69de29 100644
--- a/src/managers/_util.ts
+++ b/src/managers/_util.ts
@@ -1 +0,0 @@
-export {}
diff --git a/src/managers/channels.ts b/src/managers/channels.ts
index 6cb30bb..8056de3 100644
--- a/src/managers/channels.ts
+++ b/src/managers/channels.ts
@@ -12,8 +12,6 @@ import type {
import { CHANNEL } from '../types/endpoint.ts'
import getChannelByType from '../utils/channel.ts'
import { BaseManager } from './base.ts'
-// Deno is bugged
-import {} from './_util.ts'
export type AllMessageOptions = MessageOptions | Embed
diff --git a/src/rest/endpoints.ts b/src/rest/endpoints.ts
index ff14299..31c70ce 100644
--- a/src/rest/endpoints.ts
+++ b/src/rest/endpoints.ts
@@ -30,11 +30,11 @@ import type {
InviteWithMetadataPayload
} from '../types/invite.ts'
import type { RoleModifyPayload, RolePayload } from '../types/role.ts'
+import type { InteractionResponsePayload } from '../types/interactions.ts'
import type {
- InteractionResponsePayload,
SlashCommandPartial,
SlashCommandPayload
-} from '../types/slash.ts'
+} from '../types/slashCommands.ts'
import type { TemplatePayload } from '../types/template.ts'
import type { UserPayload } from '../types/user.ts'
import type { VoiceRegion } from '../types/voice.ts'
diff --git a/src/rest/error.ts b/src/rest/error.ts
index 3f4d4ed..1063f43 100644
--- a/src/rest/error.ts
+++ b/src/rest/error.ts
@@ -13,7 +13,7 @@ export class DiscordAPIError extends Error {
this.message =
typeof error === 'string'
? `${error} `
- : `\n${error.method.toUpperCase()} ${error.url.slice(7)} returned ${
+ : `\n${error.method.toUpperCase()} ${error.url} returned ${
error.status
}\n(${error.code ?? 'unknown'}) ${error.message}${
fmt.length === 0
diff --git a/src/rest/request.ts b/src/rest/request.ts
index 273eadb..ca26850 100644
--- a/src/rest/request.ts
+++ b/src/rest/request.ts
@@ -90,7 +90,7 @@ export class APIRequest {
)
form.append('payload_json', JSON.stringify(body))
body = form
- } else {
+ } else if (body !== undefined) {
contentType = 'application/json'
body = JSON.stringify(body)
}
diff --git a/src/structures/interactions.ts b/src/structures/interactions.ts
new file mode 100644
index 0000000..86001e9
--- /dev/null
+++ b/src/structures/interactions.ts
@@ -0,0 +1,348 @@
+import type { Client } from '../client/client.ts'
+import {
+ AllowedMentionsPayload,
+ ChannelTypes,
+ EmbedPayload,
+ MessageOptions
+} from '../types/channel.ts'
+import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
+import {
+ InteractionPayload,
+ InteractionResponseFlags,
+ InteractionResponsePayload,
+ InteractionResponseType,
+ InteractionType
+} from '../types/interactions.ts'
+import {
+ InteractionApplicationCommandData,
+ InteractionChannelPayload
+} from '../types/slashCommands.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 { GuildTextChannel } from './guildTextChannel.ts'
+import { Member } from './member.ts'
+import { Message } from './message.ts'
+import { TextChannel } from './textChannel.ts'
+import { User } from './user.ts'
+
+interface WebhookMessageOptions extends MessageOptions {
+ embeds?: Array