Merge pull request #32 from DjDeveloperr/main
Added Message Mentions, mentionPrefix, Blocked Users/Channels/Guilds for Commands, MemberRolesManager, Improved REST Errors
This commit is contained in:
		
						commit
						7600a8e477
					
				
					 22 changed files with 385 additions and 177 deletions
				
			
		|  | @ -55,7 +55,7 @@ client.on('messageCreate', (msg: Message): void => { | |||
| }) | ||||
| 
 | ||||
| // Connect to gateway | ||||
| // Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers) | ||||
| // Replace with your bot's token and intents (Intents.All, Intents.None, Intents.Presence, Intents.GuildMembers) | ||||
| client.connect('super secret token comes here', Intents.All) | ||||
| ``` | ||||
| 
 | ||||
|  | @ -84,7 +84,7 @@ class PingCommand extends Command { | |||
| client.commands.add(PingCommand) | ||||
| 
 | ||||
| // Connect to gateway | ||||
| // Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers) | ||||
| // Replace with your bot's token and intents (Intents.All, Intents.None, Intents.Presence, Intents.GuildMembers) | ||||
| client.connect('super secret token comes here', Intents.All) | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								mod.ts
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								mod.ts
									
										
									
									
									
								
							|  | @ -26,7 +26,6 @@ export * from './src/structures/groupChannel.ts' | |||
| export * from './src/structures/guild.ts' | ||||
| export * from './src/structures/guildCategoryChannel.ts' | ||||
| export * from './src/structures/guildNewsChannel.ts' | ||||
| export * from './src/structures/guildTextChannel.ts' | ||||
| export * from './src/structures/guildVoiceChannel.ts' | ||||
| export * from './src/structures/invite.ts' | ||||
| export * from './src/structures/member.ts' | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import { Message } from '../../structures/message.ts' | ||||
| import { MessageMentions } from '../../structures/messageMentions.ts' | ||||
| import { TextChannel } from '../../structures/textChannel.ts' | ||||
| import { User } from '../../structures/user.ts' | ||||
| import { MessagePayload } from '../../types/channel.ts' | ||||
|  | @ -26,10 +25,10 @@ export const messageCreate: GatewayEventHandler = async ( | |||
|     await guild.members.set(d.author.id, d.member) | ||||
|     member = await guild.members.get(d.author.id) | ||||
|   } | ||||
|   const mentions = new MessageMentions() | ||||
|   const message = new Message(gateway.client, d, channel as any, user, mentions) | ||||
|   message.member = member | ||||
|   const message = new Message(gateway.client, d, channel as any, user) | ||||
|   if (guild !== undefined) message.guild = guild | ||||
|   await message.mentions.fromPayload(d) | ||||
|   message.member = member | ||||
|   if (message.member !== undefined) { | ||||
|     if (message.member.user === undefined) { | ||||
|       const user = await gateway.client.users.get(message.member.id) | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ import { gatewayHandlers } from './handlers/index.ts' | |||
| import { GATEWAY_BOT } from '../types/endpoint.ts' | ||||
| import { GatewayCache } from '../managers/gatewayCache.ts' | ||||
| import { ClientActivityPayload } from '../structures/presence.ts' | ||||
| import { delay } from "../utils/delay.ts" | ||||
| 
 | ||||
| /** | ||||
|  * Handles Discord gateway connection. | ||||
|  | @ -140,7 +141,7 @@ class Gateway { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private onclose (event: CloseEvent): void { | ||||
|   private async onclose (event: CloseEvent): Promise<void> { | ||||
|     this.debug(`Connection Closed with code: ${event.code}`) | ||||
| 
 | ||||
|     if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { | ||||
|  | @ -178,7 +179,8 @@ class Gateway { | |||
|     } else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) { | ||||
|       throw new Error("Given Intents aren't allowed") | ||||
|     } else { | ||||
|       this.debug('Unknown Close code, probably connection error. Reconnecting.') | ||||
|       this.debug('Unknown Close code, probably connection error. Reconnecting in 5s.') | ||||
|       await delay(5000) | ||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|       this.reconnect() | ||||
|     } | ||||
|  | @ -219,7 +221,7 @@ class Gateway { | |||
|         token: this.token, | ||||
|         properties: { | ||||
|           $os: Deno.build.os, | ||||
|           $browser: 'harmony', // TODO: Change lib name
 | ||||
|           $browser: 'harmony', | ||||
|           $device: 'harmony' | ||||
|         }, | ||||
|         compress: true, | ||||
|  | @ -233,18 +235,17 @@ class Gateway { | |||
|     } | ||||
| 
 | ||||
|     if (this.client.bot === false) { | ||||
|       // TODO: Complete Selfbot support
 | ||||
|       this.debug('Modify Identify Payload for Self-bot..') | ||||
|       // delete payload.d['intents']
 | ||||
|       // payload.d.intents = Intents.None
 | ||||
|       delete payload.d.intents | ||||
|       payload.d.presence = null | ||||
|       payload.d.properties = { | ||||
|         $os: 'Windows', | ||||
|         $browser: 'Firefox', | ||||
|         $device: '' | ||||
|         $device: '', | ||||
|         $referrer: '', | ||||
|         $referring_domain: '' | ||||
|       } | ||||
| 
 | ||||
|       this.debug('Warn: Support for selfbots is incomplete') | ||||
|       payload.d.synced_guilds = [] | ||||
|     } | ||||
| 
 | ||||
|     this.send(payload) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { Client } from '../models/client.ts' | |||
| import { Channel } from '../structures/channel.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import { CategoryChannel } from '../structures/guildCategoryChannel.ts' | ||||
| import { GuildTextChannel } from '../structures/guildTextChannel.ts' | ||||
| import { GuildTextChannel } from '../structures/textChannel.ts' | ||||
| import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | ||||
| import { | ||||
|   GuildChannelCategoryPayload, | ||||
|  |  | |||
							
								
								
									
										42
									
								
								src/managers/memberRoles.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/managers/memberRoles.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { BaseChildManager } from './baseChild.ts' | ||||
| import { RolePayload } from "../types/role.ts" | ||||
| import { Role } from "../structures/role.ts" | ||||
| import { Member } from "../structures/member.ts" | ||||
| import { RolesManager } from "./roles.ts" | ||||
| import { MemberPayload } from "../types/guild.ts" | ||||
| 
 | ||||
| export class MemberRolesManager extends BaseChildManager< | ||||
|   RolePayload, | ||||
|   Role | ||||
| > { | ||||
|   member: Member | ||||
| 
 | ||||
|   constructor (client: Client, parent: RolesManager, member: Member) { | ||||
|     super(client, parent as any) | ||||
|     this.member = member | ||||
|   } | ||||
| 
 | ||||
|   async get (id: string): Promise<Role | undefined> { | ||||
|     const res = await this.parent.get(id) | ||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload | ||||
|     if (res !== undefined && mem.roles.includes(res.id) === true) return res | ||||
|     else return undefined | ||||
|   } | ||||
| 
 | ||||
|   async array (): Promise<Role[]> { | ||||
|     const arr = (await this.parent.array()) as Role[] | ||||
|     const mem = await (this.parent as any).guild.members._get(this.member.id) as MemberPayload | ||||
|     return arr.filter( | ||||
|       (c: any) => mem.roles.includes(c.id) | ||||
|     ) as any | ||||
|   } | ||||
| 
 | ||||
|   async flush (): Promise<boolean> { | ||||
|     const arr = await this.array() | ||||
|     for (const elem of arr) { | ||||
|       this.parent.delete(elem.id) | ||||
|     } | ||||
|     return true | ||||
|   } | ||||
| } | ||||
|  | @ -18,11 +18,7 @@ export class MembersManager extends BaseManager<MemberPayload, Member> { | |||
|     const raw = await this._get(key) | ||||
|     if (raw === undefined) return | ||||
|     const user = new User(this.client, raw.user) | ||||
|     const res = new this.DataType(this.client, raw, user) | ||||
|     for (const roleid of res.roleIDs as string[]) { | ||||
|       const role = await this.guild.roles.get(roleid) | ||||
|       if (role !== undefined) res.roles.push(role) | ||||
|     } | ||||
|     const res = new this.DataType(this.client, raw, user, this.guild) | ||||
|     return res | ||||
|   } | ||||
| 
 | ||||
|  | @ -31,11 +27,7 @@ export class MembersManager extends BaseManager<MemberPayload, Member> { | |||
|       this.client.rest.get(GUILD_MEMBER(this.guild.id, id)).then(async data => { | ||||
|         await this.set(id, data as MemberPayload) | ||||
|         const user: User = new User(this.client, data.user) | ||||
|         const res = new Member(this.client, data as MemberPayload, user) | ||||
|         for (const roleid of res.roleIDs as string[]) { | ||||
|           const role = await this.guild.roles.get(roleid) | ||||
|           if (role !== undefined) res.roles.push(role) | ||||
|         } | ||||
|         const res = new Member(this.client, data as MemberPayload, user, this.guild) | ||||
|         resolve(res) | ||||
|       }).catch(e => reject(e)) | ||||
|     }) | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { Message } from '../structures/message.ts' | ||||
| import { MessageMentions } from '../structures/messageMentions.ts' | ||||
| import { TextChannel } from '../structures/textChannel.ts' | ||||
| import { User } from '../structures/user.ts' | ||||
| import { MessagePayload } from '../types/channel.ts' | ||||
|  | @ -21,9 +20,10 @@ export class MessagesManager extends BaseManager<MessagePayload, Message> { | |||
|       channel = await this.client.channels.fetch(raw.channel_id) | ||||
| 
 | ||||
|     const author = new User(this.client, raw.author) | ||||
|     const mentions = new MessageMentions() | ||||
| 
 | ||||
|     return new this.DataType(this.client, raw, channel, author, mentions) as any | ||||
|     const res = new this.DataType(this.client, raw, channel, author) as any | ||||
|     await res.mentions.fromPayload(raw) | ||||
|     return res | ||||
|   } | ||||
| 
 | ||||
|   async fetch (channelID: string, id: string): Promise<Message> { | ||||
|  | @ -45,18 +45,16 @@ export class MessagesManager extends BaseManager<MessagePayload, Message> { | |||
|             (data as MessagePayload).author | ||||
|           ) | ||||
| 
 | ||||
|           // TODO: Make this thing work (MessageMentions)
 | ||||
|           const mentions = new MessageMentions() | ||||
| 
 | ||||
|           resolve( | ||||
|             new Message( | ||||
|               this.client, | ||||
|               data as MessagePayload, | ||||
|               channel as TextChannel, | ||||
|               author, | ||||
|               mentions | ||||
|             ) | ||||
|           const res = new Message( | ||||
|             this.client, | ||||
|             data as MessagePayload, | ||||
|             channel as TextChannel, | ||||
|             author | ||||
|           ) | ||||
| 
 | ||||
|           await res.mentions.fromPayload(data) | ||||
| 
 | ||||
|           resolve(res) | ||||
|         }) | ||||
|         .catch(e => reject(e)) | ||||
|     }) | ||||
|  |  | |||
|  | @ -35,6 +35,8 @@ export class Command { | |||
|   description?: string | ||||
|   /** Array of Aliases of Command, or only string */ | ||||
|   aliases?: string | string[] | ||||
|   /** Category of the Command */ | ||||
|   category?: string | ||||
|   /** Usage of Command, only Argument Names */ | ||||
|   usage?: string | string[] | ||||
|   /** Usage Example of Command, only Arguments (without Prefix and Name) */ | ||||
|  | @ -50,19 +52,29 @@ export class Command { | |||
|   /** Whether the Command can only be used by Bot Owners */ | ||||
|   ownerOnly?: boolean | ||||
| 
 | ||||
|   execute (ctx?: CommandContext): any {} | ||||
|   /** Method executed before executing actual command. Returns bool value - whether to continue or not (optional) */ | ||||
|   beforeExecute(ctx: CommandContext): boolean | Promise<boolean> { return true } | ||||
|   /** Actual command code, which is executed when all checks have passed. */ | ||||
|   execute(ctx: CommandContext): any { } | ||||
|   /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ | ||||
|   afterExecute(ctx: CommandContext, executeResult: any): any { } | ||||
| } | ||||
| 
 | ||||
| export class CommandsManager { | ||||
|   client: CommandClient | ||||
|   list: Collection<string, Command> = new Collection() | ||||
|   disabled: Set<string> = new Set() | ||||
|   disabledCategories: Set<string> = new Set() | ||||
| 
 | ||||
|   constructor (client: CommandClient) { | ||||
|   constructor(client: CommandClient) { | ||||
|     this.client = client | ||||
|   } | ||||
| 
 | ||||
|   /** Number of loaded Commands */ | ||||
|   get count(): number { return this.list.size } | ||||
| 
 | ||||
|   /** Find a Command by name/alias */ | ||||
|   find (search: string): Command | undefined { | ||||
|   find(search: string): Command | undefined { | ||||
|     if (this.client.caseSensitive === false) search = search.toLowerCase() | ||||
|     return this.list.find((cmd: Command): boolean => { | ||||
|       const name = | ||||
|  | @ -79,8 +91,17 @@ export class CommandsManager { | |||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** Fetch a Command including disable checks */ | ||||
|   fetch(name: string, bypassDisable?: boolean): Command | undefined { | ||||
|     const cmd = this.find(name) | ||||
|     if (cmd === undefined) return | ||||
|     if (this.isDisabled(cmd) && bypassDisable !== true) return | ||||
|     if (cmd.category !== undefined && this.isCategoryDisabled(cmd.category) && bypassDisable !== true) return | ||||
|     return cmd | ||||
|   } | ||||
| 
 | ||||
|   /** Check whether a Command exists or not */ | ||||
|   exists (search: Command | string): boolean { | ||||
|   exists(search: Command | string): boolean { | ||||
|     let exists = false | ||||
|     if (typeof search === 'string') return this.find(search) !== undefined | ||||
|     else { | ||||
|  | @ -97,7 +118,7 @@ export class CommandsManager { | |||
|   } | ||||
| 
 | ||||
|   /** Add a Command */ | ||||
|   add (cmd: Command | typeof Command): boolean { | ||||
|   add(cmd: Command | typeof Command): boolean { | ||||
|     // eslint-disable-next-line new-cap
 | ||||
|     if (!(cmd instanceof Command)) cmd = new cmd() | ||||
|     if (this.exists(cmd)) return false | ||||
|  | @ -106,11 +127,46 @@ export class CommandsManager { | |||
|   } | ||||
| 
 | ||||
|   /** Delete a Command */ | ||||
|   delete (cmd: string | Command): boolean { | ||||
|   delete(cmd: string | Command): boolean { | ||||
|     const find = typeof cmd === 'string' ? this.find(cmd) : cmd | ||||
|     if (find === undefined) return false | ||||
|     else return this.list.delete(find.name) | ||||
|   } | ||||
| 
 | ||||
|   /** Get all Commands of given Category */ | ||||
|   category(name: string): Collection<string, Command> { | ||||
|     return this.list.filter(c => c.category === name) | ||||
|   } | ||||
| 
 | ||||
|   /** Check whether a Command is disabled or not */ | ||||
|   isDisabled(name: string | Command): boolean { | ||||
|     const cmd = typeof name === "string" ? this.find(name) : name | ||||
|     if (cmd === undefined) return false | ||||
|     const exists = this.exists(name) | ||||
|     if (!exists) return false | ||||
|     return this.disabled.has(cmd.name) | ||||
|   } | ||||
| 
 | ||||
|   /** Disable a Command */ | ||||
|   disable(name: string | Command): boolean { | ||||
|     const cmd = typeof name === "string" ? this.find(name) : name | ||||
|     if (cmd === undefined) return false | ||||
|     if (this.isDisabled(cmd)) return false | ||||
|     this.disabled.add(cmd.name) | ||||
|     return true | ||||
|   } | ||||
| 
 | ||||
|   /** Check whether a Category is disabled */ | ||||
|   isCategoryDisabled(name: string): boolean { | ||||
|     return this.disabledCategories.has(name) | ||||
|   } | ||||
| 
 | ||||
|   /** Disable a Category of Commands */ | ||||
|   disableCategory(name: string): boolean { | ||||
|     if (this.isCategoryDisabled(name)) return false | ||||
|     this.disabledCategories.add(name) | ||||
|     return true | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export interface ParsedCommand { | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import { Embed, Message } from '../../mod.ts' | ||||
| import { awaitSync } from "../utils/mixedPromise.ts" | ||||
| import { Client, ClientOptions } from './client.ts' | ||||
| import { CommandContext, CommandsManager, parseCommand } from './command.ts' | ||||
| 
 | ||||
|  | @ -9,6 +10,9 @@ export interface CommandClientOptions extends ClientOptions { | |||
|   mentionPrefix?: boolean | ||||
|   getGuildPrefix?: (guildID: string) => PrefixReturnType | ||||
|   getUserPrefix?: (userID: string) => PrefixReturnType | ||||
|   isGuildBlacklisted?: (guildID: string) => boolean | Promise<boolean> | ||||
|   isUserBlacklisted?: (guildID: string) => boolean | Promise<boolean> | ||||
|   isChannelBlacklisted?: (guildID: string) => boolean | Promise<boolean> | ||||
|   spacesAfterPrefix?: boolean | ||||
|   betterArgs?: boolean | ||||
|   owners?: string[] | ||||
|  | @ -49,6 +53,9 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
|   mentionPrefix: boolean | ||||
|   getGuildPrefix: (guildID: string) => PrefixReturnType | ||||
|   getUserPrefix: (userID: string) => PrefixReturnType | ||||
|   isGuildBlacklisted: (guildID: string) => boolean | Promise<boolean> | ||||
|   isUserBlacklisted: (guildID: string) => boolean | Promise<boolean> | ||||
|   isChannelBlacklisted: (guildID: string) => boolean | Promise<boolean> | ||||
|   spacesAfterPrefix: boolean | ||||
|   betterArgs: boolean | ||||
|   owners: string[] | ||||
|  | @ -58,7 +65,7 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
|   commands: CommandsManager = new CommandsManager(this) | ||||
|   texts: CommandTexts = DefaultCommandTexts | ||||
| 
 | ||||
|   constructor (options: CommandClientOptions) { | ||||
|   constructor(options: CommandClientOptions) { | ||||
|     super(options) | ||||
|     this.prefix = options.prefix | ||||
|     this.mentionPrefix = | ||||
|  | @ -71,6 +78,18 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
|       options.getUserPrefix === undefined | ||||
|         ? (id: string) => this.prefix | ||||
|         : options.getUserPrefix | ||||
|     this.isUserBlacklisted = | ||||
|       options.isUserBlacklisted === undefined | ||||
|         ? (id: string) => false | ||||
|         : options.isUserBlacklisted | ||||
|     this.isGuildBlacklisted = | ||||
|       options.isGuildBlacklisted === undefined | ||||
|         ? (id: string) => false | ||||
|         : options.isGuildBlacklisted | ||||
|     this.isChannelBlacklisted = | ||||
|       options.isChannelBlacklisted === undefined | ||||
|         ? (id: string) => false | ||||
|         : options.isChannelBlacklisted | ||||
|     this.spacesAfterPrefix = | ||||
|       options.spacesAfterPrefix === undefined | ||||
|         ? false | ||||
|  | @ -89,29 +108,52 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   async processMessage (msg: Message): Promise<any> { | ||||
|   async processMessage(msg: Message): Promise<any> { | ||||
|     if (!this.allowBots && msg.author.bot === true) return | ||||
| 
 | ||||
|     const isUserBlacklisted = await awaitSync(this.isUserBlacklisted(msg.author.id)) | ||||
|     if (isUserBlacklisted === true) return | ||||
| 
 | ||||
|     const isChannelBlacklisted = await awaitSync(this.isChannelBlacklisted(msg.channel.id)) | ||||
|     if (isChannelBlacklisted === true) return | ||||
| 
 | ||||
|     if (msg.guild !== undefined) { | ||||
|       const isGuildBlacklisted = await awaitSync(this.isGuildBlacklisted(msg.guild.id)) | ||||
|       if (isGuildBlacklisted === true) return | ||||
|     } | ||||
| 
 | ||||
|     let prefix: string | string[] = this.prefix | ||||
| 
 | ||||
|     if (msg.guild !== undefined) { | ||||
|       let guildPrefix = this.getGuildPrefix(msg.guild.id) | ||||
|       if (guildPrefix instanceof Promise) guildPrefix = await guildPrefix | ||||
|       prefix = guildPrefix | ||||
|       prefix = await awaitSync(this.getGuildPrefix(msg.guild.id)) | ||||
|     } else { | ||||
|       let userPrefix = this.getUserPrefix(msg.author.id) | ||||
|       if (userPrefix instanceof Promise) userPrefix = await userPrefix | ||||
|       prefix = userPrefix | ||||
|       prefix = await awaitSync(this.getUserPrefix(msg.author.id)) | ||||
|     } | ||||
| 
 | ||||
|     let mentionPrefix = false | ||||
| 
 | ||||
|     if (typeof prefix === 'string') { | ||||
|       if (msg.content.startsWith(prefix) === false) return | ||||
|       if (msg.content.startsWith(prefix) === false) { | ||||
|         if (this.mentionPrefix) mentionPrefix = true | ||||
|         else return | ||||
|       } | ||||
|     } else { | ||||
|       const usedPrefix = prefix.find(v => msg.content.startsWith(v)) | ||||
|       if (usedPrefix === undefined) return | ||||
|       if (usedPrefix === undefined) { | ||||
|         if (this.mentionPrefix) mentionPrefix = true | ||||
|         else return | ||||
|       } | ||||
|       else prefix = usedPrefix | ||||
|     } | ||||
| 
 | ||||
|     if (mentionPrefix) { | ||||
|       if (msg.content.startsWith(this.user?.mention as string) === true) prefix = this.user?.mention as string | ||||
|       else if (msg.content.startsWith(this.user?.nickMention as string) === true) prefix = this.user?.nickMention as string | ||||
|       else return | ||||
|     } | ||||
| 
 | ||||
|     if (typeof prefix !== 'string') return | ||||
| 
 | ||||
|     const parsed = parseCommand(this, msg, prefix) | ||||
|     const command = this.commands.find(parsed.name) | ||||
| 
 | ||||
|  | @ -158,10 +200,15 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
| 
 | ||||
|     try { | ||||
|       this.emit('commandUsed', { context: ctx }) | ||||
|       command.execute(ctx) | ||||
| 
 | ||||
|       const beforeExecute = await awaitSync(command.beforeExecute(ctx)) | ||||
|       if (beforeExecute === false) return | ||||
| 
 | ||||
|       const result = await awaitSync(command.execute(ctx)) | ||||
|       command.afterExecute(ctx, result) | ||||
|     } catch (e) { | ||||
|       if (this.texts.ERROR !== undefined) | ||||
|         return this.sendProcessedText( | ||||
|         this.sendProcessedText( | ||||
|           msg, | ||||
|           this.texts.ERROR, | ||||
|           Object.assign(baseReplaces, { error: e.message }) | ||||
|  | @ -170,7 +217,7 @@ export class CommandClient extends Client implements CommandClientOptions { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   sendProcessedText (msg: Message, text: CommandText, replaces: Replaces): any { | ||||
|   sendProcessedText(msg: Message, text: CommandText, replaces: Replaces): any { | ||||
|     if (typeof text === 'string') { | ||||
|       text = massReplace(text, replaces) | ||||
|       return msg.channel.send(text) | ||||
|  |  | |||
|  | @ -25,13 +25,13 @@ export interface RequestHeaders { | |||
| } | ||||
| 
 | ||||
| export interface QueuedItem { | ||||
|   bucket?: string | null | ||||
|   url: string | ||||
|   onComplete: () => Promise<{ | ||||
|     rateLimited: any | ||||
|     bucket?: string | null | ||||
|     before: boolean | ||||
|   } | undefined> | ||||
|   bucket?: string | null | ||||
|   url: string | ||||
| } | ||||
| 
 | ||||
| export interface RateLimit { | ||||
|  | @ -93,10 +93,8 @@ export class RESTManager { | |||
|               request.bucket | ||||
|             ) | ||||
|             if (rateLimitResetIn !== false) { | ||||
|               // This request is still rate limited read to queue
 | ||||
|               this.queue(request) | ||||
|             } else { | ||||
|               // This request is not rate limited so it should be run
 | ||||
|               const result = await request.onComplete() | ||||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.queue({ | ||||
|  | @ -107,10 +105,8 @@ export class RESTManager { | |||
|             } | ||||
|           } else { | ||||
|             if (rateLimitedURLResetIn !== false) { | ||||
|               // This URL is rate limited readd to queue
 | ||||
|               this.queue(request) | ||||
|             } else { | ||||
|               // This request has no bucket id so it should be processed
 | ||||
|               const result = await request.onComplete() | ||||
|               if (result?.rateLimited !== undefined) { | ||||
|                 this.queue({ | ||||
|  | @ -253,27 +249,35 @@ export class RESTManager { | |||
|   } | ||||
| 
 | ||||
|   async handleStatusCode( | ||||
|     response: Response | ||||
|     response: Response, body: any, data: { [key: string]: any } | ||||
|   ): Promise<undefined> { | ||||
|     const status = response.status | ||||
| 
 | ||||
|     if ((status >= 200 && status < 400) || status === HttpResponseCode.TooManyRequests) return | ||||
|     if ( | ||||
|       (status >= 200 && status < 400) | ||||
|       || status === HttpResponseCode.NoContent  | ||||
|       || status === HttpResponseCode.TooManyRequests | ||||
|     ) return | ||||
| 
 | ||||
|     const body = await response.json() | ||||
|     const text = Deno.inspect(body.errors) | ||||
|     let text: undefined | string = Deno.inspect(body.errors === undefined ? body : body.errors) | ||||
|     if (text === 'undefined') text = undefined | ||||
| 
 | ||||
|     if (status === HttpResponseCode.Unauthorized) | ||||
|       throw new Error(`Request was not successful (Unauthorized). Invalid Token.\n${text}`) | ||||
| 
 | ||||
|     // At this point we know it is error
 | ||||
|     let error = { url: response.url, status, method: data.method, body: data.body } | ||||
|     if (body !== undefined) error = Object.assign(error, body) | ||||
| 
 | ||||
|     if ([ | ||||
|       HttpResponseCode.BadRequest, | ||||
|       HttpResponseCode.NotFound, | ||||
|       HttpResponseCode.Forbidden, | ||||
|       HttpResponseCode.MethodNotAllowed | ||||
|     ].includes(status)) { | ||||
|       throw new Error(`Request - Client Error. Code: ${status}\n${text}`) | ||||
|       throw new Error(Deno.inspect(error)) | ||||
|     } else if (status === HttpResponseCode.GatewayUnavailable) { | ||||
|       throw new Error(`Request - Server Error. Code: ${status}\n${text}`) | ||||
|       throw new Error(Deno.inspect(error)) | ||||
|     } else throw new Error('Request - Unknown Error') | ||||
|   } | ||||
| 
 | ||||
|  | @ -319,12 +323,13 @@ export class RESTManager { | |||
| 
 | ||||
|           const response = await fetch(urlToUse, requestData) | ||||
|           const bucketFromHeaders = this.processHeaders(url, response.headers) | ||||
|           // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|           this.handleStatusCode(response) | ||||
| 
 | ||||
|           if (response.status === 204) return resolve(undefined) | ||||
| 
 | ||||
|           const json = await response.json() | ||||
|           const json: any = await response.json() | ||||
|           // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
|           this.handleStatusCode(response, json, requestData) | ||||
| 
 | ||||
|           if ( | ||||
|             json.retry_after !== undefined || | ||||
|             json.message === 'You are being rate limited.' | ||||
|  |  | |||
|  | @ -1,48 +0,0 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { GuildTextChannelPayload, Overwrite } from '../types/channel.ts' | ||||
| import { TextChannel } from './textChannel.ts' | ||||
| import { Guild } from './guild.ts' | ||||
| 
 | ||||
| export class GuildTextChannel extends TextChannel { | ||||
|   guildID: string | ||||
|   name: string | ||||
|   position: number | ||||
|   permissionOverwrites: Overwrite[] | ||||
|   nsfw: boolean | ||||
|   parentID?: string | ||||
|   rateLimit: number | ||||
|   topic?: string | ||||
|   guild: Guild | ||||
| 
 | ||||
|   get mention (): string { | ||||
|     return `<#${this.id}>` | ||||
|   } | ||||
| 
 | ||||
|   constructor (client: Client, data: GuildTextChannelPayload, guild: Guild) { | ||||
|     super(client, data) | ||||
|     this.guildID = data.guild_id | ||||
|     this.name = data.name | ||||
|     this.guild = guild | ||||
|     this.position = data.position | ||||
|     this.permissionOverwrites = data.permission_overwrites | ||||
|     this.nsfw = data.nsfw | ||||
|     this.parentID = data.parent_id | ||||
|     this.topic = data.topic | ||||
|     this.rateLimit = data.rate_limit_per_user | ||||
|     // TODO: Cache in Gateway Event Code
 | ||||
|     // cache.set('guildtextchannel', this.id, this)
 | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: GuildTextChannelPayload): void { | ||||
|     super.readFromData(data) | ||||
|     this.guildID = data.guild_id ?? this.guildID | ||||
|     this.name = data.name ?? this.name | ||||
|     this.position = data.position ?? this.position | ||||
|     this.permissionOverwrites = | ||||
|       data.permission_overwrites ?? this.permissionOverwrites | ||||
|     this.nsfw = data.nsfw ?? this.nsfw | ||||
|     this.parentID = data.parent_id ?? this.parentID | ||||
|     this.topic = data.topic ?? this.topic | ||||
|     this.rateLimit = data.rate_limit_per_user ?? this.rateLimit | ||||
|   } | ||||
| } | ||||
|  | @ -1,28 +1,30 @@ | |||
| import { MemberRolesManager } from "../managers/memberRoles.ts" | ||||
| import { Client } from '../models/client.ts' | ||||
| import { MemberPayload } from '../types/guild.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { Role } from './role.ts' | ||||
| import { Guild } from "./guild.ts" | ||||
| import { User } from './user.ts' | ||||
| 
 | ||||
| export class Member extends Base { | ||||
|   id: string | ||||
|   user: User | ||||
|   nick?: string | ||||
|   roleIDs: string[] | ||||
|   roles: Role[] = [] | ||||
|   roles: MemberRolesManager | ||||
|   joinedAt: string | ||||
|   premiumSince?: string | ||||
|   deaf: boolean | ||||
|   mute: boolean | ||||
|   guild: Guild | ||||
| 
 | ||||
|   constructor (client: Client, data: MemberPayload, user: User) { | ||||
|   constructor (client: Client, data: MemberPayload, user: User, guild: Guild) { | ||||
|     super(client) | ||||
|     this.id = data.user.id | ||||
|     this.user = user | ||||
|     // this.user =
 | ||||
|     //   cache.get('user', data.user.id) ?? new User(this.client, data.user)
 | ||||
|     this.nick = data.nick | ||||
|     this.roleIDs = data.roles | ||||
|     this.guild = guild | ||||
|     this.roles = new MemberRolesManager(this.client, this.guild.roles, this) | ||||
|     this.joinedAt = data.joined_at | ||||
|     this.premiumSince = data.premium_since | ||||
|     this.deaf = data.deaf | ||||
|  | @ -34,7 +36,6 @@ export class Member extends Base { | |||
|   protected readFromData (data: MemberPayload): void { | ||||
|     super.readFromData(data.user) | ||||
|     this.nick = data.nick ?? this.nick | ||||
|     this.roleIDs = data.roles ?? this.roles.map(r => r.id) | ||||
|     this.joinedAt = data.joined_at ?? this.joinedAt | ||||
|     this.premiumSince = data.premium_since ?? this.premiumSince | ||||
|     this.deaf = data.deaf ?? this.deaf | ||||
|  |  | |||
|  | @ -19,9 +19,9 @@ import { TextChannel } from './textChannel.ts' | |||
| import { DMChannel } from './dmChannel.ts' | ||||
| import { Guild } from './guild.ts' | ||||
| 
 | ||||
| type AllMessageOptions = MessageOption | Embed | ||||
| 
 | ||||
| export class Message extends Base { | ||||
|   // eslint-disable-next-line @typescript-eslint/prefer-readonly
 | ||||
|   private data: MessagePayload | ||||
|   id: string | ||||
|   channelID: string | ||||
|   channel: TextChannel | ||||
|  | @ -53,26 +53,19 @@ export class Message extends Base { | |||
|     client: Client, | ||||
|     data: MessagePayload, | ||||
|     channel: TextChannel, | ||||
|     author: User, | ||||
|     mentions: MessageMentions | ||||
|     author: User | ||||
|   ) { | ||||
|     super(client) | ||||
|     this.data = data | ||||
|     this.id = data.id | ||||
|     this.channelID = data.channel_id | ||||
|     this.guildID = data.guild_id | ||||
|     this.author = author | ||||
|     // this.author =
 | ||||
|     //   this.client.users.get(data.author.id) || new User(this.client, data.author)
 | ||||
|     this.content = data.content | ||||
|     this.timestamp = data.timestamp | ||||
|     this.editedTimestamp = data.edited_timestamp | ||||
|     this.tts = data.tts | ||||
|     this.mentionEveryone = data.mention_everyone | ||||
|     this.mentions = mentions | ||||
|     // this.mentions = data.mentions.map(
 | ||||
|     //   v => this.client.users.get(v.id) || new User(client, v)
 | ||||
|     // )
 | ||||
|     this.mentions = new MessageMentions(this.client, this) | ||||
|     this.mentionRoles = data.mention_roles | ||||
|     this.mentionChannels = data.mention_channels | ||||
|     this.attachments = data.attachments | ||||
|  | @ -87,27 +80,17 @@ export class Message extends Base { | |||
|     this.messageReference = data.message_reference | ||||
|     this.flags = data.flags | ||||
|     this.channel = channel | ||||
|     // TODO: Cache in Gateway Event Code
 | ||||
|     // if (!noSave) this.client.messages.set(this.id, data)
 | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: MessagePayload): void { | ||||
|     super.readFromData(data) | ||||
|     this.channelID = data.channel_id ?? this.channelID | ||||
|     this.guildID = data.guild_id ?? this.guildID | ||||
|     // this.author =
 | ||||
|     //   this.client.users.get(data.author.id) ||
 | ||||
|     //   this.author ||
 | ||||
|     //   new User(this.client, data.author)
 | ||||
|     this.content = data.content ?? this.content | ||||
|     this.timestamp = data.timestamp ?? this.timestamp | ||||
|     this.editedTimestamp = data.edited_timestamp ?? this.editedTimestamp | ||||
|     this.tts = data.tts ?? this.tts | ||||
|     this.mentionEveryone = data.mention_everyone ?? this.mentionEveryone | ||||
|     // this.mentions =
 | ||||
|     //   data.mentions.map(
 | ||||
|     //     v => this.client.users.get(v.id) || new User(this.client, v)
 | ||||
|     //   ) ?? this.mentions
 | ||||
|     this.mentionRoles = data.mention_roles ?? this.mentionRoles | ||||
|     this.mentionChannels = data.mention_channels ?? this.mentionChannels | ||||
|     this.attachments = data.attachments ?? this.attachments | ||||
|  | @ -124,13 +107,13 @@ export class Message extends Base { | |||
|   } | ||||
| 
 | ||||
|   async edit (text?: string, option?: MessageOption): Promise<Message> { | ||||
|     return this.channel.edit(this.id, text, option)   | ||||
|     return this.channel.editMessage(this.id, text, option)   | ||||
|   } | ||||
| 
 | ||||
|   async reply(text: string, options?: MessageOption): Promise<Message> { | ||||
|   async reply(text?: string | AllMessageOptions, option?: AllMessageOptions): Promise<Message> { | ||||
|     // TODO: Use inline replies once they're out
 | ||||
|     if (this.channel instanceof DMChannel) return this.channel.send(text, options) | ||||
|     return this.channel.send(`${this.author.mention}, ${text}`, options) | ||||
|     if (this.channel instanceof DMChannel) return this.channel.send(text, option) | ||||
|     return this.channel.send(`${this.author.mention}, ${text}`, option) | ||||
|   } | ||||
| 
 | ||||
|   async delete (): Promise<void> { | ||||
|  |  | |||
|  | @ -1,3 +1,55 @@ | |||
| import { Client } from "../models/client.ts"; | ||||
| import { MessagePayload } from "../types/channel.ts"; | ||||
| import { Collection } from "../utils/collection.ts"; | ||||
| import { GuildTextChannel } from "./textChannel.ts"; | ||||
| import { Message } from "./message.ts"; | ||||
| import { Role } from "./role.ts"; | ||||
| import { User } from "./user.ts"; | ||||
| 
 | ||||
| export class MessageMentions { | ||||
|     str: string = "str" | ||||
|   client: Client | ||||
|   message: Message | ||||
|   users: Collection<string, User> = new Collection() | ||||
|   roles: Collection<string, Role> = new Collection() | ||||
|   channels: Collection<string, GuildTextChannel> = new Collection() | ||||
|   everyone: boolean = false | ||||
| 
 | ||||
|   static EVERYONE_MENTION = /@(everyone|here)/g | ||||
|   static USER_MENTION = /<@!?(\d{17,19})>/g | ||||
|   static ROLE_MENTION = /<@&(\d{17,19})>/g | ||||
|   static CHANNEL_MENTION = /<#(\d{17,19})>/g | ||||
| 
 | ||||
|   constructor(client: Client, message: Message) { | ||||
|     this.client = client | ||||
|     this.message = message | ||||
|   } | ||||
| 
 | ||||
|   async fromPayload(payload: MessagePayload): Promise<MessageMentions> { | ||||
|     payload.mentions.forEach(rawUser => { | ||||
|       this.users.set(rawUser.id, new User(this.client, rawUser)) | ||||
|     }) | ||||
|      | ||||
|     if (this.message.guild !== undefined) { | ||||
|       for (const id of payload.mention_roles) { | ||||
|         const role = await this.message.guild.roles.get(id) | ||||
|         if(role !== undefined) this.roles.set(role.id, role) | ||||
|       } | ||||
|     } | ||||
|     if (payload.mention_channels !== undefined) { | ||||
|       for (const mentionChannel of payload.mention_channels) { | ||||
|         const channel = await this.client.channels.get<GuildTextChannel>(mentionChannel.id) | ||||
|         if (channel !== undefined) this.channels.set(channel.id, channel) | ||||
|       } | ||||
|     } | ||||
|     const matchChannels = this.message.content.match(MessageMentions.CHANNEL_MENTION) | ||||
|     if (matchChannels !== null) { | ||||
|       for (const id of matchChannels) { | ||||
|         const parsedID = id.substr(2, id.length - 3) | ||||
|         const channel = await this.client.channels.get<GuildTextChannel>(parsedID) | ||||
|         if (channel !== undefined) this.channels.set(channel.id, channel) | ||||
|       } | ||||
|     } | ||||
|     this.everyone = payload.mention_everyone | ||||
|     return this | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ export class Role extends Base { | |||
|     return `<@&${this.id}>` | ||||
|   } | ||||
| 
 | ||||
|   toString(): string { return this.mention } | ||||
| 
 | ||||
|   constructor (client: Client, data: RolePayload) { | ||||
|     super(client, data) | ||||
|     this.id = data.id | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { MessageOption, TextChannelPayload } from '../types/channel.ts' | ||||
| import { GuildTextChannelPayload, MessageOption, Overwrite, TextChannelPayload } from '../types/channel.ts' | ||||
| import { CHANNEL_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts' | ||||
| import { Channel } from './channel.ts' | ||||
| import { Embed } from './embed.ts' | ||||
| import { Guild } from "./guild.ts" | ||||
| import { Message } from './message.ts' | ||||
| import { MessageMentions } from './messageMentions.ts' | ||||
| 
 | ||||
| type AllMessageOptions = MessageOption | Embed | ||||
| 
 | ||||
|  | @ -16,8 +16,6 @@ export class TextChannel extends Channel { | |||
|     super(client, data) | ||||
|     this.lastMessageID = data.last_message_id | ||||
|     this.lastPinTimestamp = data.last_pin_timestamp | ||||
|     // TODO: Cache in Gateway Event Code
 | ||||
|     // cache.set('textchannel', this.id, this)
 | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: TextChannelPayload): void { | ||||
|  | @ -46,10 +44,12 @@ export class TextChannel extends Channel { | |||
|         allowed_mentions: option?.allowedMention | ||||
|     }) | ||||
| 
 | ||||
|     return new Message(this.client, resp as any, this, this.client.user as any, new MessageMentions()) | ||||
|     const res = new Message(this.client, resp, this, this.client.user as any) | ||||
|     await res.mentions.fromPayload(resp) | ||||
|     return res | ||||
|   } | ||||
| 
 | ||||
|   async edit ( | ||||
|   async editMessage ( | ||||
|     message: Message | string, | ||||
|     text?: string, | ||||
|     option?: MessageOption | ||||
|  | @ -76,9 +76,54 @@ export class TextChannel extends Channel { | |||
|       } | ||||
|     ) | ||||
| 
 | ||||
|     // TODO: Actually construct this object
 | ||||
|     const mentions = new MessageMentions() | ||||
| 
 | ||||
|     return new Message(this.client, newMsg, this, this.client.user, mentions) | ||||
|     const res = new Message(this.client, newMsg, this, this.client.user) | ||||
|     await res.mentions.fromPayload(newMsg) | ||||
|     return res | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export class GuildTextChannel extends TextChannel { | ||||
|   guildID: string | ||||
|   name: string | ||||
|   position: number | ||||
|   permissionOverwrites: Overwrite[] | ||||
|   nsfw: boolean | ||||
|   parentID?: string | ||||
|   rateLimit: number | ||||
|   topic?: string | ||||
|   guild: Guild | ||||
| 
 | ||||
|   get mention (): string { | ||||
|     return `<#${this.id}>` | ||||
|   } | ||||
| 
 | ||||
|   toString(): string { | ||||
|     return this.mention | ||||
|   } | ||||
| 
 | ||||
|   constructor (client: Client, data: GuildTextChannelPayload, guild: Guild) { | ||||
|     super(client, data) | ||||
|     this.guildID = data.guild_id | ||||
|     this.name = data.name | ||||
|     this.guild = guild | ||||
|     this.position = data.position | ||||
|     this.permissionOverwrites = data.permission_overwrites | ||||
|     this.nsfw = data.nsfw | ||||
|     this.parentID = data.parent_id | ||||
|     this.topic = data.topic | ||||
|     this.rateLimit = data.rate_limit_per_user | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: GuildTextChannelPayload): void { | ||||
|     super.readFromData(data) | ||||
|     this.guildID = data.guild_id ?? this.guildID | ||||
|     this.name = data.name ?? this.name | ||||
|     this.position = data.position ?? this.position | ||||
|     this.permissionOverwrites = | ||||
|       data.permission_overwrites ?? this.permissionOverwrites | ||||
|     this.nsfw = data.nsfw ?? this.nsfw | ||||
|     this.parentID = data.parent_id ?? this.parentID | ||||
|     this.topic = data.topic ?? this.topic | ||||
|     this.rateLimit = data.rate_limit_per_user ?? this.rateLimit | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,10 @@ | |||
| import { CommandClient, Intents } from '../../mod.ts' | ||||
| import PingCommand from './cmds/ping.ts' | ||||
| import AddEmojiCommand from './cmds/addemoji.ts' | ||||
| import UserinfoCommand from './cmds/userinfo.ts' | ||||
| import { TOKEN } from './config.ts' | ||||
| 
 | ||||
| const client = new CommandClient({ | ||||
|   prefix: ["pls", "!"], | ||||
|   spacesAfterPrefix: true | ||||
|   spacesAfterPrefix: true, | ||||
|   mentionPrefix: true | ||||
| }) | ||||
| 
 | ||||
| client.on('debug', console.log) | ||||
|  | @ -15,10 +13,23 @@ client.on('ready', () => { | |||
|   console.log(`[Login] Logged in as ${client.user?.tag}!`) | ||||
| }) | ||||
| 
 | ||||
| // client.on('messageCreate', msg => console.log(`${msg.author.tag}: ${msg.content}`))
 | ||||
| 
 | ||||
| client.on("commandError", console.error) | ||||
| 
 | ||||
| client.commands.add(PingCommand) | ||||
| client.commands.add(UserinfoCommand) | ||||
| client.commands.add(AddEmojiCommand) | ||||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||
| ;(async() => { | ||||
|   const files = Deno.readDirSync('./src/test/cmds') | ||||
| 
 | ||||
| client.connect(TOKEN, Intents.All) | ||||
|   for (const file of files) { | ||||
|     const module = await import(`./cmds/${file.name}`) | ||||
|     // eslint-disable-next-line new-cap
 | ||||
|     const cmd = new module.default() | ||||
|     client.commands.add(cmd) | ||||
|     console.log(`Loaded command ${cmd.name}!`) | ||||
|   } | ||||
| 
 | ||||
|   console.log(`Loaded ${client.commands.count} commands!`) | ||||
| 
 | ||||
|   client.connect(TOKEN, Intents.All) | ||||
| })() | ||||
							
								
								
									
										18
									
								
								src/test/cmds/mentions.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/test/cmds/mentions.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import { Command, Embed } from '../../../mod.ts' | ||||
| import { CommandContext } from '../../models/command.ts' | ||||
| 
 | ||||
| export default class PingCommand extends Command { | ||||
|   name = "mentions" | ||||
|   aliases = ["m"] | ||||
| 
 | ||||
|   execute(ctx: CommandContext): void { | ||||
|     const embed = new Embed() | ||||
|       .setTitle('Mentions') | ||||
|       .addField('Users', `${ctx.message.mentions.users.size === 0 ? `None` : ''}${ctx.message.mentions.users.map(u => u.toString()).join(", ")}`) | ||||
|       .addField('Channels', `${ctx.message.mentions.channels.size === 0 ? `None` : ''}${ctx.message.mentions.channels.map(u => u.toString()).join(", ")}`) | ||||
|       .addField('Roles', `${ctx.message.mentions.roles.size === 0 ? `None` : ''}${ctx.message.mentions.roles.map(u => u.toString()).join(", ")}`) | ||||
|       .addField('Everyone?', ctx.message.mentions.everyone === true ? 'Yes' : 'No') | ||||
|       .setColor(0xff0000) | ||||
|     ctx.message.channel.send(embed) | ||||
|   } | ||||
| } | ||||
|  | @ -3,14 +3,17 @@ import { Command, Member, CommandContext, Embed } from '../../../mod.ts' | |||
| export default class UserinfoCommand extends Command { | ||||
|     name = "userinfo" | ||||
|     guildOnly = true | ||||
|     aliases = [ 'u', 'user' ] | ||||
| 
 | ||||
|     execute(ctx: CommandContext): void { | ||||
|     async execute(ctx: CommandContext): Promise<void> { | ||||
|         const member: Member = ctx.message.member as any | ||||
|         const roles = await member.roles.array() | ||||
|         const embed = new Embed() | ||||
|         .setTitle(`User Info`) | ||||
|         .setAuthor({ name: member.user.tag }) | ||||
|         .addField("ID", member.id) | ||||
|         .addField("Roles", member.roles.map(r => r.name).join(", ")) | ||||
|         .addField("Roles", roles.map(r => r.name).join(", ")) | ||||
|         .setColor(0xff00ff) | ||||
|         ctx.channel.send(embed) | ||||
|     } | ||||
| } | ||||
|  | @ -15,8 +15,7 @@ import { CategoryChannel } from '../structures/guildCategoryChannel.ts' | |||
| import { NewsChannel } from '../structures/guildNewsChannel.ts' | ||||
| import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import { GuildTextChannel } from '../structures/guildTextChannel.ts' | ||||
| import { TextChannel } from '../structures/textChannel.ts' | ||||
| import { TextChannel, GuildTextChannel } from '../structures/textChannel.ts' | ||||
| 
 | ||||
| const getChannelByType = ( | ||||
|   client: Client, | ||||
|  |  | |||
							
								
								
									
										3
									
								
								src/utils/mixedPromise.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/utils/mixedPromise.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| export const awaitSync = async(val: any | Promise<any>): Promise<any> => { | ||||
|     return val instanceof Promise ? await val : val | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue