commit
4cbc2b344d
25 changed files with 289 additions and 74 deletions
|
@ -7,7 +7,7 @@ export const channelDelete: GatewayEventHandler = async (
|
||||||
) => {
|
) => {
|
||||||
const channel = await gateway.client.channels.get(d.id)
|
const channel = await gateway.client.channels.get(d.id)
|
||||||
if (channel !== undefined) {
|
if (channel !== undefined) {
|
||||||
await gateway.client.channels.delete(d.id)
|
await gateway.client.channels._delete(d.id)
|
||||||
gateway.client.emit('channelDelete', channel)
|
gateway.client.emit('channelDelete', channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,5 +30,5 @@ export const guildCreate: GatewayEventHandler = async (
|
||||||
if (hasGuild === undefined) {
|
if (hasGuild === undefined) {
|
||||||
// It wasn't lazy load, so emit event
|
// It wasn't lazy load, so emit event
|
||||||
gateway.client.emit('guildCreate', guild)
|
gateway.client.emit('guildCreate', guild)
|
||||||
}
|
} else gateway.client.emit('guildLoaded', guild)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const guildDelete: GatewayEventHandler = async (
|
||||||
await guild.channels.flush()
|
await guild.channels.flush()
|
||||||
await guild.roles.flush()
|
await guild.roles.flush()
|
||||||
await guild.presences.flush()
|
await guild.presences.flush()
|
||||||
await gateway.client.guilds.delete(d.id)
|
await gateway.client.guilds._delete(d.id)
|
||||||
|
|
||||||
gateway.client.emit('guildDelete', guild)
|
gateway.client.emit('guildDelete', guild)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const guildRoleDelete: GatewayEventHandler = async (
|
||||||
const role = await guild.roles.get(d.role_id)
|
const role = await guild.roles.get(d.role_id)
|
||||||
// Shouldn't happen either
|
// Shouldn't happen either
|
||||||
if (role === undefined) return
|
if (role === undefined) return
|
||||||
|
await guild.roles._delete(d.role_id)
|
||||||
|
|
||||||
gateway.client.emit('guildRoleDelete', role)
|
gateway.client.emit('guildRoleDelete', role)
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,11 @@ export interface ClientEvents {
|
||||||
* @param guild The new Guild object
|
* @param guild The new Guild object
|
||||||
*/
|
*/
|
||||||
guildCreate: [guild: Guild]
|
guildCreate: [guild: Guild]
|
||||||
|
/**
|
||||||
|
* A Guild was successfully loaded.
|
||||||
|
* @param guild The Guild object
|
||||||
|
*/
|
||||||
|
guildLoaded: [guild: Guild]
|
||||||
/**
|
/**
|
||||||
* A Guild in which Client was either deleted, or bot was kicked
|
* A Guild in which Client was either deleted, or bot was kicked
|
||||||
* @param guild The Guild object
|
* @param guild The Guild object
|
||||||
|
|
|
@ -19,7 +19,6 @@ export const inviteDelete: GatewayEventHandler = async (
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const cachedGuild = await gateway.client.guilds.get(d.guild_id!)
|
const cachedGuild = await gateway.client.guilds.get(d.guild_id!)
|
||||||
|
|
||||||
// TODO(DjDeveloperr): Make it support self-bots and make Guild not always defined
|
|
||||||
if (cachedInvite === undefined) {
|
if (cachedInvite === undefined) {
|
||||||
const uncachedInvite: PartialInvitePayload = {
|
const uncachedInvite: PartialInvitePayload = {
|
||||||
guild: (cachedGuild as unknown) as Guild,
|
guild: (cachedGuild as unknown) as Guild,
|
||||||
|
@ -28,7 +27,7 @@ export const inviteDelete: GatewayEventHandler = async (
|
||||||
}
|
}
|
||||||
return gateway.client.emit('inviteDeleteUncached', uncachedInvite)
|
return gateway.client.emit('inviteDeleteUncached', uncachedInvite)
|
||||||
} else {
|
} else {
|
||||||
await guild.invites.delete(d.code)
|
await guild.invites._delete(d.code)
|
||||||
gateway.client.emit('inviteDelete', cachedInvite)
|
gateway.client.emit('inviteDelete', cachedInvite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@ export const messageDelete: GatewayEventHandler = async (
|
||||||
const message = await channel.messages.get(d.id)
|
const message = await channel.messages.get(d.id)
|
||||||
if (message === undefined)
|
if (message === undefined)
|
||||||
return gateway.client.emit('messageDeleteUncached', d)
|
return gateway.client.emit('messageDeleteUncached', d)
|
||||||
await channel.messages.delete(d.id)
|
await channel.messages._delete(d.id)
|
||||||
gateway.client.emit('messageDelete', message)
|
gateway.client.emit('messageDelete', message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,9 @@ export class Gateway extends EventEmitter {
|
||||||
this.reconnect()
|
this.reconnect()
|
||||||
break
|
break
|
||||||
case GatewayCloseCodes.UNKNOWN_OPCODE:
|
case GatewayCloseCodes.UNKNOWN_OPCODE:
|
||||||
throw new Error("Unknown OP Code was sent. This shouldn't happen!")
|
throw new Error(
|
||||||
|
"Invalid OP Code or Payload was sent. This shouldn't happen!"
|
||||||
|
)
|
||||||
case 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!")
|
||||||
case GatewayCloseCodes.NOT_AUTHENTICATED:
|
case GatewayCloseCodes.NOT_AUTHENTICATED:
|
||||||
|
@ -320,8 +322,8 @@ export class Gateway extends EventEmitter {
|
||||||
op: GatewayOpcodes.REQUEST_GUILD_MEMBERS,
|
op: GatewayOpcodes.REQUEST_GUILD_MEMBERS,
|
||||||
d: {
|
d: {
|
||||||
guild_id: guild,
|
guild_id: guild,
|
||||||
query: options.query,
|
query: options.query ?? '',
|
||||||
limit: options.limit,
|
limit: options.limit ?? 0,
|
||||||
presences: options.presences,
|
presences: options.presences,
|
||||||
user_ids: options.users,
|
user_ids: options.users,
|
||||||
nonce
|
nonce
|
||||||
|
@ -387,14 +389,13 @@ export class Gateway extends EventEmitter {
|
||||||
|
|
||||||
send(data: GatewayResponse): boolean {
|
send(data: GatewayResponse): boolean {
|
||||||
if (this.websocket.readyState !== this.websocket.OPEN) return false
|
if (this.websocket.readyState !== this.websocket.OPEN) return false
|
||||||
this.websocket.send(
|
const packet = JSON.stringify({
|
||||||
JSON.stringify({
|
|
||||||
op: data.op,
|
op: data.op,
|
||||||
d: data.d,
|
d: data.d,
|
||||||
s: typeof data.s === 'number' ? data.s : null,
|
s: typeof data.s === 'number' ? data.s : null,
|
||||||
t: data.t === undefined ? null : data.t
|
t: data.t === undefined ? null : data.t
|
||||||
})
|
})
|
||||||
)
|
this.websocket.send(packet)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> {
|
||||||
const arr = await (this.client.cache.array(
|
const arr = await (this.client.cache.array(
|
||||||
this.cacheName
|
this.cacheName
|
||||||
) as ChannelPayload[])
|
) as ChannelPayload[])
|
||||||
|
if (arr === undefined) return []
|
||||||
const result: any[] = []
|
const result: any[] = []
|
||||||
for (const elem of arr) {
|
for (const elem of arr) {
|
||||||
let guild
|
let guild
|
||||||
|
|
|
@ -5,11 +5,14 @@ import { CategoryChannel } from '../structures/guildCategoryChannel.ts'
|
||||||
import { GuildTextChannel } from '../structures/textChannel.ts'
|
import { GuildTextChannel } from '../structures/textChannel.ts'
|
||||||
import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
|
import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
|
||||||
import {
|
import {
|
||||||
|
ChannelTypes,
|
||||||
GuildCategoryChannelPayload,
|
GuildCategoryChannelPayload,
|
||||||
|
GuildChannelPayload,
|
||||||
GuildTextChannelPayload,
|
GuildTextChannelPayload,
|
||||||
GuildVoiceChannelPayload
|
GuildVoiceChannelPayload,
|
||||||
|
Overwrite
|
||||||
} from '../types/channel.ts'
|
} from '../types/channel.ts'
|
||||||
import { CHANNEL } from '../types/endpoint.ts'
|
import { CHANNEL, GUILD_CHANNELS } from '../types/endpoint.ts'
|
||||||
import { BaseChildManager } from './baseChild.ts'
|
import { BaseChildManager } from './baseChild.ts'
|
||||||
import { ChannelsManager } from './channels.ts'
|
import { ChannelsManager } from './channels.ts'
|
||||||
|
|
||||||
|
@ -19,6 +22,19 @@ export type GuildChannelPayloads =
|
||||||
| GuildCategoryChannelPayload
|
| GuildCategoryChannelPayload
|
||||||
export type GuildChannel = GuildTextChannel | VoiceChannel | CategoryChannel
|
export type GuildChannel = GuildTextChannel | VoiceChannel | CategoryChannel
|
||||||
|
|
||||||
|
export interface CreateChannelOptions {
|
||||||
|
name: string
|
||||||
|
type?: ChannelTypes
|
||||||
|
topic?: string
|
||||||
|
bitrate?: number
|
||||||
|
userLimit?: number
|
||||||
|
rateLimitPerUser?: number
|
||||||
|
position?: number
|
||||||
|
permissionOverwrites?: Overwrite[]
|
||||||
|
parent?: CategoryChannel | string
|
||||||
|
nsfw?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export class GuildChannelsManager extends BaseChildManager<
|
export class GuildChannelsManager extends BaseChildManager<
|
||||||
GuildChannelPayloads,
|
GuildChannelPayloads,
|
||||||
GuildChannel
|
GuildChannel
|
||||||
|
@ -55,4 +71,32 @@ export class GuildChannelsManager extends BaseChildManager<
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Create a new Guild Channel */
|
||||||
|
async create(options: CreateChannelOptions): Promise<GuildChannel> {
|
||||||
|
if (options.name === undefined)
|
||||||
|
throw new Error('name is required for GuildChannelsManager#create')
|
||||||
|
const res = ((await this.client.rest.post(GUILD_CHANNELS(this.guild.id)),
|
||||||
|
{
|
||||||
|
name: options.name,
|
||||||
|
type: options.type,
|
||||||
|
topic: options.topic,
|
||||||
|
bitrate: options.bitrate,
|
||||||
|
user_limit: options.userLimit,
|
||||||
|
rate_limit_per_user: options.rateLimitPerUser,
|
||||||
|
position: options.position,
|
||||||
|
permission_overwrites: options.permissionOverwrites,
|
||||||
|
parent_id:
|
||||||
|
options.parent === undefined
|
||||||
|
? undefined
|
||||||
|
: typeof options.parent === 'object'
|
||||||
|
? options.parent.id
|
||||||
|
: options.parent,
|
||||||
|
nsfw: options.nsfw
|
||||||
|
}) as unknown) as GuildChannelPayload
|
||||||
|
|
||||||
|
await this.set(res.id, res)
|
||||||
|
const channel = await this.get(res.id)
|
||||||
|
return (channel as unknown) as GuildChannel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,13 @@ export class GuildVoiceStatesManager extends BaseManager<
|
||||||
this.guild = guild
|
this.guild = guild
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get Client's Voice State in the Guild */
|
||||||
|
async me(): Promise<VoiceState | undefined> {
|
||||||
|
const member = await this.guild.me()
|
||||||
|
return await this.get(member.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a Voice State by User ID */
|
||||||
async get(key: string): Promise<VoiceState | undefined> {
|
async get(key: string): Promise<VoiceState | undefined> {
|
||||||
const raw = await this._get(key)
|
const raw = await this._get(key)
|
||||||
if (raw === undefined) return
|
if (raw === undefined) return
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
|
import { GuildTextChannel, User } from '../../mod.ts'
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import { Guild } from '../structures/guild.ts'
|
import { Guild } from '../structures/guild.ts'
|
||||||
import { Invite } from '../structures/invite.ts'
|
import { Invite } from '../structures/invite.ts'
|
||||||
import { INVITE } from '../types/endpoint.ts'
|
import { CHANNEL_INVITES, GUILD_INVITES, INVITE } from '../types/endpoint.ts'
|
||||||
import { InvitePayload } from '../types/invite.ts'
|
import { InvitePayload } from '../types/invite.ts'
|
||||||
import { BaseManager } from './base.ts'
|
import { BaseManager } from './base.ts'
|
||||||
|
|
||||||
|
export enum InviteTargetUserType {
|
||||||
|
STREAM = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateInviteOptions {
|
||||||
|
maxAge?: number
|
||||||
|
maxUses?: number
|
||||||
|
temporary?: boolean
|
||||||
|
unique?: boolean
|
||||||
|
targetUser?: string | User
|
||||||
|
targetUserType?: InviteTargetUserType
|
||||||
|
}
|
||||||
|
|
||||||
export class InviteManager extends BaseManager<InvitePayload, Invite> {
|
export class InviteManager extends BaseManager<InvitePayload, Invite> {
|
||||||
guild: Guild
|
guild: Guild
|
||||||
|
|
||||||
|
@ -20,10 +34,10 @@ export class InviteManager extends BaseManager<InvitePayload, Invite> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fetch an Invite */
|
/** Fetch an Invite */
|
||||||
async fetch(id: string): Promise<Invite> {
|
async fetch(id: string, withCounts: boolean = true): Promise<Invite> {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
this.client.rest
|
this.client.rest
|
||||||
.get(INVITE(id))
|
.get(`${INVITE(id)}${withCounts ? '?with_counts=true' : ''}`)
|
||||||
.then(async (data) => {
|
.then(async (data) => {
|
||||||
this.set(id, data as InvitePayload)
|
this.set(id, data as InvitePayload)
|
||||||
const newInvite = await this.get(data.code)
|
const newInvite = await this.get(data.code)
|
||||||
|
@ -33,6 +47,57 @@ export class InviteManager extends BaseManager<InvitePayload, Invite> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Fetch all Invites of a Guild or a specific Channel */
|
||||||
|
async fetchAll(channel?: string | GuildTextChannel): Promise<Invite[]> {
|
||||||
|
const rawInvites = (await this.client.rest.get(
|
||||||
|
channel === undefined
|
||||||
|
? GUILD_INVITES(this.guild.id)
|
||||||
|
: CHANNEL_INVITES(typeof channel === 'string' ? channel : channel.id)
|
||||||
|
)) as InvitePayload[]
|
||||||
|
|
||||||
|
const res: Invite[] = []
|
||||||
|
|
||||||
|
for (const raw of rawInvites) {
|
||||||
|
await this.set(raw.code, raw)
|
||||||
|
res.push(new Invite(this.client, raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete an Invite */
|
||||||
|
async delete(invite: string | Invite): Promise<boolean> {
|
||||||
|
await this.client.rest.delete(
|
||||||
|
INVITE(typeof invite === 'string' ? invite : invite.code)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create an Invite */
|
||||||
|
async create(
|
||||||
|
channel: string | GuildTextChannel,
|
||||||
|
options?: CreateInviteOptions
|
||||||
|
): Promise<Invite> {
|
||||||
|
const raw = ((await this.client.rest.post(
|
||||||
|
CHANNEL_INVITES(typeof channel === 'string' ? channel : channel.id),
|
||||||
|
{
|
||||||
|
max_age: options?.maxAge,
|
||||||
|
max_uses: options?.maxUses,
|
||||||
|
temporary: options?.temporary,
|
||||||
|
unique: options?.unique,
|
||||||
|
target_user:
|
||||||
|
options?.targetUser === undefined
|
||||||
|
? undefined
|
||||||
|
: typeof options.targetUser === 'string'
|
||||||
|
? options.targetUser
|
||||||
|
: options.targetUser.id,
|
||||||
|
target_user_type: options?.targetUserType
|
||||||
|
}
|
||||||
|
)) as unknown) as InvitePayload
|
||||||
|
|
||||||
|
return new Invite(this.client, raw)
|
||||||
|
}
|
||||||
|
|
||||||
async fromPayload(invites: InvitePayload[]): Promise<boolean> {
|
async fromPayload(invites: InvitePayload[]): Promise<boolean> {
|
||||||
for (const invite of invites) {
|
for (const invite of invites) {
|
||||||
await this.set(invite.code, invite)
|
await this.set(invite.code, invite)
|
||||||
|
|
|
@ -3,8 +3,13 @@ import { Emoji } from '../structures/emoji.ts'
|
||||||
import { Guild } from '../structures/guild.ts'
|
import { Guild } from '../structures/guild.ts'
|
||||||
import { Message } from '../structures/message.ts'
|
import { Message } from '../structures/message.ts'
|
||||||
import { MessageReaction } from '../structures/messageReaction.ts'
|
import { MessageReaction } from '../structures/messageReaction.ts'
|
||||||
|
import { User } from '../structures/user.ts'
|
||||||
import { Reaction } from '../types/channel.ts'
|
import { Reaction } from '../types/channel.ts'
|
||||||
import { MESSAGE_REACTION, MESSAGE_REACTIONS } from '../types/endpoint.ts'
|
import {
|
||||||
|
MESSAGE_REACTION,
|
||||||
|
MESSAGE_REACTIONS,
|
||||||
|
MESSAGE_REACTION_USER
|
||||||
|
} from '../types/endpoint.ts'
|
||||||
import { BaseManager } from './base.ts'
|
import { BaseManager } from './base.ts'
|
||||||
|
|
||||||
export class MessageReactionsManager extends BaseManager<
|
export class MessageReactionsManager extends BaseManager<
|
||||||
|
@ -77,4 +82,23 @@ export class MessageReactionsManager extends BaseManager<
|
||||||
)
|
)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remove a specific Emoji from Reactions */
|
||||||
|
async removeUser(
|
||||||
|
emoji: Emoji | string,
|
||||||
|
user: User | string
|
||||||
|
): Promise<MessageReactionsManager> {
|
||||||
|
const val = encodeURIComponent(
|
||||||
|
(typeof emoji === 'object' ? emoji.id ?? emoji.name : emoji) as string
|
||||||
|
)
|
||||||
|
await this.client.rest.delete(
|
||||||
|
MESSAGE_REACTION_USER(
|
||||||
|
this.message.channel.id,
|
||||||
|
this.message.id,
|
||||||
|
val,
|
||||||
|
typeof user === 'string' ? user : user.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import { MessageReaction } from '../structures/messageReaction.ts'
|
import { MessageReaction } from '../structures/messageReaction.ts'
|
||||||
|
import { User } from '../structures/user.ts'
|
||||||
import { UsersManager } from './users.ts'
|
import { UsersManager } from './users.ts'
|
||||||
|
|
||||||
export class ReactionUsersManager extends UsersManager {
|
export class ReactionUsersManager extends UsersManager {
|
||||||
|
@ -10,4 +11,14 @@ export class ReactionUsersManager extends UsersManager {
|
||||||
this.cacheName = `reaction_users:${reaction.message.id}`
|
this.cacheName = `reaction_users:${reaction.message.id}`
|
||||||
this.reaction = reaction
|
this.reaction = reaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remove all Users from this Reaction */
|
||||||
|
async removeAll(): Promise<void> {
|
||||||
|
await this.reaction.message.reactions.removeEmoji(this.reaction.emoji)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove a specific User from this Reaction */
|
||||||
|
async remove(user: User | string): Promise<void> {
|
||||||
|
await this.reaction.message.reactions.removeUser(this.reaction.emoji, user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
import {
|
import { connect, Redis, RedisConnectOptions } from '../../deps.ts'
|
||||||
connect,
|
|
||||||
Redis,
|
|
||||||
RedisConnectOptions
|
|
||||||
} 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.
|
||||||
|
|
|
@ -134,7 +134,6 @@ export class Client extends EventEmitter {
|
||||||
handler: (interaction: Interaction) => any
|
handler: (interaction: Interaction) => any
|
||||||
}>
|
}>
|
||||||
|
|
||||||
_decoratedSlashModules?: SlashModule[]
|
|
||||||
_id?: string
|
_id?: string
|
||||||
|
|
||||||
/** Shard on which this Client is */
|
/** Shard on which this Client is */
|
||||||
|
@ -266,6 +265,7 @@ export class Client extends EventEmitter {
|
||||||
this.gateway = new Gateway(this, token, intents)
|
this.gateway = new Gateway(this, token, intents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Wait for an Event (optionally satisfying an event) to occur */
|
||||||
async waitFor<K extends keyof ClientEvents>(
|
async waitFor<K extends keyof ClientEvents>(
|
||||||
event: K,
|
event: K,
|
||||||
checkFunction: (...args: ClientEvents[K]) => boolean,
|
checkFunction: (...args: ClientEvents[K]) => boolean,
|
||||||
|
@ -291,6 +291,7 @@ export class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Event decorator to create an Event handler from function */
|
||||||
export function event(name?: keyof ClientEvents) {
|
export function event(name?: keyof ClientEvents) {
|
||||||
return function (client: Client | Extension, prop: keyof ClientEvents) {
|
return function (client: Client | Extension, prop: keyof ClientEvents) {
|
||||||
const listener = ((client as unknown) as {
|
const listener = ((client as unknown) as {
|
||||||
|
@ -307,11 +308,11 @@ export function event(name?: keyof ClientEvents) {
|
||||||
|
|
||||||
/** Decorator to create a Slash Command handler */
|
/** 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 | SlashClient | SlashModule, 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') {
|
||||||
client._decoratedSlash.push(item)
|
throw new Error('@slash decorator requires a function')
|
||||||
} else
|
} else
|
||||||
client._decoratedSlash.push({
|
client._decoratedSlash.push({
|
||||||
name: name ?? prop,
|
name: name ?? prop,
|
||||||
|
@ -323,12 +324,11 @@ export function slash(name?: string, guild?: string) {
|
||||||
|
|
||||||
/** Decorator to create a Sub-Slash Command handler */
|
/** 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 | 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') {
|
||||||
item.parent = parent
|
throw new Error('@subslash decorator requires a function')
|
||||||
client._decoratedSlash.push(item)
|
|
||||||
} else
|
} else
|
||||||
client._decoratedSlash.push({
|
client._decoratedSlash.push({
|
||||||
parent,
|
parent,
|
||||||
|
@ -350,9 +350,7 @@ export function groupslash(
|
||||||
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') {
|
||||||
item.parent = parent
|
throw new Error('@groupslash decorator requires a function')
|
||||||
item.group = group
|
|
||||||
client._decoratedSlash.push(item)
|
|
||||||
} else
|
} else
|
||||||
client._decoratedSlash.push({
|
client._decoratedSlash.push({
|
||||||
group,
|
group,
|
||||||
|
@ -363,14 +361,3 @@ export function groupslash(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Decorator to add a Slash Module to Client */
|
|
||||||
export function slashModule() {
|
|
||||||
return function (client: Client, prop: string) {
|
|
||||||
if (client._decoratedSlashModules === undefined)
|
|
||||||
client._decoratedSlashModules = []
|
|
||||||
|
|
||||||
const mod = ((client as unknown) as { [key: string]: any })[prop]
|
|
||||||
client._decoratedSlashModules.push(mod)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -382,6 +382,7 @@ export class CommandClient extends Client implements CommandClientOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Command decorator */
|
||||||
export function command(options?: CommandOptions) {
|
export function command(options?: CommandOptions) {
|
||||||
return function (target: CommandClient | Extension, name: string) {
|
return function (target: CommandClient | Extension, name: string) {
|
||||||
if (target._decoratedCommands === undefined) target._decoratedCommands = {}
|
if (target._decoratedCommands === undefined) target._decoratedCommands = {}
|
||||||
|
@ -390,10 +391,8 @@ export function command(options?: CommandOptions) {
|
||||||
[name: string]: (ctx: CommandContext) => any
|
[name: string]: (ctx: CommandContext) => any
|
||||||
})[name]
|
})[name]
|
||||||
|
|
||||||
if (prop instanceof Command) {
|
if (typeof prop !== 'function')
|
||||||
target._decoratedCommands[prop.name] = prop
|
throw new Error('@command decorator can only be used on functions')
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = new Command()
|
const command = new Command()
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,11 @@ 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/mod.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 { Buffer } from 'https://deno.land/std@0.80.0/node/buffer.ts'
|
||||||
import {
|
import type {
|
||||||
Request as ORequest,
|
Request as ORequest,
|
||||||
Response as OResponse
|
Response as OResponse
|
||||||
} from 'https://deno.land/x/opine@1.0.0/src/types.ts'
|
} 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'
|
import type { Context } from 'https://deno.land/x/oak@v6.4.0/mod.ts'
|
||||||
|
|
||||||
export class SlashCommand {
|
export class SlashCommand {
|
||||||
slash: SlashCommandsManager
|
slash: SlashCommandsManager
|
||||||
|
@ -353,8 +353,6 @@ export class SlashClient {
|
||||||
handler: (interaction: Interaction) => any
|
handler: (interaction: Interaction) => any
|
||||||
}>
|
}>
|
||||||
|
|
||||||
_decoratedSlashModules?: SlashModule[]
|
|
||||||
|
|
||||||
constructor(options: SlashOptions) {
|
constructor(options: SlashOptions) {
|
||||||
let id = options.id
|
let id = options.id
|
||||||
if (options.token !== undefined) id = atob(options.token?.split('.')[0])
|
if (options.token !== undefined) id = atob(options.token?.split('.')[0])
|
||||||
|
@ -376,24 +374,12 @@ export class SlashClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client?._decoratedSlashModules !== undefined) {
|
|
||||||
this.client._decoratedSlashModules.forEach((e) => {
|
|
||||||
this.modules.push(e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._decoratedSlash !== undefined) {
|
if (this._decoratedSlash !== undefined) {
|
||||||
this._decoratedSlash.forEach((e) => {
|
this._decoratedSlash.forEach((e) => {
|
||||||
this.handlers.push(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
|
||||||
|
@ -418,11 +404,13 @@ export class SlashClient {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Load a Slash Module */
|
||||||
loadModule(module: SlashModule): SlashClient {
|
loadModule(module: SlashModule): SlashClient {
|
||||||
this.modules.push(module)
|
this.modules.push(module)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get all Handlers. Including Slash Modules */
|
||||||
getHandlers(): SlashCommandHandler[] {
|
getHandlers(): SlashCommandHandler[] {
|
||||||
let res = this.handlers
|
let res = this.handlers
|
||||||
for (const mod of this.modules) {
|
for (const mod of this.modules) {
|
||||||
|
@ -438,6 +426,7 @@ export class SlashClient {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get Handler for an Interaction. Supports nested sub commands and sub command groups. */
|
||||||
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
|
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
|
||||||
return this.getHandlers().find((e) => {
|
return this.getHandlers().find((e) => {
|
||||||
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
|
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
|
||||||
|
@ -467,6 +456,10 @@ export class SlashClient {
|
||||||
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
|
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
|
||||||
|
|
||||||
const cmd = this._getCommand(interaction)
|
const cmd = this._getCommand(interaction)
|
||||||
|
if (cmd?.group !== undefined)
|
||||||
|
interaction.data.options = interaction.data.options[0].options ?? []
|
||||||
|
if (cmd?.parent !== undefined)
|
||||||
|
interaction.data.options = interaction.data.options[0].options ?? []
|
||||||
|
|
||||||
if (cmd === undefined) return
|
if (cmd === undefined) return
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@ export class GroupDMChannel extends Channel {
|
||||||
this.name = data.name
|
this.name = data.name
|
||||||
this.icon = data.icon
|
this.icon = data.icon
|
||||||
this.ownerID = data.owner_id
|
this.ownerID = data.owner_id
|
||||||
// TODO: Cache in Gateway Event Code
|
|
||||||
// cache.set('groupchannel', this.id, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readFromData(data: GroupDMChannelPayload): void {
|
readFromData(data: GroupDMChannelPayload): void {
|
||||||
|
|
|
@ -8,9 +8,13 @@ import {
|
||||||
IntegrationExpireBehavior
|
IntegrationExpireBehavior
|
||||||
} from '../types/guild.ts'
|
} from '../types/guild.ts'
|
||||||
import { Base } from './base.ts'
|
import { Base } from './base.ts'
|
||||||
import { RolesManager } from '../managers/roles.ts'
|
import { CreateGuildRoleOptions, RolesManager } from '../managers/roles.ts'
|
||||||
import { InviteManager } from '../managers/invites.ts'
|
import { InviteManager } from '../managers/invites.ts'
|
||||||
import { GuildChannelsManager } from '../managers/guildChannels.ts'
|
import {
|
||||||
|
CreateChannelOptions,
|
||||||
|
GuildChannel,
|
||||||
|
GuildChannelsManager
|
||||||
|
} from '../managers/guildChannels.ts'
|
||||||
import { MembersManager } from '../managers/members.ts'
|
import { MembersManager } from '../managers/members.ts'
|
||||||
import { Role } from './role.ts'
|
import { Role } from './role.ts'
|
||||||
import { GuildEmojisManager } from '../managers/guildEmojis.ts'
|
import { GuildEmojisManager } from '../managers/guildEmojis.ts'
|
||||||
|
@ -259,6 +263,16 @@ export class Guild extends Base {
|
||||||
return raw.map((e) => new GuildIntegration(this.client, e))
|
return raw.map((e) => new GuildIntegration(this.client, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Create a new Guild Channel */
|
||||||
|
async createChannel(options: CreateChannelOptions): Promise<GuildChannel> {
|
||||||
|
return this.channels.create(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a new Guild Role */
|
||||||
|
async createRole(options?: CreateGuildRoleOptions): Promise<Role> {
|
||||||
|
return this.roles.create(options)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chunks the Guild Members, i.e. cache them.
|
* Chunks the Guild Members, i.e. cache them.
|
||||||
* @param options Options regarding the Members Request
|
* @param options Options regarding the Members Request
|
||||||
|
@ -289,7 +303,6 @@ export class Guild extends Base {
|
||||||
}
|
}
|
||||||
}, timeout)
|
}, timeout)
|
||||||
}
|
}
|
||||||
resolve(this)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,32 @@ export class Invite extends Base {
|
||||||
approximatePresenceCount?: number
|
approximatePresenceCount?: number
|
||||||
approximateMemberCount?: number
|
approximateMemberCount?: number
|
||||||
|
|
||||||
|
/** Number of times Invite was used. This is an Invite Metadata property (not always available) */
|
||||||
|
uses?: number
|
||||||
|
/** Max number of times this Invite can be used. This is an Invite Metadata property (not always available) */
|
||||||
|
maxUses?: number
|
||||||
|
/** Max age of the Invite in seconds. This is an Invite Metadata property (not always available) */
|
||||||
|
maxAge?: number
|
||||||
|
/** Whether Invite is temporary or not. This is an Invite Metadata property (not always available) */
|
||||||
|
temporary?: boolean
|
||||||
|
/** Timestamp (string) when Invite was created. This is an Invite Metadata property (not always available) */
|
||||||
|
createdAtTimestamp?: string
|
||||||
|
|
||||||
|
/** Timestamp (Date) when Invite was created. This is an Invite Metadata property (not always available) */
|
||||||
|
get createdAt(): Date | undefined {
|
||||||
|
return this.createdAtTimestamp === undefined
|
||||||
|
? undefined
|
||||||
|
: new Date(this.createdAtTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
get link(): string {
|
get link(): string {
|
||||||
return `https://discord.gg/${this.code}`
|
return `https://discord.gg/${this.code}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return this.link
|
||||||
|
}
|
||||||
|
|
||||||
constructor(client: Client, data: InvitePayload) {
|
constructor(client: Client, data: InvitePayload) {
|
||||||
super(client)
|
super(client)
|
||||||
this.code = data.code
|
this.code = data.code
|
||||||
|
@ -30,6 +52,12 @@ export class Invite extends Base {
|
||||||
this.targetUserType = data.target_user_type
|
this.targetUserType = data.target_user_type
|
||||||
this.approximateMemberCount = data.approximate_member_count
|
this.approximateMemberCount = data.approximate_member_count
|
||||||
this.approximatePresenceCount = data.approximate_presence_count
|
this.approximatePresenceCount = data.approximate_presence_count
|
||||||
|
|
||||||
|
this.uses = (data as any).uses
|
||||||
|
this.maxUses = (data as any).maxUses
|
||||||
|
this.maxAge = (data as any).maxAge
|
||||||
|
this.temporary = (data as any).temporary
|
||||||
|
this.createdAtTimestamp = (data as any).createdAtTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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. */
|
/** 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. */
|
||||||
|
@ -49,5 +77,12 @@ export class Invite extends Base {
|
||||||
data.approximate_member_count ?? this.approximateMemberCount
|
data.approximate_member_count ?? this.approximateMemberCount
|
||||||
this.approximatePresenceCount =
|
this.approximatePresenceCount =
|
||||||
data.approximate_presence_count ?? this.approximatePresenceCount
|
data.approximate_presence_count ?? this.approximatePresenceCount
|
||||||
|
|
||||||
|
this.uses = (data as any).uses ?? this.uses
|
||||||
|
this.maxUses = (data as any).maxUses ?? this.maxUses
|
||||||
|
this.maxAge = (data as any).maxAge ?? this.maxAge
|
||||||
|
this.temporary = (data as any).temporary ?? this.temporary
|
||||||
|
this.createdAtTimestamp =
|
||||||
|
(data as any).createdAtTimestamp ?? this.createdAtTimestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { CreateInviteOptions } from '../managers/invites.ts'
|
||||||
import { MessagesManager } from '../managers/messages.ts'
|
import { MessagesManager } from '../managers/messages.ts'
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
|
@ -22,6 +23,7 @@ import { Channel } from './channel.ts'
|
||||||
import { Embed } from './embed.ts'
|
import { Embed } from './embed.ts'
|
||||||
import { Emoji } from './emoji.ts'
|
import { Emoji } from './emoji.ts'
|
||||||
import { Guild } from './guild.ts'
|
import { Guild } from './guild.ts'
|
||||||
|
import { Invite } from './invite.ts'
|
||||||
import { Member } from './member.ts'
|
import { Member } from './member.ts'
|
||||||
import { Message } from './message.ts'
|
import { Message } from './message.ts'
|
||||||
import { User } from './user.ts'
|
import { User } from './user.ts'
|
||||||
|
@ -319,4 +321,9 @@ export class GuildTextChannel extends TextChannel {
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Create an Invite for this Channel */
|
||||||
|
async createInvite(options?: CreateInviteOptions): Promise<Invite> {
|
||||||
|
return this.guild.invites.create(this.id, options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/test/chunk.ts
Normal file
27
src/test/chunk.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Client, Intents } from '../../mod.ts'
|
||||||
|
import { TOKEN } from './config.ts'
|
||||||
|
|
||||||
|
const client = new Client()
|
||||||
|
|
||||||
|
client.on('debug', console.log)
|
||||||
|
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log(`Logged in as ${client.user?.tag}!`)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('guildLoaded', async (guild) => {
|
||||||
|
if (guild.id !== '783319033205751809') return
|
||||||
|
const arr = await guild.channels.array()
|
||||||
|
console.log(arr.length)
|
||||||
|
guild
|
||||||
|
.chunk({ presences: true }, true)
|
||||||
|
.then((guild) => {
|
||||||
|
console.log(`Chunked guild:`, guild.id)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(`Failed to Chunk: ${guild.id} - ${e}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Connecting...')
|
||||||
|
client.connect(TOKEN, Intents.All)
|
|
@ -4,6 +4,8 @@ import { TOKEN } from './config.ts'
|
||||||
|
|
||||||
export const slash = new SlashClient({ token: TOKEN })
|
export const slash = new SlashClient({ token: TOKEN })
|
||||||
|
|
||||||
|
console.log(slash.modules)
|
||||||
|
|
||||||
// Cmd objects come here
|
// Cmd objects come here
|
||||||
const commands: SlashCommandPartial[] = []
|
const commands: SlashCommandPartial[] = []
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ const GUILD_BANS = (guildID: string): string =>
|
||||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/bans`
|
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/bans`
|
||||||
const GUILD_CHANNEL = (channelID: string): string =>
|
const GUILD_CHANNEL = (channelID: string): string =>
|
||||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}`
|
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/channels/${channelID}`
|
||||||
const GUILD_CHANNELS = (guildID: string, channelID: string): string =>
|
const GUILD_CHANNELS = (guildID: string): string =>
|
||||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/channels`
|
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/channels`
|
||||||
const GUILD_MEMBER = (guildID: string, memberID: string): string =>
|
const GUILD_MEMBER = (guildID: string, memberID: string): string =>
|
||||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/${memberID}`
|
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/${memberID}`
|
||||||
|
|
Loading…
Reference in a new issue