Github online editor FTW
This commit is contained in:
commit
a14104ad14
29 changed files with 759 additions and 159 deletions
19
README.md
19
README.md
|
@ -10,15 +10,12 @@
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
- Lightweight and easy to use.
|
- Lightweight and easy to use.
|
||||||
- Built-in Command Framework,
|
- Complete Object-Oriented approach.
|
||||||
- Easily build Commands on the fly.
|
- Slash Commands supported.
|
||||||
- Completely Customizable.
|
- Built-in Commands framework.
|
||||||
- Complete Object-Oriented approach.
|
- Customizable Caching, with Redis support.
|
||||||
- 100% Discord API Coverage.
|
- Use `@decorators` to easily make things!
|
||||||
- Customizable caching.
|
- Made with ❤️ TypeScript.
|
||||||
- Built in support for Redis.
|
|
||||||
- Write Custom Cache Adapters.
|
|
||||||
- Complete TypeScript support.
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
@ -102,13 +99,14 @@ client.connect('super secret token comes here', Intents.All)
|
||||||
```
|
```
|
||||||
|
|
||||||
Or with Decorators!
|
Or with Decorators!
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {
|
import {
|
||||||
Client,
|
Client,
|
||||||
event,
|
event,
|
||||||
Intents,
|
Intents,
|
||||||
command,
|
command,
|
||||||
CommandContext,
|
CommandContext
|
||||||
} from 'https://deno.land/x/harmony/mod.ts'
|
} from 'https://deno.land/x/harmony/mod.ts'
|
||||||
|
|
||||||
class MyClient extends CommandClient {
|
class MyClient extends CommandClient {
|
||||||
|
@ -141,6 +139,7 @@ Documentation is available for `main` (branch) and `stable` (release).
|
||||||
|
|
||||||
- [Main](https://doc.deno.land/https/raw.githubusercontent.com/harmony-org/harmony/main/mod.ts)
|
- [Main](https://doc.deno.land/https/raw.githubusercontent.com/harmony-org/harmony/main/mod.ts)
|
||||||
- [Stable](https://doc.deno.land/https/deno.land/x/harmony/mod.ts)
|
- [Stable](https://doc.deno.land/https/deno.land/x/harmony/mod.ts)
|
||||||
|
- [Guide](https://harmony-org.github.io)
|
||||||
|
|
||||||
## Found a bug or want support? Join our discord server!
|
## Found a bug or want support? Join our discord server!
|
||||||
|
|
||||||
|
|
13
deps.ts
Normal file
13
deps.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export { EventEmitter } from 'https://deno.land/std@0.82.0/node/events.ts'
|
||||||
|
export { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts'
|
||||||
|
export { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts'
|
||||||
|
export { parse } from 'https://deno.land/x/mutil@0.1.2/mod.ts'
|
||||||
|
export { connect } from 'https://deno.land/x/redis@v0.14.1/mod.ts'
|
||||||
|
export type {
|
||||||
|
Redis,
|
||||||
|
RedisConnectOptions
|
||||||
|
} from 'https://deno.land/x/redis@v0.14.1/mod.ts'
|
||||||
|
export {
|
||||||
|
Manager,
|
||||||
|
Player
|
||||||
|
} from 'https://raw.githubusercontent.com/Lavaclient/lavadeno/master/mod.ts'
|
3
mod.ts
3
mod.ts
|
@ -1,5 +1,4 @@
|
||||||
export { GatewayIntents } from './src/types/gateway.ts'
|
export { GatewayIntents } from './src/types/gateway.ts'
|
||||||
export { default as EventEmitter } from 'https://deno.land/std@0.74.0/node/events.ts'
|
|
||||||
export { Base } from './src/structures/base.ts'
|
export { Base } from './src/structures/base.ts'
|
||||||
export { Gateway } from './src/gateway/index.ts'
|
export { Gateway } from './src/gateway/index.ts'
|
||||||
export type { ClientEvents } from './src/gateway/handlers/index.ts'
|
export type { ClientEvents } from './src/gateway/handlers/index.ts'
|
||||||
|
@ -66,7 +65,7 @@ export {
|
||||||
ActivityTypes
|
ActivityTypes
|
||||||
} from './src/structures/presence.ts'
|
} from './src/structures/presence.ts'
|
||||||
export { Role } from './src/structures/role.ts'
|
export { Role } from './src/structures/role.ts'
|
||||||
export { Snowflake } from './src/structures/snowflake.ts'
|
export { Snowflake } from './src/utils/snowflake.ts'
|
||||||
export { TextChannel, GuildTextChannel } from './src/structures/textChannel.ts'
|
export { TextChannel, GuildTextChannel } from './src/structures/textChannel.ts'
|
||||||
export { MessageReaction } from './src/structures/messageReaction.ts'
|
export { MessageReaction } from './src/structures/messageReaction.ts'
|
||||||
export { User } from './src/structures/user.ts'
|
export { User } from './src/structures/user.ts'
|
||||||
|
|
|
@ -17,23 +17,29 @@ export const guildEmojiUpdate: GatewayEventHandler = async (
|
||||||
const _updated: EmojiPayload[] = []
|
const _updated: EmojiPayload[] = []
|
||||||
|
|
||||||
for (const raw of d.emojis) {
|
for (const raw of d.emojis) {
|
||||||
const has = emojis.get(raw.id)
|
const emojiID = raw.id !== null ? raw.id : raw.name
|
||||||
|
const has = emojis.get(emojiID)
|
||||||
if (has === undefined) {
|
if (has === undefined) {
|
||||||
await guild.emojis.set(raw.id, raw)
|
await guild.emojis.set(emojiID, raw)
|
||||||
const emoji = (await guild.emojis.get(raw.id)) as Emoji
|
const emoji = (await guild.emojis.get(emojiID)) as Emoji
|
||||||
added.push(emoji)
|
added.push(emoji)
|
||||||
} else _updated.push(raw)
|
} else _updated.push(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const emoji of emojis.values()) {
|
for (const emoji of emojis.values()) {
|
||||||
const find = _updated.find((e) => emoji.id === e.id)
|
const emojiID = emoji.id !== null ? emoji.id : emoji.name
|
||||||
|
const find = _updated.find((e) => {
|
||||||
|
const eID = e.id !== null ? e.id : e.name
|
||||||
|
return emojiID === eID
|
||||||
|
})
|
||||||
if (find === undefined) {
|
if (find === undefined) {
|
||||||
await guild.emojis.delete(emoji.id)
|
await guild.emojis.delete(emojiID)
|
||||||
deleted.push(emoji)
|
deleted.push(emoji)
|
||||||
} else {
|
} else {
|
||||||
const before = (await guild.emojis.get(find.id)) as Emoji
|
const foundID = find.id !== null ? find.id : find.name
|
||||||
await guild.emojis.set(find.id, find)
|
const before = (await guild.emojis.get(foundID)) as Emoji
|
||||||
const after = (await guild.emojis.get(find.id)) as Emoji
|
await guild.emojis.set(foundID, find)
|
||||||
|
const after = (await guild.emojis.get(foundID)) as Emoji
|
||||||
updated.push({ before, after })
|
updated.push({ before, after })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,16 @@ export const messageReactionAdd: GatewayEventHandler = async (
|
||||||
} else return
|
} else return
|
||||||
}
|
}
|
||||||
|
|
||||||
let reaction = await message.reactions.get(d.emoji.id)
|
const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name
|
||||||
|
let reaction = await message.reactions.get(emojiID)
|
||||||
if (reaction === undefined) {
|
if (reaction === undefined) {
|
||||||
await message.reactions.set(d.emoji.id, {
|
await message.reactions.set(emojiID, {
|
||||||
count: 1,
|
count: 1,
|
||||||
emoji: d.emoji,
|
emoji: d.emoji,
|
||||||
me: d.user_id === gateway.client.user?.id
|
me: d.user_id === gateway.client.user?.id
|
||||||
})
|
})
|
||||||
reaction = ((await message.reactions.get(
|
reaction = ((await message.reactions.get(
|
||||||
d.emoji.id
|
emojiID
|
||||||
)) as unknown) as MessageReaction
|
)) as unknown) as MessageReaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ export const messageReactionRemove: GatewayEventHandler = async (
|
||||||
} else return
|
} else return
|
||||||
}
|
}
|
||||||
|
|
||||||
const reaction = await message.reactions.get(d.emoji.id)
|
const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name
|
||||||
|
const reaction = await message.reactions.get(emojiID)
|
||||||
if (reaction === undefined) return
|
if (reaction === undefined) return
|
||||||
|
|
||||||
reaction.users.delete(d.user_id)
|
reaction.users.delete(d.user_id)
|
||||||
|
|
|
@ -19,7 +19,8 @@ export const messageReactionRemoveEmoji: GatewayEventHandler = async (
|
||||||
} else return
|
} else return
|
||||||
}
|
}
|
||||||
|
|
||||||
const reaction = await message.reactions.get(d.emoji.id)
|
const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name
|
||||||
|
const reaction = await message.reactions.get(emojiID)
|
||||||
if (reaction === undefined) return
|
if (reaction === undefined) return
|
||||||
|
|
||||||
await reaction.users.flush()
|
await reaction.users.flush()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts'
|
import { unzlib, EventEmitter } from '../../deps.ts'
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
DISCORD_GATEWAY_URL,
|
DISCORD_GATEWAY_URL,
|
||||||
|
@ -18,7 +18,6 @@ import { GatewayCache } from '../managers/gatewayCache.ts'
|
||||||
import { delay } from '../utils/delay.ts'
|
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 EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts'
|
|
||||||
|
|
||||||
export interface RequestMembersOptions {
|
export interface RequestMembersOptions {
|
||||||
limit?: number
|
limit?: number
|
||||||
|
@ -177,46 +176,51 @@ export class Gateway extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onclose(event: CloseEvent): Promise<void> {
|
private async onclose({ reason, code }: CloseEvent): Promise<void> {
|
||||||
if (event.reason === RECONNECT_REASON) return
|
if (reason === RECONNECT_REASON) return
|
||||||
this.emit('close', event.code, event.reason)
|
this.emit('close', code, reason)
|
||||||
this.debug(`Connection Closed with code: ${event.code}`)
|
this.debug(`Connection Closed with code: ${code}`)
|
||||||
|
|
||||||
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
switch (code) {
|
||||||
|
case GatewayCloseCodes.UNKNOWN_ERROR:
|
||||||
this.debug('API has encountered Unknown Error. Reconnecting...')
|
this.debug('API has encountered Unknown Error. Reconnecting...')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.reconnect()
|
this.reconnect()
|
||||||
} else if (event.code === GatewayCloseCodes.UNKNOWN_OPCODE) {
|
break
|
||||||
|
case GatewayCloseCodes.UNKNOWN_OPCODE:
|
||||||
throw new Error("Unknown OP Code was sent. This shouldn't happen!")
|
throw new Error("Unknown OP Code was sent. This shouldn't happen!")
|
||||||
} else if (event.code === GatewayCloseCodes.DECODE_ERROR) {
|
case GatewayCloseCodes.DECODE_ERROR:
|
||||||
throw new Error("Invalid Payload was sent. This shouldn't happen!")
|
throw new Error("Invalid Payload was sent. This shouldn't happen!")
|
||||||
} else if (event.code === GatewayCloseCodes.NOT_AUTHENTICATED) {
|
case GatewayCloseCodes.NOT_AUTHENTICATED:
|
||||||
throw new Error('Not Authorized: Payload was sent before Identifying.')
|
throw new Error('Not Authorized: Payload was sent before Identifying.')
|
||||||
} else if (event.code === GatewayCloseCodes.AUTHENTICATION_FAILED) {
|
case GatewayCloseCodes.AUTHENTICATION_FAILED:
|
||||||
throw new Error('Invalid Token provided!')
|
throw new Error('Invalid Token provided!')
|
||||||
} else if (event.code === GatewayCloseCodes.INVALID_SEQ) {
|
case GatewayCloseCodes.INVALID_SEQ:
|
||||||
this.debug('Invalid Seq was sent. Reconnecting.')
|
this.debug('Invalid Seq was sent. Reconnecting.')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.reconnect()
|
this.reconnect()
|
||||||
} else if (event.code === GatewayCloseCodes.RATE_LIMITED) {
|
break
|
||||||
|
case GatewayCloseCodes.RATE_LIMITED:
|
||||||
throw new Error("You're ratelimited. Calm down.")
|
throw new Error("You're ratelimited. Calm down.")
|
||||||
} else if (event.code === GatewayCloseCodes.SESSION_TIMED_OUT) {
|
case GatewayCloseCodes.SESSION_TIMED_OUT:
|
||||||
this.debug('Session Timeout. Reconnecting.')
|
this.debug('Session Timeout. Reconnecting.')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.reconnect(true)
|
this.reconnect(true)
|
||||||
} else if (event.code === GatewayCloseCodes.INVALID_SHARD) {
|
break
|
||||||
|
case GatewayCloseCodes.INVALID_SHARD:
|
||||||
this.debug('Invalid Shard was sent. Reconnecting.')
|
this.debug('Invalid Shard was sent. Reconnecting.')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.reconnect()
|
this.reconnect()
|
||||||
} else if (event.code === GatewayCloseCodes.SHARDING_REQUIRED) {
|
break
|
||||||
|
case GatewayCloseCodes.SHARDING_REQUIRED:
|
||||||
throw new Error("Couldn't connect. Sharding is required!")
|
throw new Error("Couldn't connect. Sharding is required!")
|
||||||
} else if (event.code === GatewayCloseCodes.INVALID_API_VERSION) {
|
case GatewayCloseCodes.INVALID_API_VERSION:
|
||||||
throw new Error("Invalid API Version was used. This shouldn't happen!")
|
throw new Error("Invalid API Version was used. This shouldn't happen!")
|
||||||
} else if (event.code === GatewayCloseCodes.INVALID_INTENTS) {
|
case GatewayCloseCodes.INVALID_INTENTS:
|
||||||
throw new Error('Invalid Intents')
|
throw new Error('Invalid Intents')
|
||||||
} else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) {
|
case GatewayCloseCodes.DISALLOWED_INTENTS:
|
||||||
throw new Error("Given Intents aren't allowed")
|
throw new Error("Given Intents aren't allowed")
|
||||||
} else {
|
default:
|
||||||
this.debug(
|
this.debug(
|
||||||
'Unknown Close code, probably connection error. Reconnecting in 5s.'
|
'Unknown Close code, probably connection error. Reconnecting in 5s.'
|
||||||
)
|
)
|
||||||
|
@ -226,6 +230,7 @@ export class Gateway extends EventEmitter {
|
||||||
}
|
}
|
||||||
await delay(5000)
|
await delay(5000)
|
||||||
await this.reconnect(true)
|
await this.reconnect(true)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { EmojiPayload } from '../types/emoji.ts'
|
||||||
import { CHANNEL, GUILD_EMOJI, GUILD_EMOJIS } from '../types/endpoint.ts'
|
import { CHANNEL, GUILD_EMOJI, GUILD_EMOJIS } from '../types/endpoint.ts'
|
||||||
import { BaseChildManager } from './baseChild.ts'
|
import { BaseChildManager } from './baseChild.ts'
|
||||||
import { EmojisManager } from './emojis.ts'
|
import { EmojisManager } from './emojis.ts'
|
||||||
import { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts'
|
import { fetchAuto } from '../../deps.ts'
|
||||||
|
|
||||||
export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> {
|
export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> {
|
||||||
guild: Guild
|
guild: Guild
|
||||||
|
@ -87,7 +87,8 @@ export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> {
|
||||||
async flush(): Promise<boolean> {
|
async flush(): Promise<boolean> {
|
||||||
const arr = await this.array()
|
const arr = await this.array()
|
||||||
for (const elem of arr) {
|
for (const elem of arr) {
|
||||||
this.parent.delete(elem.id)
|
const emojiID = elem.id !== null ? elem.id : elem.name
|
||||||
|
this.parent.delete(emojiID)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ export class MessageReactionsManager extends BaseManager<
|
||||||
const raw = await this._get(id)
|
const raw = await this._get(id)
|
||||||
if (raw === undefined) return
|
if (raw === undefined) return
|
||||||
|
|
||||||
let emoji = await this.client.emojis.get(raw.emoji.id)
|
const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name
|
||||||
|
|
||||||
|
let emoji = await this.client.emojis.get(emojiID)
|
||||||
if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji)
|
if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji)
|
||||||
|
|
||||||
const reaction = new MessageReaction(this.client, raw, this.message, emoji)
|
const reaction = new MessageReaction(this.client, raw, this.message, emoji)
|
||||||
|
@ -43,7 +45,8 @@ export class MessageReactionsManager extends BaseManager<
|
||||||
|
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
arr.map(async (raw) => {
|
arr.map(async (raw) => {
|
||||||
let emoji = await this.client.emojis.get(raw.emoji.id)
|
const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name
|
||||||
|
let emoji = await this.client.emojis.get(emojiID)
|
||||||
if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji)
|
if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji)
|
||||||
|
|
||||||
return new MessageReaction(this.client, raw, this.message, emoji)
|
return new MessageReaction(this.client, raw, this.message, emoji)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
connect,
|
connect,
|
||||||
Redis,
|
Redis,
|
||||||
RedisConnectOptions
|
RedisConnectOptions
|
||||||
} from 'https://denopkg.com/keroxp/deno-redis/mod.ts'
|
} from '../../deps.ts'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ICacheAdapter is the interface to be implemented by Cache Adapters for them to be usable with Harmony.
|
* ICacheAdapter is the interface to be implemented by Cache Adapters for them to be usable with Harmony.
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { User } from '../structures/user.ts'
|
||||||
import { GatewayIntents } from '../types/gateway.ts'
|
import { GatewayIntents } from '../types/gateway.ts'
|
||||||
import { Gateway } from '../gateway/index.ts'
|
import { Gateway } from '../gateway/index.ts'
|
||||||
import { RESTManager } from './rest.ts'
|
import { RESTManager } from './rest.ts'
|
||||||
import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts'
|
import { EventEmitter } from '../../deps.ts'
|
||||||
import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts'
|
import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts'
|
||||||
import { UsersManager } from '../managers/users.ts'
|
import { UsersManager } from '../managers/users.ts'
|
||||||
import { GuildManager } from '../managers/guilds.ts'
|
import { GuildManager } from '../managers/guilds.ts'
|
||||||
|
@ -247,6 +247,7 @@ export function event(name?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decorator to create a Slash Command handler */
|
||||||
export function slash(name?: string, guild?: string) {
|
export function slash(name?: string, guild?: string) {
|
||||||
return function (client: Client | SlashModule, prop: string) {
|
return function (client: Client | SlashModule, prop: string) {
|
||||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||||
|
@ -262,6 +263,7 @@ export function slash(name?: string, guild?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decorator to create a Sub-Slash Command handler */
|
||||||
export function subslash(parent: string, name?: string, guild?: string) {
|
export function subslash(parent: string, name?: string, guild?: string) {
|
||||||
return function (client: Client | SlashModule, prop: string) {
|
return function (client: Client | SlashModule, prop: string) {
|
||||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||||
|
@ -279,13 +281,14 @@ export function subslash(parent: string, name?: string, guild?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decorator to create a Grouped Slash Command handler */
|
||||||
export function groupslash(
|
export function groupslash(
|
||||||
parent: string,
|
parent: string,
|
||||||
group: string,
|
group: string,
|
||||||
name?: string,
|
name?: string,
|
||||||
guild?: string
|
guild?: string
|
||||||
) {
|
) {
|
||||||
return function (client: Client | SlashModule, prop: string) {
|
return function (client: Client | SlashModule | SlashClient, prop: string) {
|
||||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||||
const item = (client as { [name: string]: any })[prop]
|
const item = (client as { [name: string]: any })[prop]
|
||||||
if (typeof item !== 'function') {
|
if (typeof item !== 'function') {
|
||||||
|
@ -303,6 +306,7 @@ export function groupslash(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decorator to add a Slash Module to Client */
|
||||||
export function slashModule() {
|
export function slashModule() {
|
||||||
return function (client: Client, prop: string) {
|
return function (client: Client, prop: string) {
|
||||||
if (client._decoratedSlashModules === undefined)
|
if (client._decoratedSlashModules === undefined)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { User } from '../structures/user.ts'
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
import { CommandClient } from './commandClient.ts'
|
import { CommandClient } from './commandClient.ts'
|
||||||
import { Extension } from './extensions.ts'
|
import { Extension } from './extensions.ts'
|
||||||
import { parse } from 'https://deno.land/x/mutil@0.1.2/mod.ts'
|
import { parse } from '../../deps.ts'
|
||||||
|
|
||||||
export interface CommandContext {
|
export interface CommandContext {
|
||||||
/** The Client object */
|
/** The Client object */
|
||||||
|
|
|
@ -97,6 +97,7 @@ export interface RESTOptions {
|
||||||
token?: string
|
token?: string
|
||||||
headers?: { [name: string]: string | undefined }
|
headers?: { [name: string]: string | undefined }
|
||||||
canary?: boolean
|
canary?: boolean
|
||||||
|
version?: 6 | 7 | 8
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RESTManager {
|
export class RESTManager {
|
||||||
|
@ -111,6 +112,7 @@ export class RESTManager {
|
||||||
constructor(client?: RESTOptions) {
|
constructor(client?: RESTOptions) {
|
||||||
this.client = client
|
this.client = client
|
||||||
this.api = builder(this)
|
this.api = builder(this)
|
||||||
|
if (client?.version !== undefined) this.version = client.version
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.handleRateLimits()
|
this.handleRateLimits()
|
||||||
}
|
}
|
||||||
|
@ -408,6 +410,7 @@ export class RESTManager {
|
||||||
const query =
|
const query =
|
||||||
method === 'get' && body !== undefined
|
method === 'get' && body !== undefined
|
||||||
? Object.entries(body as any)
|
? Object.entries(body as any)
|
||||||
|
.filter(([k, v]) => v !== undefined)
|
||||||
.map(
|
.map(
|
||||||
([key, value]) =>
|
([key, value]) =>
|
||||||
`${encodeURIComponent(key)}=${encodeURIComponent(
|
`${encodeURIComponent(key)}=${encodeURIComponent(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
import { Client, ClientOptions } from './client.ts'
|
import { Client, ClientOptions } from './client.ts'
|
||||||
import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts'
|
import {EventEmitter} from '../../deps.ts'
|
||||||
import { RESTManager } from './rest.ts'
|
import { RESTManager } from './rest.ts'
|
||||||
// import { GATEWAY_BOT } from '../types/endpoint.ts'
|
// import { GATEWAY_BOT } from '../types/endpoint.ts'
|
||||||
// import { GatewayBotPayload } from '../types/gatewayBot.ts'
|
// import { GatewayBotPayload } from '../types/gatewayBot.ts'
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
import { Guild } from '../structures/guild.ts'
|
import { Guild } from '../structures/guild.ts'
|
||||||
import { Interaction } from '../structures/slash.ts'
|
import { Interaction } from '../structures/slash.ts'
|
||||||
import {
|
|
||||||
APPLICATION_COMMAND,
|
|
||||||
APPLICATION_COMMANDS,
|
|
||||||
APPLICATION_GUILD_COMMAND,
|
|
||||||
APPLICATION_GUILD_COMMANDS
|
|
||||||
} from '../types/endpoint.ts'
|
|
||||||
import {
|
import {
|
||||||
InteractionType,
|
InteractionType,
|
||||||
|
SlashCommandChoice,
|
||||||
SlashCommandOption,
|
SlashCommandOption,
|
||||||
|
SlashCommandOptionType,
|
||||||
SlashCommandPartial,
|
SlashCommandPartial,
|
||||||
SlashCommandPayload
|
SlashCommandPayload
|
||||||
} from '../types/slash.ts'
|
} from '../types/slash.ts'
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
import { Client } from './client.ts'
|
import { Client } from './client.ts'
|
||||||
import { RESTManager } from './rest.ts'
|
import { RESTManager } from './rest.ts'
|
||||||
|
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'
|
||||||
|
import {
|
||||||
|
Request as ORequest,
|
||||||
|
Response as OResponse
|
||||||
|
} from 'https://deno.land/x/opine@1.0.0/src/types.ts'
|
||||||
|
import { Context } from 'https://deno.land/x/oak@v6.4.0/mod.ts'
|
||||||
|
|
||||||
export class SlashCommand {
|
export class SlashCommand {
|
||||||
slash: SlashCommandsManager
|
slash: SlashCommandsManager
|
||||||
|
@ -41,6 +45,158 @@ export class SlashCommand {
|
||||||
async edit(data: SlashCommandPartial): Promise<void> {
|
async edit(data: SlashCommandPartial): Promise<void> {
|
||||||
await this.slash.edit(this.id, data, this._guild)
|
await this.slash.edit(this.id, data, this._guild)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) =>
|
||||||
|
typeof e === 'function' ? e(SlashOption) : e
|
||||||
|
),
|
||||||
|
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
|
||||||
|
export class SlashOption {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SlashOptionCallable = (o: typeof SlashOption) => SlashCommandOption
|
||||||
|
|
||||||
|
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)
|
||||||
|
? options.map((op) => (typeof op === 'function' ? op(SlashOption) : op))
|
||||||
|
: Object.entries(options).map((entry) =>
|
||||||
|
typeof entry[1] === 'function'
|
||||||
|
? entry[1](SlashOption)
|
||||||
|
: 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(
|
||||||
|
typeof option === 'function' ? option(SlashOption) : option
|
||||||
|
)
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SlashCommandsManager {
|
export class SlashCommandsManager {
|
||||||
|
@ -58,9 +214,9 @@ export class SlashCommandsManager {
|
||||||
async all(): Promise<Collection<string, SlashCommand>> {
|
async all(): Promise<Collection<string, SlashCommand>> {
|
||||||
const col = new Collection<string, SlashCommand>()
|
const col = new Collection<string, SlashCommand>()
|
||||||
|
|
||||||
const res = (await this.rest.get(
|
const res = (await this.rest.api.applications[
|
||||||
APPLICATION_COMMANDS(this.slash.getID())
|
this.slash.getID()
|
||||||
)) as SlashCommandPayload[]
|
].commands.get()) as SlashCommandPayload[]
|
||||||
if (!Array.isArray(res)) return col
|
if (!Array.isArray(res)) return col
|
||||||
|
|
||||||
for (const raw of res) {
|
for (const raw of res) {
|
||||||
|
@ -77,12 +233,9 @@ export class SlashCommandsManager {
|
||||||
): Promise<Collection<string, SlashCommand>> {
|
): Promise<Collection<string, SlashCommand>> {
|
||||||
const col = new Collection<string, SlashCommand>()
|
const col = new Collection<string, SlashCommand>()
|
||||||
|
|
||||||
const res = (await this.rest.get(
|
const res = (await this.rest.api.applications[this.slash.getID()].guilds[
|
||||||
APPLICATION_GUILD_COMMANDS(
|
|
||||||
this.slash.getID(),
|
|
||||||
typeof guild === 'string' ? guild : guild.id
|
typeof guild === 'string' ? guild : guild.id
|
||||||
)
|
].commands.get()) as SlashCommandPayload[]
|
||||||
)) as SlashCommandPayload[]
|
|
||||||
if (!Array.isArray(res)) return col
|
if (!Array.isArray(res)) return col
|
||||||
|
|
||||||
for (const raw of res) {
|
for (const raw of res) {
|
||||||
|
@ -99,15 +252,14 @@ export class SlashCommandsManager {
|
||||||
data: SlashCommandPartial,
|
data: SlashCommandPartial,
|
||||||
guild?: Guild | string
|
guild?: Guild | string
|
||||||
): Promise<SlashCommand> {
|
): Promise<SlashCommand> {
|
||||||
const payload = await this.rest.post(
|
const route =
|
||||||
guild === undefined
|
guild === undefined
|
||||||
? APPLICATION_COMMANDS(this.slash.getID())
|
? this.rest.api.applications[this.slash.getID()].commands
|
||||||
: APPLICATION_GUILD_COMMANDS(
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
||||||
this.slash.getID(),
|
|
||||||
typeof guild === 'string' ? guild : guild.id
|
typeof guild === 'string' ? guild : guild.id
|
||||||
),
|
].commands
|
||||||
data
|
|
||||||
)
|
const payload = await route.post(data)
|
||||||
|
|
||||||
const cmd = new SlashCommand(this, payload)
|
const cmd = new SlashCommand(this, payload)
|
||||||
cmd._guild =
|
cmd._guild =
|
||||||
|
@ -122,16 +274,14 @@ export class SlashCommandsManager {
|
||||||
data: SlashCommandPartial,
|
data: SlashCommandPartial,
|
||||||
guild?: Guild | string
|
guild?: Guild | string
|
||||||
): Promise<SlashCommandsManager> {
|
): Promise<SlashCommandsManager> {
|
||||||
await this.rest.patch(
|
const route =
|
||||||
guild === undefined
|
guild === undefined
|
||||||
? APPLICATION_COMMAND(this.slash.getID(), id)
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
||||||
: APPLICATION_GUILD_COMMAND(
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
||||||
this.slash.getID(),
|
typeof guild === 'string' ? guild : guild.id
|
||||||
typeof guild === 'string' ? guild : guild.id,
|
].commands[id]
|
||||||
id
|
|
||||||
),
|
await route.patch(data)
|
||||||
data
|
|
||||||
)
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,29 +290,28 @@ export class SlashCommandsManager {
|
||||||
id: string,
|
id: string,
|
||||||
guild?: Guild | string
|
guild?: Guild | string
|
||||||
): Promise<SlashCommandsManager> {
|
): Promise<SlashCommandsManager> {
|
||||||
await this.rest.delete(
|
const route =
|
||||||
guild === undefined
|
guild === undefined
|
||||||
? APPLICATION_COMMAND(this.slash.getID(), id)
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
||||||
: APPLICATION_GUILD_COMMAND(
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
||||||
this.slash.getID(),
|
typeof guild === 'string' ? guild : guild.id
|
||||||
typeof guild === 'string' ? guild : guild.id,
|
].commands[id]
|
||||||
id
|
|
||||||
)
|
await route.delete()
|
||||||
)
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a Slash Command (global or Guild) */
|
/** Get a Slash Command (global or Guild) */
|
||||||
async get(id: string, guild?: Guild | string): Promise<SlashCommand> {
|
async get(id: string, guild?: Guild | string): Promise<SlashCommand> {
|
||||||
const data = await this.rest.get(
|
const route =
|
||||||
guild === undefined
|
guild === undefined
|
||||||
? APPLICATION_COMMAND(this.slash.getID(), id)
|
? this.rest.api.applications[this.slash.getID()].commands[id]
|
||||||
: APPLICATION_GUILD_COMMAND(
|
: this.rest.api.applications[this.slash.getID()].guilds[
|
||||||
this.slash.getID(),
|
typeof guild === 'string' ? guild : guild.id
|
||||||
typeof guild === 'string' ? guild : guild.id,
|
].commands[id]
|
||||||
id
|
|
||||||
)
|
const data = await route.get()
|
||||||
)
|
|
||||||
return new SlashCommand(this, data)
|
return new SlashCommand(this, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,6 +331,7 @@ export interface SlashOptions {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
token?: string
|
token?: string
|
||||||
rest?: RESTManager
|
rest?: RESTManager
|
||||||
|
publicKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SlashClient {
|
export class SlashClient {
|
||||||
|
@ -192,6 +342,18 @@ export class SlashClient {
|
||||||
commands: SlashCommandsManager
|
commands: SlashCommandsManager
|
||||||
handlers: SlashCommandHandler[] = []
|
handlers: SlashCommandHandler[] = []
|
||||||
rest: RESTManager
|
rest: RESTManager
|
||||||
|
modules: SlashModule[] = []
|
||||||
|
publicKey?: string
|
||||||
|
|
||||||
|
_decoratedSlash?: Array<{
|
||||||
|
name: string
|
||||||
|
guild?: string
|
||||||
|
parent?: string
|
||||||
|
group?: string
|
||||||
|
handler: (interaction: Interaction) => any
|
||||||
|
}>
|
||||||
|
|
||||||
|
_decoratedSlashModules?: SlashModule[]
|
||||||
|
|
||||||
constructor(options: SlashOptions) {
|
constructor(options: SlashOptions) {
|
||||||
let id = options.id
|
let id = options.id
|
||||||
|
@ -202,6 +364,7 @@ export class SlashClient {
|
||||||
this.client = options.client
|
this.client = options.client
|
||||||
this.token = options.token
|
this.token = options.token
|
||||||
this.commands = new SlashCommandsManager(this)
|
this.commands = new SlashCommandsManager(this)
|
||||||
|
this.publicKey = options.publicKey
|
||||||
|
|
||||||
if (options !== undefined) {
|
if (options !== undefined) {
|
||||||
this.enabled = options.enabled ?? true
|
this.enabled = options.enabled ?? true
|
||||||
|
@ -213,6 +376,24 @@ export class SlashClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.client?._decoratedSlashModules !== undefined) {
|
||||||
|
this.client._decoratedSlashModules.forEach((e) => {
|
||||||
|
this.modules.push(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._decoratedSlash !== undefined) {
|
||||||
|
this._decoratedSlash.forEach((e) => {
|
||||||
|
this.handlers.push(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._decoratedSlashModules !== undefined) {
|
||||||
|
this._decoratedSlashModules.forEach((e) => {
|
||||||
|
this.modules.push(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.rest =
|
this.rest =
|
||||||
options.client === undefined
|
options.client === undefined
|
||||||
? options.rest === undefined
|
? options.rest === undefined
|
||||||
|
@ -237,8 +418,28 @@ export class SlashClient {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadModule(module: SlashModule): SlashClient {
|
||||||
|
this.modules.push(module)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getHandlers(): SlashCommandHandler[] {
|
||||||
|
let res = this.handlers
|
||||||
|
for (const mod of this.modules) {
|
||||||
|
if (mod === undefined) continue
|
||||||
|
res = [
|
||||||
|
...res,
|
||||||
|
...mod.commands.map((cmd) => {
|
||||||
|
cmd.handler = cmd.handler.bind(mod)
|
||||||
|
return cmd
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
|
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
|
||||||
return this.handlers.find((e) => {
|
return this.getHandlers().find((e) => {
|
||||||
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
|
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
|
||||||
const groupMatched =
|
const groupMatched =
|
||||||
e.group !== undefined && e.parent !== undefined
|
e.group !== undefined && e.parent !== undefined
|
||||||
|
@ -271,4 +472,78 @@ export class SlashClient {
|
||||||
|
|
||||||
cmd.handler(interaction)
|
cmd.handler(interaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,14 @@ export class Emoji extends Base {
|
||||||
available?: boolean
|
available?: boolean
|
||||||
|
|
||||||
get getEmojiString(): string {
|
get getEmojiString(): string {
|
||||||
|
if (this.id === null) {
|
||||||
|
return this.name as string;
|
||||||
|
} else {
|
||||||
if (this.animated === false) {
|
if (this.animated === false) {
|
||||||
return `<:${this.name}:${this.id}>`
|
return `<:${this.name}:${this.id}>`
|
||||||
} else return `<a:${this.name}:${this.id}>`
|
} else return `<a:${this.name}:${this.id}>`
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return this.getEmojiString
|
return this.getEmojiString
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import { ChannelPayload } from '../types/channel.ts'
|
import { ChannelPayload } from '../types/channel.ts'
|
||||||
|
import { INVITE } from '../types/endpoint.ts'
|
||||||
import { GuildPayload } from '../types/guild.ts'
|
import { GuildPayload } from '../types/guild.ts'
|
||||||
import { InvitePayload } from '../types/invite.ts'
|
import { InvitePayload } from '../types/invite.ts'
|
||||||
import { UserPayload } from '../types/user.ts'
|
import { UserPayload } from '../types/user.ts'
|
||||||
|
@ -31,6 +32,12 @@ export class Invite extends Base {
|
||||||
this.approximatePresenceCount = data.approximate_presence_count
|
this.approximatePresenceCount = data.approximate_presence_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Delete an invite. Requires the MANAGE_CHANNELS permission on the channel this invite belongs to, or MANAGE_GUILD to remove any invite across the guild. Returns an invite object on success. Fires a Invite Delete Gateway event. */
|
||||||
|
async delete(): Promise<Invite> {
|
||||||
|
const res = await this.client.rest.delete(INVITE(this.code))
|
||||||
|
return new Invite(this.client, res)
|
||||||
|
}
|
||||||
|
|
||||||
readFromData(data: InvitePayload): void {
|
readFromData(data: InvitePayload): void {
|
||||||
this.code = data.code ?? this.code
|
this.code = data.code ?? this.code
|
||||||
this.guild = data.guild ?? this.guild
|
this.guild = data.guild ?? this.guild
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { TextChannel } from './textChannel.ts'
|
||||||
import { Guild } from './guild.ts'
|
import { Guild } from './guild.ts'
|
||||||
import { MessageReactionsManager } from '../managers/messageReactions.ts'
|
import { MessageReactionsManager } from '../managers/messageReactions.ts'
|
||||||
import { MessageSticker } from './messageSticker.ts'
|
import { MessageSticker } from './messageSticker.ts'
|
||||||
|
import { Emoji } from './emoji.ts'
|
||||||
|
|
||||||
type AllMessageOptions = MessageOption | Embed
|
type AllMessageOptions = MessageOption | Embed
|
||||||
|
|
||||||
|
@ -46,6 +47,10 @@ export class Message extends Base {
|
||||||
flags?: number
|
flags?: number
|
||||||
stickers?: MessageSticker[]
|
stickers?: MessageSticker[]
|
||||||
|
|
||||||
|
get createdAt(): Date {
|
||||||
|
return new Date(this.timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
client: Client,
|
client: Client,
|
||||||
data: MessagePayload,
|
data: MessagePayload,
|
||||||
|
@ -108,25 +113,52 @@ export class Message extends Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Edits this message. */
|
/** Edits this message. */
|
||||||
async edit(text?: string, option?: MessageOption): Promise<Message> {
|
async edit(
|
||||||
|
content?: string | AllMessageOptions,
|
||||||
|
option?: AllMessageOptions
|
||||||
|
): Promise<Message> {
|
||||||
|
if (typeof content === 'object') {
|
||||||
|
option = content
|
||||||
|
content = undefined
|
||||||
|
}
|
||||||
|
if (content === undefined && option === undefined) {
|
||||||
|
throw new Error('Either text or option is necessary.')
|
||||||
|
}
|
||||||
|
if (option instanceof Embed) {
|
||||||
|
option = {
|
||||||
|
embed: option
|
||||||
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.client.user !== undefined &&
|
this.client.user !== undefined &&
|
||||||
this.author.id !== this.client.user?.id
|
this.author.id !== this.client.user?.id
|
||||||
)
|
) {
|
||||||
throw new Error("Cannot edit other users' messages")
|
throw new Error("Cannot edit other users' messages")
|
||||||
return this.channel.editMessage(this.id, text, option)
|
}
|
||||||
|
return this.channel.editMessage(this.id, content, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a Reply to this Message. */
|
/** Creates a Reply to this Message. */
|
||||||
async reply(
|
async reply(
|
||||||
text?: string | AllMessageOptions,
|
content?: string | AllMessageOptions,
|
||||||
option?: AllMessageOptions
|
option?: AllMessageOptions
|
||||||
): Promise<Message> {
|
): Promise<Message> {
|
||||||
return this.channel.send(text, option, this)
|
return this.channel.send(content, option, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deletes the Message. */
|
/** Deletes the Message. */
|
||||||
async delete(): Promise<void> {
|
async delete(): Promise<void> {
|
||||||
return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id))
|
return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addReaction(emoji: string | Emoji): Promise<void> {
|
||||||
|
return this.channel.addReaction(this, emoji)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeReaction(
|
||||||
|
emoji: string | Emoji,
|
||||||
|
user?: User | Member | string
|
||||||
|
): Promise<void> {
|
||||||
|
return this.channel.removeReaction(this, emoji, user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
70
src/structures/template.ts
Normal file
70
src/structures/template.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { Client } from '../models/client.ts'
|
||||||
|
import { TEMPLATE } from '../types/endpoint.ts'
|
||||||
|
import { TemplatePayload } from '../types/template.ts'
|
||||||
|
import { Base } from './base.ts'
|
||||||
|
import { Guild } from './guild.ts'
|
||||||
|
import { User } from './user.ts'
|
||||||
|
|
||||||
|
export class Template extends Base {
|
||||||
|
/** The template code (unique ID) */
|
||||||
|
code: string
|
||||||
|
/** The template name */
|
||||||
|
name: string
|
||||||
|
/** The description for the template */
|
||||||
|
description: string | null
|
||||||
|
/** Number of times this template has been used */
|
||||||
|
usageCount: number
|
||||||
|
/** The ID of the user who created the template */
|
||||||
|
creatorID: string
|
||||||
|
/** The user who created the template */
|
||||||
|
creator: User
|
||||||
|
/** When this template was created (in ms) */
|
||||||
|
createdAt: number
|
||||||
|
/** When this template was last synced to the source guild (in ms) */
|
||||||
|
updatedAt: number
|
||||||
|
/** The ID of the guild this template is based on */
|
||||||
|
sourceGuildID: string
|
||||||
|
/** The guild snapshot this template contains */
|
||||||
|
serializedSourceGuild: Guild
|
||||||
|
/** Whether the template has unsynced changes */
|
||||||
|
isDirty: boolean | null
|
||||||
|
|
||||||
|
constructor(client: Client, data: TemplatePayload) {
|
||||||
|
super(client, data)
|
||||||
|
this.code = data.code
|
||||||
|
this.name = data.name
|
||||||
|
this.description = data.description
|
||||||
|
this.usageCount = data.usage_count
|
||||||
|
this.creatorID = data.creator_id
|
||||||
|
this.creator = new User(client, data.creator)
|
||||||
|
this.createdAt = Date.parse(data.created_at)
|
||||||
|
this.updatedAt = Date.parse(data.updated_at)
|
||||||
|
this.sourceGuildID = data.source_guild_id
|
||||||
|
this.serializedSourceGuild = new Guild(client, data.serialized_source_guild)
|
||||||
|
this.isDirty = Boolean(data.is_dirty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Modifies the template's metadata. Requires the MANAGE_GUILD permission. Returns the template object on success. */
|
||||||
|
async edit(data: ModifyGuildTemplateParams): Promise<Template> {
|
||||||
|
const res = await this.client.rest.patch(TEMPLATE(this.code), data)
|
||||||
|
return new Template(this.client, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deletes the template. Requires the MANAGE_GUILD permission. Returns the deleted template object on success. */
|
||||||
|
async delete(): Promise<Template> {
|
||||||
|
const res = await this.client.rest.delete(TEMPLATE(this.code))
|
||||||
|
return new Template(this.client, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Syncs the template to the guild's current state. Requires the MANAGE_GUILD permission. Returns the template object on success. */
|
||||||
|
async sync(): Promise<Template> {
|
||||||
|
const res = await this.client.rest.put(TEMPLATE(this.code))
|
||||||
|
return new Template(this.client, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** https://discord.com/developers/docs/resources/template#modify-guild-template-json-params */
|
||||||
|
export interface ModifyGuildTemplateParams {
|
||||||
|
name?: string
|
||||||
|
description?: string | null
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
GuildTextChannelPayload,
|
GuildTextChannelPayload,
|
||||||
MessageOption,
|
MessageOption,
|
||||||
|
MessagePayload,
|
||||||
MessageReference,
|
MessageReference,
|
||||||
ModifyGuildTextChannelOption,
|
ModifyGuildTextChannelOption,
|
||||||
ModifyGuildTextChannelPayload,
|
ModifyGuildTextChannelPayload,
|
||||||
|
@ -12,12 +13,18 @@ import {
|
||||||
import {
|
import {
|
||||||
CHANNEL,
|
CHANNEL,
|
||||||
CHANNEL_MESSAGE,
|
CHANNEL_MESSAGE,
|
||||||
CHANNEL_MESSAGES
|
CHANNEL_MESSAGES,
|
||||||
|
MESSAGE_REACTION_ME,
|
||||||
|
MESSAGE_REACTION_USER
|
||||||
} from '../types/endpoint.ts'
|
} from '../types/endpoint.ts'
|
||||||
|
import { Collection } from '../utils/collection.ts'
|
||||||
import { Channel } from './channel.ts'
|
import { Channel } from './channel.ts'
|
||||||
import { Embed } from './embed.ts'
|
import { Embed } from './embed.ts'
|
||||||
|
import { Emoji } from './emoji.ts'
|
||||||
import { Guild } from './guild.ts'
|
import { Guild } from './guild.ts'
|
||||||
|
import { Member } from './member.ts'
|
||||||
import { Message } from './message.ts'
|
import { Message } from './message.ts'
|
||||||
|
import { User } from './user.ts'
|
||||||
|
|
||||||
export type AllMessageOptions = MessageOption | Embed
|
export type AllMessageOptions = MessageOption | Embed
|
||||||
|
|
||||||
|
@ -41,29 +48,30 @@ export class TextChannel extends Channel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param text Text content of the Message to send.
|
* @param content Text content of the Message to send.
|
||||||
* @param option Various other Message options.
|
* @param option Various other Message options.
|
||||||
* @param reply Reference to a Message object to reply-to.
|
* @param reply Reference to a Message object to reply-to.
|
||||||
*/
|
*/
|
||||||
async send(
|
async send(
|
||||||
text?: string | AllMessageOptions,
|
content?: string | AllMessageOptions,
|
||||||
option?: AllMessageOptions,
|
option?: AllMessageOptions,
|
||||||
reply?: Message
|
reply?: Message
|
||||||
): Promise<Message> {
|
): Promise<Message> {
|
||||||
if (typeof text === 'object') {
|
if (typeof content === 'object') {
|
||||||
option = text
|
option = content
|
||||||
text = undefined
|
content = undefined
|
||||||
}
|
}
|
||||||
if (text === undefined && option === undefined) {
|
if (content === undefined && option === undefined) {
|
||||||
throw new Error('Either text or option is necessary.')
|
throw new Error('Either text or option is necessary.')
|
||||||
}
|
}
|
||||||
if (option instanceof Embed)
|
if (option instanceof Embed) {
|
||||||
option = {
|
option = {
|
||||||
embed: option
|
embed: option
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
content: text,
|
content: content,
|
||||||
embed: option?.embed,
|
embed: option?.embed,
|
||||||
file: option?.file,
|
file: option?.file,
|
||||||
tts: option?.tts,
|
tts: option?.tts,
|
||||||
|
@ -124,6 +132,96 @@ export class TextChannel extends Channel {
|
||||||
await res.mentions.fromPayload(newMsg)
|
await res.mentions.fromPayload(newMsg)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addReaction(
|
||||||
|
message: Message | string,
|
||||||
|
emoji: Emoji | string
|
||||||
|
): Promise<void> {
|
||||||
|
if (emoji instanceof Emoji) {
|
||||||
|
emoji = emoji.getEmojiString
|
||||||
|
}
|
||||||
|
if (message instanceof Message) {
|
||||||
|
message = message.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodedEmoji = encodeURI(emoji)
|
||||||
|
|
||||||
|
await this.client.rest.put(
|
||||||
|
MESSAGE_REACTION_ME(this.id, message, encodedEmoji)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeReaction(
|
||||||
|
message: Message | string,
|
||||||
|
emoji: Emoji | string,
|
||||||
|
user?: User | Member | string
|
||||||
|
): Promise<void> {
|
||||||
|
if (emoji instanceof Emoji) {
|
||||||
|
emoji = emoji.getEmojiString
|
||||||
|
}
|
||||||
|
if (message instanceof Message) {
|
||||||
|
message = message.id
|
||||||
|
}
|
||||||
|
if (user !== undefined) {
|
||||||
|
if (typeof user !== 'string') {
|
||||||
|
user = user.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodedEmoji = encodeURI(emoji)
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
await this.client.rest.delete(
|
||||||
|
MESSAGE_REACTION_ME(this.id, message, encodedEmoji)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await this.client.rest.delete(
|
||||||
|
MESSAGE_REACTION_USER(this.id, message, encodedEmoji, user)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch Messages of a Channel
|
||||||
|
* @param options Options to configure fetching Messages
|
||||||
|
*/
|
||||||
|
async fetchMessages(options?: {
|
||||||
|
limit?: number
|
||||||
|
around?: Message | string
|
||||||
|
before?: Message | string
|
||||||
|
after?: Message | string
|
||||||
|
}): Promise<Collection<string, Message>> {
|
||||||
|
const res = new Collection<string, Message>()
|
||||||
|
const raws = (await this.client.rest.api.channels[this.id].messages.get({
|
||||||
|
limit: options?.limit ?? 50,
|
||||||
|
around:
|
||||||
|
options?.around === undefined
|
||||||
|
? undefined
|
||||||
|
: typeof options.around === 'string'
|
||||||
|
? options.around
|
||||||
|
: options.around.id,
|
||||||
|
before:
|
||||||
|
options?.before === undefined
|
||||||
|
? undefined
|
||||||
|
: typeof options.before === 'string'
|
||||||
|
? options.before
|
||||||
|
: options.before.id,
|
||||||
|
after:
|
||||||
|
options?.after === undefined
|
||||||
|
? undefined
|
||||||
|
: typeof options.after === 'string'
|
||||||
|
? options.after
|
||||||
|
: options.after.id
|
||||||
|
})) as MessagePayload[]
|
||||||
|
|
||||||
|
for (const raw of raws) {
|
||||||
|
await this.messages.set(raw.id, raw)
|
||||||
|
const msg = ((await this.messages.get(raw.id)) as unknown) as Message
|
||||||
|
res.set(msg.id, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GuildTextChannel extends TextChannel {
|
export class GuildTextChannel extends TextChannel {
|
||||||
|
@ -185,4 +283,40 @@ export class GuildTextChannel extends TextChannel {
|
||||||
|
|
||||||
return new GuildTextChannel(this.client, resp, this.guild)
|
return new GuildTextChannel(this.client, resp, this.guild)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk Delete Messages in a Guild Text Channel
|
||||||
|
* @param messages Messages to delete. Can be a number, or Array of Message or IDs
|
||||||
|
*/
|
||||||
|
async bulkDelete(
|
||||||
|
messages: Array<Message | string> | number
|
||||||
|
): Promise<GuildTextChannel> {
|
||||||
|
let ids: string[] = []
|
||||||
|
|
||||||
|
if (Array.isArray(messages))
|
||||||
|
ids = messages.map((e) => (typeof e === 'string' ? e : e.id))
|
||||||
|
else {
|
||||||
|
let list = await this.messages.array()
|
||||||
|
if (list.length < messages) list = (await this.fetchMessages()).array()
|
||||||
|
ids = list
|
||||||
|
.sort((b, a) => a.createdAt.getTime() - b.createdAt.getTime())
|
||||||
|
.filter((e, i) => i < messages)
|
||||||
|
.filter(
|
||||||
|
(e) =>
|
||||||
|
new Date().getTime() - e.createdAt.getTime() <=
|
||||||
|
1000 * 60 * 60 * 24 * 14
|
||||||
|
)
|
||||||
|
.map((e) => e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = [...new Set(ids)]
|
||||||
|
if (ids.length < 2 || ids.length > 100)
|
||||||
|
throw new Error('bulkDelete can only delete messages in range 2-100')
|
||||||
|
|
||||||
|
await this.client.rest.api.channels[this.id].messages['bulk-delete'].post({
|
||||||
|
messages: ids
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Embed } from './embed.ts'
|
||||||
import { Message } from './message.ts'
|
import { Message } from './message.ts'
|
||||||
import { TextChannel } from './textChannel.ts'
|
import { TextChannel } from './textChannel.ts'
|
||||||
import { User } from './user.ts'
|
import { User } from './user.ts'
|
||||||
import { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts'
|
import { fetchAuto } from '../../deps.ts'
|
||||||
import { WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
import { WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
||||||
|
|
||||||
export interface WebhookMessageOptions extends MessageOption {
|
export interface WebhookMessageOptions extends MessageOption {
|
||||||
|
|
|
@ -112,6 +112,17 @@ client.on('messageCreate', async (msg: Message) => {
|
||||||
} else {
|
} else {
|
||||||
msg.channel.send('Failed...')
|
msg.channel.send('Failed...')
|
||||||
}
|
}
|
||||||
|
} else if (msg.content === '!react') {
|
||||||
|
msg.addReaction('🤔')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('messageReactionRemove', (reaction, user) => {
|
||||||
|
const msg = reaction.message
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||||
|
if (reaction.me && reaction.emoji.getEmojiString === '🤔') {
|
||||||
|
msg.removeReaction(reaction.emoji)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,14 @@ import {
|
||||||
groupslash,
|
groupslash,
|
||||||
CommandContext,
|
CommandContext,
|
||||||
Extension,
|
Extension,
|
||||||
Collection
|
Collection,
|
||||||
|
GuildTextChannel
|
||||||
} from '../../mod.ts'
|
} from '../../mod.ts'
|
||||||
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
||||||
import {
|
import {
|
||||||
Manager,
|
Manager,
|
||||||
Player
|
Player
|
||||||
} from 'https://raw.githubusercontent.com/Lavaclient/lavadeno/master/mod.ts'
|
} from '../../deps.ts'
|
||||||
import { Interaction } from '../structures/slash.ts'
|
import { Interaction } from '../structures/slash.ts'
|
||||||
import { slash } from '../models/client.ts'
|
import { slash } from '../models/client.ts'
|
||||||
// import { SlashCommandOptionType } from '../types/slash.ts'
|
// import { SlashCommandOptionType } from '../types/slash.ts'
|
||||||
|
@ -69,6 +70,17 @@ class MyClient extends CommandClient {
|
||||||
d.respond({ content: 'sub-cmd-group worked' })
|
d.respond({ content: 'sub-cmd-group worked' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@command()
|
||||||
|
rmrf(ctx: CommandContext): any {
|
||||||
|
if (ctx.author.id !== '422957901716652033') return
|
||||||
|
;((ctx.channel as any) as GuildTextChannel)
|
||||||
|
.bulkDelete(3)
|
||||||
|
.then((chan) => {
|
||||||
|
ctx.channel.send(`Bulk deleted 2 in ${chan}`)
|
||||||
|
})
|
||||||
|
.catch((e) => ctx.channel.send(`${e.message}`))
|
||||||
|
}
|
||||||
|
|
||||||
@slash()
|
@slash()
|
||||||
run(d: Interaction): void {
|
run(d: Interaction): void {
|
||||||
console.log(d.name)
|
console.log(d.name)
|
||||||
|
@ -205,6 +217,10 @@ class VCExtension extends Extension {
|
||||||
|
|
||||||
const client = new MyClient()
|
const client = new MyClient()
|
||||||
|
|
||||||
|
client.on('raw', (e, d) => {
|
||||||
|
if (e === 'GUILD_MEMBER_ADD' || e === 'GUILD_MEMBER_UPDATE') console.log(e, d)
|
||||||
|
})
|
||||||
|
|
||||||
client.extensions.load(VCExtension)
|
client.extensions.load(VCExtension)
|
||||||
|
|
||||||
client.connect(TOKEN, Intents.None)
|
client.connect(TOKEN, Intents.All)
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
import { SlashClient } from '../models/slashClient.ts'
|
import { SlashClient } from '../models/slashClient.ts'
|
||||||
|
import { SlashCommandPartial } from '../types/slash.ts'
|
||||||
import { TOKEN } from './config.ts'
|
import { TOKEN } from './config.ts'
|
||||||
|
|
||||||
const slash = new SlashClient({ token: TOKEN })
|
export const slash = new SlashClient({ token: TOKEN })
|
||||||
|
|
||||||
slash.commands.all().then(console.log)
|
// Cmd objects come here
|
||||||
|
const commands: SlashCommandPartial[] = []
|
||||||
|
|
||||||
|
console.log('Creating...')
|
||||||
|
commands.forEach((cmd) => {
|
||||||
|
slash.commands
|
||||||
|
.create(cmd, '!! Your testing guild ID comes here !!')
|
||||||
|
.then((c) => console.log(`Created command ${c.name}!`))
|
||||||
|
.catch((e) => `Failed to create ${cmd.name} - ${e.message}`)
|
||||||
|
})
|
||||||
|
|
|
@ -62,6 +62,7 @@ export interface MemberPayload {
|
||||||
premium_since?: string
|
premium_since?: string
|
||||||
deaf: boolean
|
deaf: boolean
|
||||||
mute: boolean
|
mute: boolean
|
||||||
|
pending?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MessageNotification {
|
export enum MessageNotification {
|
||||||
|
@ -113,6 +114,9 @@ export type GuildFeatures =
|
||||||
| 'FEATURABLE'
|
| 'FEATURABLE'
|
||||||
| 'ANIMATED_ICON'
|
| 'ANIMATED_ICON'
|
||||||
| 'BANNER'
|
| 'BANNER'
|
||||||
|
| 'WELCOME_SCREEN_ENABLED'
|
||||||
|
| 'MEMBER_VERIFICATION_GATE_ENABLED'
|
||||||
|
| 'PREVIEW_ENABLED'
|
||||||
|
|
||||||
export enum IntegrationExpireBehavior {
|
export enum IntegrationExpireBehavior {
|
||||||
REMOVE_ROLE = 0,
|
REMOVE_ROLE = 0,
|
||||||
|
|
|
@ -50,7 +50,7 @@ export interface SlashCommandChoice {
|
||||||
/** (Display) name of the Choice */
|
/** (Display) name of the Choice */
|
||||||
name: string
|
name: string
|
||||||
/** Actual value to be sent in Interaction */
|
/** Actual value to be sent in Interaction */
|
||||||
value: string
|
value: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SlashCommandOptionType {
|
export enum SlashCommandOptionType {
|
||||||
|
@ -66,7 +66,8 @@ export enum SlashCommandOptionType {
|
||||||
|
|
||||||
export interface SlashCommandOption {
|
export interface SlashCommandOption {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
/** Description not required in Sub-Command or Sub-Command-Group */
|
||||||
|
description?: string
|
||||||
type: SlashCommandOptionType
|
type: SlashCommandOptionType
|
||||||
required?: boolean
|
required?: boolean
|
||||||
default?: boolean
|
default?: boolean
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { UserPayload } from './user.ts'
|
||||||
export interface TemplatePayload {
|
export interface TemplatePayload {
|
||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
description: string | undefined
|
description: string | null
|
||||||
usage_count: number
|
usage_count: number
|
||||||
creator_id: string
|
creator_id: string
|
||||||
creator: UserPayload
|
creator: UserPayload
|
||||||
|
@ -12,5 +12,5 @@ export interface TemplatePayload {
|
||||||
updated_at: string
|
updated_at: string
|
||||||
source_guild_id: string
|
source_guild_id: string
|
||||||
serialized_source_guild: GuildPayload
|
serialized_source_guild: GuildPayload
|
||||||
is_dirty: boolean | undefined
|
is_dirty: boolean | null
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue