From c3fafdfcf06ec3533ad289ddcedaca3b0922f28a Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 1 Jan 2021 11:25:23 +0530 Subject: [PATCH] more things --- src/managers/guildChannels.ts | 48 ++++++++++++++++++++++++++++++-- src/managers/guildVoiceStates.ts | 7 +++++ src/managers/messageReactions.ts | 26 ++++++++++++++++- src/managers/reactionUsers.ts | 11 ++++++++ src/models/client.ts | 12 ++++---- src/models/commandClient.ts | 7 ++--- src/models/slashClient.ts | 29 +++++++++++++++---- src/structures/groupChannel.ts | 2 -- src/structures/guild.ts | 18 ++++++++++-- src/types/endpoint.ts | 2 +- 10 files changed, 138 insertions(+), 24 deletions(-) diff --git a/src/managers/guildChannels.ts b/src/managers/guildChannels.ts index 3bdaed8..bbc279e 100644 --- a/src/managers/guildChannels.ts +++ b/src/managers/guildChannels.ts @@ -5,11 +5,14 @@ import { CategoryChannel } from '../structures/guildCategoryChannel.ts' import { GuildTextChannel } from '../structures/textChannel.ts' import { VoiceChannel } from '../structures/guildVoiceChannel.ts' import { + ChannelTypes, GuildCategoryChannelPayload, + GuildChannelPayload, GuildTextChannelPayload, - GuildVoiceChannelPayload + GuildVoiceChannelPayload, + Overwrite } from '../types/channel.ts' -import { CHANNEL } from '../types/endpoint.ts' +import { CHANNEL, GUILD_CHANNELS } from '../types/endpoint.ts' import { BaseChildManager } from './baseChild.ts' import { ChannelsManager } from './channels.ts' @@ -19,6 +22,19 @@ export type GuildChannelPayloads = | GuildCategoryChannelPayload 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< GuildChannelPayloads, GuildChannel @@ -55,4 +71,32 @@ export class GuildChannelsManager extends BaseChildManager< } return true } + + /** Create a new Guild Channel */ + async create(options: CreateChannelOptions): Promise { + 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 + } } diff --git a/src/managers/guildVoiceStates.ts b/src/managers/guildVoiceStates.ts index 677caef..4b1c5bd 100644 --- a/src/managers/guildVoiceStates.ts +++ b/src/managers/guildVoiceStates.ts @@ -17,6 +17,13 @@ export class GuildVoiceStatesManager extends BaseManager< this.guild = guild } + /** Get Client's Voice State in the Guild */ + async me(): Promise { + const member = await this.guild.me() + return await this.get(member.id) + } + + /** Get a Voice State by User ID */ async get(key: string): Promise { const raw = await this._get(key) if (raw === undefined) return diff --git a/src/managers/messageReactions.ts b/src/managers/messageReactions.ts index 000c5f9..30c0672 100644 --- a/src/managers/messageReactions.ts +++ b/src/managers/messageReactions.ts @@ -3,8 +3,13 @@ import { Emoji } from '../structures/emoji.ts' import { Guild } from '../structures/guild.ts' import { Message } from '../structures/message.ts' import { MessageReaction } from '../structures/messageReaction.ts' +import { User } from '../structures/user.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' export class MessageReactionsManager extends BaseManager< @@ -77,4 +82,23 @@ export class MessageReactionsManager extends BaseManager< ) return this } + + /** Remove a specific Emoji from Reactions */ + async removeUser( + emoji: Emoji | string, + user: User | string + ): Promise { + 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 + } } diff --git a/src/managers/reactionUsers.ts b/src/managers/reactionUsers.ts index be78e13..5e38b2a 100644 --- a/src/managers/reactionUsers.ts +++ b/src/managers/reactionUsers.ts @@ -1,5 +1,6 @@ import { Client } from '../models/client.ts' import { MessageReaction } from '../structures/messageReaction.ts' +import { User } from '../structures/user.ts' import { UsersManager } from './users.ts' export class ReactionUsersManager extends UsersManager { @@ -10,4 +11,14 @@ export class ReactionUsersManager extends UsersManager { this.cacheName = `reaction_users:${reaction.message.id}` this.reaction = reaction } + + /** Remove all Users from this Reaction */ + async removeAll(): Promise { + await this.reaction.message.reactions.removeEmoji(this.reaction.emoji) + } + + /** Remove a specific User from this Reaction */ + async remove(user: User | string): Promise { + await this.reaction.message.reactions.removeUser(this.reaction.emoji, user) + } } diff --git a/src/models/client.ts b/src/models/client.ts index ee00401..1e4e002 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -134,7 +134,7 @@ export class Client extends EventEmitter { handler: (interaction: Interaction) => any }> - _decoratedSlashModules?: SlashModule[] + _decoratedSlashModules?: string[] _id?: string /** Shard on which this Client is */ @@ -291,6 +291,7 @@ export class Client extends EventEmitter { } } +/** Event decorator to create an Event handler from function */ export function event(name?: keyof ClientEvents) { return function (client: Client | Extension, prop: keyof ClientEvents) { const listener = ((client as unknown) as { @@ -311,7 +312,7 @@ export function slash(name?: string, guild?: string) { if (client._decoratedSlash === undefined) client._decoratedSlash = [] const item = (client as { [name: string]: any })[prop] if (typeof item !== 'function') { - client._decoratedSlash.push(item) + throw new Error('@slash decorator requires a function') } else client._decoratedSlash.push({ name: name ?? prop, @@ -327,8 +328,7 @@ export function subslash(parent: string, name?: string, guild?: string) { if (client._decoratedSlash === undefined) client._decoratedSlash = [] const item = (client as { [name: string]: any })[prop] if (typeof item !== 'function') { - item.parent = parent - client._decoratedSlash.push(item) + throw new Error('@subslash decorator requires a function') } else client._decoratedSlash.push({ parent, @@ -350,9 +350,7 @@ export function groupslash( if (client._decoratedSlash === undefined) client._decoratedSlash = [] const item = (client as { [name: string]: any })[prop] if (typeof item !== 'function') { - item.parent = parent - item.group = group - client._decoratedSlash.push(item) + throw new Error('@groupslash decorator requires a function') } else client._decoratedSlash.push({ group, diff --git a/src/models/commandClient.ts b/src/models/commandClient.ts index 72da0fe..28fba90 100644 --- a/src/models/commandClient.ts +++ b/src/models/commandClient.ts @@ -382,6 +382,7 @@ export class CommandClient extends Client implements CommandClientOptions { } } +/** Command decorator */ export function command(options?: CommandOptions) { return function (target: CommandClient | Extension, name: string) { if (target._decoratedCommands === undefined) target._decoratedCommands = {} @@ -390,10 +391,8 @@ export function command(options?: CommandOptions) { [name: string]: (ctx: CommandContext) => any })[name] - if (prop instanceof Command) { - target._decoratedCommands[prop.name] = prop - return - } + if (typeof prop !== 'function') + throw new Error('@command decorator can only be used on functions') const command = new Command() diff --git a/src/models/slashClient.ts b/src/models/slashClient.ts index e029e49..f2e550b 100644 --- a/src/models/slashClient.ts +++ b/src/models/slashClient.ts @@ -14,11 +14,11 @@ 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 { +import type { 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' +import type { Context } from 'https://deno.land/x/oak@v6.4.0/mod.ts' export class SlashCommand { slash: SlashCommandsManager @@ -353,7 +353,7 @@ export class SlashClient { handler: (interaction: Interaction) => any }> - _decoratedSlashModules?: SlashModule[] + _decoratedSlashModules?: string[] constructor(options: SlashOptions) { let id = options.id @@ -378,7 +378,14 @@ export class SlashClient { if (this.client?._decoratedSlashModules !== undefined) { this.client._decoratedSlashModules.forEach((e) => { - this.modules.push(e) + const mod = ((this.client as unknown) as { + [name: string]: SlashModule + })[e] + if (!(mod instanceof SlashModule)) + throw new Error( + '@slashModule can only be used on SlashModule instances' + ) + this.modules.push(mod) }) } @@ -390,7 +397,12 @@ export class SlashClient { if (this._decoratedSlashModules !== undefined) { this._decoratedSlashModules.forEach((e) => { - this.modules.push(e) + const mod = ((this as unknown) as { [name: string]: SlashModule })[e] + if (!(mod instanceof SlashModule)) + throw new Error( + '@slashModule can only be used on SlashModule instances' + ) + this.modules.push(mod) }) } @@ -418,11 +430,13 @@ export class SlashClient { return this } + /** Load a Slash Module */ loadModule(module: SlashModule): SlashClient { this.modules.push(module) return this } + /** Get all Handlers. Including Slash Modules */ getHandlers(): SlashCommandHandler[] { let res = this.handlers for (const mod of this.modules) { @@ -438,6 +452,7 @@ export class SlashClient { return res } + /** Get Handler for an Interaction. Supports nested sub commands and sub command groups. */ private _getCommand(i: Interaction): SlashCommandHandler | undefined { return this.getHandlers().find((e) => { const hasGroupOrParent = e.group !== undefined || e.parent !== undefined @@ -467,6 +482,10 @@ export class SlashClient { if (interaction.type !== InteractionType.APPLICATION_COMMAND) return 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 diff --git a/src/structures/groupChannel.ts b/src/structures/groupChannel.ts index 33a31b2..bd0cb15 100644 --- a/src/structures/groupChannel.ts +++ b/src/structures/groupChannel.ts @@ -13,8 +13,6 @@ export class GroupDMChannel extends Channel { this.name = data.name this.icon = data.icon this.ownerID = data.owner_id - // TODO: Cache in Gateway Event Code - // cache.set('groupchannel', this.id, this) } readFromData(data: GroupDMChannelPayload): void { diff --git a/src/structures/guild.ts b/src/structures/guild.ts index 0e7e98d..d50f500 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -8,9 +8,13 @@ import { IntegrationExpireBehavior } from '../types/guild.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 { GuildChannelsManager } from '../managers/guildChannels.ts' +import { + CreateChannelOptions, + GuildChannel, + GuildChannelsManager +} from '../managers/guildChannels.ts' import { MembersManager } from '../managers/members.ts' import { Role } from './role.ts' import { GuildEmojisManager } from '../managers/guildEmojis.ts' @@ -297,6 +301,16 @@ export class Guild extends Base { return raw.map((e) => new GuildIntegration(this.client, e)) } + /** Create a new Guild Channel */ + async createChannel(options: CreateChannelOptions): Promise { + return this.channels.create(options) + } + + /** Create a new Guild Role */ + async createRole(options?: CreateGuildRoleOptions): Promise { + return this.roles.create(options) + } + /** * Chunks the Guild Members, i.e. cache them. * @param options Options regarding the Members Request diff --git a/src/types/endpoint.ts b/src/types/endpoint.ts index 69d363b..c3cb0da 100644 --- a/src/types/endpoint.ts +++ b/src/types/endpoint.ts @@ -32,7 +32,7 @@ const GUILD_BANS = (guildID: string): string => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/bans` const GUILD_CHANNEL = (channelID: string): string => `${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` const GUILD_MEMBER = (guildID: string, memberID: string): string => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/guilds/${guildID}/members/${memberID}`