From 711f78002ead5cd40ca4b7030661fc2136abbf5b Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 16:20:16 +0530 Subject: [PATCH 1/7] add channels.sendMessage --- src/managers/channels.ts | 101 +++++++++++++++++++++++++++++++++- src/models/command.ts | 3 + src/models/commandClient.ts | 13 +++-- src/structures/textChannel.ts | 67 +--------------------- src/test/index.ts | 14 +++++ 5 files changed, 128 insertions(+), 70 deletions(-) diff --git a/src/managers/channels.ts b/src/managers/channels.ts index f7bdae4..b5fd232 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -1,10 +1,20 @@ import { Client } from '../models/client.ts' import { Channel } from '../structures/channel.ts' -import { ChannelPayload, GuildChannelPayload } from '../types/channel.ts' +import { Embed } from '../structures/embed.ts' +import { Message } from '../structures/message.ts' +import { TextChannel } from '../structures/textChannel.ts' +import { + ChannelPayload, + GuildChannelPayload, + MessageOptions, + MessageReference +} from '../types/channel.ts' import { CHANNEL } from '../types/endpoint.ts' import getChannelByType from '../utils/getChannelByType.ts' import { BaseManager } from './base.ts' +export type AllMessageOptions = MessageOptions | Embed + export class ChannelsManager extends BaseManager { constructor(client: Client) { super(client, 'channels', Channel) @@ -66,4 +76,93 @@ export class ChannelsManager extends BaseManager { .catch((e) => reject(e)) }) } + + async sendMessage( + channel: string | TextChannel, + content?: string | AllMessageOptions, + option?: AllMessageOptions, + reply?: Message + ): Promise { + const channelID = typeof channel === 'string' ? channel : channel.id + + 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 + } + } + + const payload: any = { + content: content, + embed: option?.embed, + file: option?.file, + files: option?.files, + tts: option?.tts, + allowed_mentions: option?.allowedMentions + } + + if (reply !== undefined) { + const reference: MessageReference = { + message_id: reply.id, + channel_id: reply.channel.id, + guild_id: reply.guild?.id + } + payload.message_reference = reference + } + + const resp = await this.client.rest.api.channels[channelID].messages.post( + payload + ) + const chan = + typeof channel === 'string' + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await this.get(channel))! + : channel + const res = new Message(this.client, resp, chan, this.client.user as any) + await res.mentions.fromPayload(resp) + return res + } + + async editMessage( + channel: string | TextChannel, + message: Message | string, + text?: string, + option?: MessageOptions + ): Promise { + const channelID = typeof channel === 'string' ? channel : channel.id + + if (text === undefined && option === undefined) { + throw new Error('Either text or option is necessary.') + } + + if (this.client.user === undefined) { + throw new Error('Client user has not initialized.') + } + + const newMsg = await this.client.rest.api.channels[channelID].messages[ + typeof message === 'string' ? message : message.id + ].patch({ + content: text, + embed: option?.embed !== undefined ? option.embed.toJSON() : undefined, + // Cannot upload new files with Message + // file: option?.file, + tts: option?.tts, + allowed_mentions: option?.allowedMentions + }) + + const chan = + typeof channel === 'string' + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await this.get(channel))! + : channel + const res = new Message(this.client, newMsg, chan, this.client.user) + await res.mentions.fromPayload(newMsg) + return res + } } diff --git a/src/models/command.ts b/src/models/command.ts index 05e59ff..4040449 100644 --- a/src/models/command.ts +++ b/src/models/command.ts @@ -92,6 +92,9 @@ export class Command implements CommandOptions { dmOnly?: boolean ownerOnly?: boolean + /** Method called when the command errors */ + onError(ctx: CommandContext, error: Error): any {} + /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ beforeExecute(ctx: CommandContext): boolean | Promise { return true diff --git a/src/models/commandClient.ts b/src/models/commandClient.ts index 67d121d..1c78a13 100644 --- a/src/models/commandClient.ts +++ b/src/models/commandClient.ts @@ -286,7 +286,8 @@ export class CommandClient extends Client implements CommandClientOptions { if ( (command.botPermissions !== undefined || - category?.permissions !== undefined) && + category?.botPermissions !== undefined || + allPermissions !== undefined) && msg.guild !== undefined ) { // TODO: Check Overwrites too @@ -315,7 +316,8 @@ export class CommandClient extends Client implements CommandClientOptions { if ( (command.userPermissions !== undefined || - category?.userPermissions !== undefined) && + category?.userPermissions !== undefined || + allPermissions !== undefined) && msg.guild !== undefined ) { let permissions = @@ -358,8 +360,11 @@ export class CommandClient extends Client implements CommandClientOptions { if (beforeExecute === false) return const result = await command.execute(ctx) - command.afterExecute(ctx, result) + await command.afterExecute(ctx, result) } catch (e) { + await command + .onError(ctx, e) + .catch((e: Error) => this.emit('commandError', ctx, e)) this.emit('commandError', ctx, e) } } @@ -375,7 +380,7 @@ export function command(options?: CommandOptions) { })[name] if (typeof prop !== 'function') - throw new Error('@command decorator can only be used on functions') + throw new Error('@command decorator can only be used on class methods') const command = new Command() diff --git a/src/structures/textChannel.ts b/src/structures/textChannel.ts index 5f05f8d..8291740 100644 --- a/src/structures/textChannel.ts +++ b/src/structures/textChannel.ts @@ -5,7 +5,6 @@ import { GuildTextChannelPayload, MessageOptions, MessagePayload, - MessageReference, ModifyGuildTextChannelOption, ModifyGuildTextChannelPayload, Overwrite, @@ -13,8 +12,6 @@ import { } from '../types/channel.ts' import { CHANNEL, - CHANNEL_MESSAGE, - CHANNEL_MESSAGES, MESSAGE_REACTION_ME, MESSAGE_REACTION_USER } from '../types/endpoint.ts' @@ -62,42 +59,7 @@ export class TextChannel extends Channel { option?: AllMessageOptions, reply?: Message ): Promise { - 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 - } - } - - const payload: any = { - content: content, - embed: option?.embed, - file: option?.file, - files: option?.files, - tts: option?.tts, - allowed_mentions: option?.allowedMentions - } - - if (reply !== undefined) { - const reference: MessageReference = { - message_id: reply.id, - channel_id: reply.channel.id, - guild_id: reply.guild?.id - } - payload.message_reference = reference - } - - const resp = await this.client.rest.post(CHANNEL_MESSAGES(this.id), payload) - - const res = new Message(this.client, resp, this, this.client.user as any) - await res.mentions.fromPayload(resp) - return res + return this.client.channels.sendMessage(this, content, option, reply) } /** @@ -111,32 +73,7 @@ export class TextChannel extends Channel { text?: string, option?: MessageOptions ): Promise { - if (text === undefined && option === undefined) { - throw new Error('Either text or option is necessary.') - } - - if (this.client.user === undefined) { - throw new Error('Client user has not initialized.') - } - - const newMsg = await this.client.rest.patch( - CHANNEL_MESSAGE( - this.id, - typeof message === 'string' ? message : message.id - ), - { - content: text, - embed: option?.embed !== undefined ? option.embed.toJSON() : undefined, - // Cannot upload new files with Message - // file: option?.file, - tts: option?.tts, - allowed_mentions: option?.allowedMentions - } - ) - - const res = new Message(this.client, newMsg, this, this.client.user) - await res.mentions.fromPayload(newMsg) - return res + return this.client.channels.editMessage(this, message, text, option) } /** Add a reaction to a Message in this Channel */ diff --git a/src/test/index.ts b/src/test/index.ts index da0d0bb..0143763 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -243,6 +243,20 @@ client.on('messageCreate', async (msg: Message) => { buf += `\n${role.name}` } msg.reply(buf) + } else if (msg.content === '!timer') { + msg.channel.send('3...').then((msg) => { + setTimeout(() => { + msg.edit('2...').then((msg) => { + setTimeout(() => { + msg.edit('1...').then((msg) => { + setTimeout(() => { + msg.edit('ok wut') + }, 1000) + }) + }, 1000) + }) + }, 1000) + }) } }) From 2286c9af3f7c90ed555d255680d037b9e6574b6d Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 16:48:11 +0530 Subject: [PATCH 2/7] required changes --- src/managers/channels.ts | 33 +++++++++++++++++++-------------- src/structures/textChannel.ts | 6 +++++- src/types/channel.ts | 22 ++++++++++++++++------ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/managers/channels.ts b/src/managers/channels.ts index b5fd232..266b420 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -6,8 +6,7 @@ import { TextChannel } from '../structures/textChannel.ts' import { ChannelPayload, GuildChannelPayload, - MessageOptions, - MessageReference + MessageOptions } from '../types/channel.ts' import { CHANNEL } from '../types/endpoint.ts' import getChannelByType from '../utils/getChannelByType.ts' @@ -80,8 +79,7 @@ export class ChannelsManager extends BaseManager { async sendMessage( channel: string | TextChannel, content?: string | AllMessageOptions, - option?: AllMessageOptions, - reply?: Message + option?: AllMessageOptions ): Promise { const channelID = typeof channel === 'string' ? channel : channel.id @@ -104,16 +102,23 @@ export class ChannelsManager extends BaseManager { file: option?.file, files: option?.files, tts: option?.tts, - allowed_mentions: option?.allowedMentions - } - - if (reply !== undefined) { - const reference: MessageReference = { - message_id: reply.id, - channel_id: reply.channel.id, - guild_id: reply.guild?.id - } - payload.message_reference = reference + allowed_mentions: option?.allowedMentions, + message_reference: + option?.reply === undefined + ? undefined + : typeof option.reply === 'string' + ? { + message_id: option.reply + } + : typeof option.reply === 'object' + ? option.reply instanceof Message + ? { + message_id: option.reply.id, + channel_id: option.reply.channel.id, + guild_id: option.reply.guild?.id + } + : option.reply + : undefined } const resp = await this.client.rest.api.channels[channelID].messages.post( diff --git a/src/structures/textChannel.ts b/src/structures/textChannel.ts index 8291740..0520520 100644 --- a/src/structures/textChannel.ts +++ b/src/structures/textChannel.ts @@ -59,7 +59,11 @@ export class TextChannel extends Channel { option?: AllMessageOptions, reply?: Message ): Promise { - return this.client.channels.sendMessage(this, content, option, reply) + return this.client.channels.sendMessage( + this, + content, + Object.assign(option ?? {}, { reply }) + ) } /** diff --git a/src/types/channel.ts b/src/types/channel.ts index e1d44e0..7d06156 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -1,5 +1,5 @@ import { Embed } from '../structures/embed.ts' -import { MessageAttachment } from '../structures/message.ts' +import type { Message, MessageAttachment } from '../structures/message.ts' import { EmojiPayload } from './emoji.ts' import { MemberPayload } from './guild.ts' import { UserPayload } from './user.ts' @@ -156,16 +156,26 @@ export interface MessagePayload { stickers?: MessageStickerPayload[] } +export enum AllowedMentionType { + Roles = 'roles', + Users = 'users', + Everyone = 'everyone' +} + +export interface AllowedMentionsPayload { + parse?: AllowedMentionType[] + users?: string[] + roles?: string[] + replied_user?: boolean +} + export interface MessageOptions { tts?: boolean embed?: Embed file?: MessageAttachment files?: MessageAttachment[] - allowedMentions?: { - parse?: 'everyone' | 'users' | 'roles' - roles?: string[] - users?: string[] - } + allowedMentions?: AllowedMentionsPayload + reply?: Message | MessageReference | string } export interface ChannelMention { From 6641778578e4867fcb9b563e69a0bab8a1961d2c Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 16:51:25 +0530 Subject: [PATCH 3/7] part 2 --- src/managers/channels.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/managers/channels.ts b/src/managers/channels.ts index 266b420..4980802 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -137,7 +137,7 @@ export class ChannelsManager extends BaseManager { async editMessage( channel: string | TextChannel, message: Message | string, - text?: string, + text?: string | MessageOptions, option?: MessageOptions ): Promise { const channelID = typeof channel === 'string' ? channel : channel.id @@ -150,6 +150,12 @@ export class ChannelsManager extends BaseManager { throw new Error('Client user has not initialized.') } + if (typeof text === 'object') { + if (typeof option === 'object') Object.assign(option, text) + else option = text + text = undefined + } + const newMsg = await this.client.rest.api.channels[channelID].messages[ typeof message === 'string' ? message : message.id ].patch({ From 4f7171704756cfd2211a217fe4cb68dad9d49507 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 17:07:57 +0530 Subject: [PATCH 4/7] resolve --- src/managers/base.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/managers/base.ts b/src/managers/base.ts index 442b7fc..d55a34f 100644 --- a/src/managers/base.ts +++ b/src/managers/base.ts @@ -67,6 +67,20 @@ export class BaseManager { yield* readable.getIterator() } + async fetch(...args: unknown[]): Promise { + return undefined + } + + /** Try to get value from cache, if not found then fetch */ + async resolve(key: string): Promise { + const cacheValue = await this.get(key) + if (cacheValue !== undefined) return cacheValue + else { + const fetchValue = await this.fetch(key).catch(() => undefined) + if (fetchValue !== undefined) return fetchValue + } + } + /** Deletes everything from Cache */ flush(): any { return this.client.cache.deleteCache(this.cacheName) From fccac82fdc07c5e80bcc8b71485433a7a8f7da99 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 17:18:37 +0530 Subject: [PATCH 5/7] x --- src/managers/baseChild.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/managers/baseChild.ts b/src/managers/baseChild.ts index 0842859..e016bb7 100644 --- a/src/managers/baseChild.ts +++ b/src/managers/baseChild.ts @@ -46,4 +46,18 @@ export class BaseChildManager { arr.forEach((el: unknown) => writable.getWriter().write(el)) yield* readable.getIterator() } + + async fetch(...args: unknown[]): Promise { + return undefined + } + + /** Try to get value from cache, if not found then fetch */ + async resolve(key: string): Promise { + const cacheValue = await this.get(key) + if (cacheValue !== undefined) return cacheValue + else { + const fetchValue = await this.fetch(key).catch(() => undefined) + if (fetchValue !== undefined) return fetchValue + } + } } From 9023606faab07a6968534078dae61e01b224046d Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 17:20:46 +0530 Subject: [PATCH 6/7] fix --- src/managers/baseChild.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/managers/baseChild.ts b/src/managers/baseChild.ts index e016bb7..28f5d77 100644 --- a/src/managers/baseChild.ts +++ b/src/managers/baseChild.ts @@ -48,7 +48,7 @@ export class BaseChildManager { } async fetch(...args: unknown[]): Promise { - return undefined + return this.parent.fetch(...args) } /** Try to get value from cache, if not found then fetch */ From 11fa1281cf5cd7bf85844b726d1e98b786921fb9 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 19 Mar 2021 17:24:48 +0530 Subject: [PATCH 7/7] fix deprecated warn --- src/managers/base.ts | 2 +- src/managers/baseChild.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/managers/base.ts b/src/managers/base.ts index d55a34f..7efa4e0 100644 --- a/src/managers/base.ts +++ b/src/managers/base.ts @@ -64,7 +64,7 @@ export class BaseManager { const arr = (await this.array()) ?? [] const { readable, writable } = new TransformStream() arr.forEach((el) => writable.getWriter().write(el)) - yield* readable.getIterator() + yield* readable } async fetch(...args: unknown[]): Promise { diff --git a/src/managers/baseChild.ts b/src/managers/baseChild.ts index 28f5d77..446a5d3 100644 --- a/src/managers/baseChild.ts +++ b/src/managers/baseChild.ts @@ -44,7 +44,7 @@ export class BaseChildManager { const arr = (await this.array()) ?? [] const { readable, writable } = new TransformStream() arr.forEach((el: unknown) => writable.getWriter().write(el)) - yield* readable.getIterator() + yield* readable } async fetch(...args: unknown[]): Promise {