Implement MessageMentions, mentionPrefix, Blocked Users/Channels/Guilds for Commands, self-bot support

This commit is contained in:
DjDeveloperr 2020-11-09 15:59:16 +05:30
parent 15ff747718
commit b935bb1238
17 changed files with 308 additions and 143 deletions

View file

@ -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
View file

@ -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'

View file

@ -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)

View file

@ -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)

View file

@ -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,

View file

@ -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(
const res = new Message(
this.client,
data as MessagePayload,
channel as TextChannel,
author,
mentions
)
author
)
await res.mentions.fromPayload(data)
resolve(res)
})
.catch(e => reject(e))
})

View file

@ -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,17 +52,27 @@ 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) {
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 {
if (this.client.caseSensitive === false) search = search.toLowerCase()
@ -79,6 +91,15 @@ 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 {
let exists = false
@ -111,6 +132,41 @@ export class CommandsManager {
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 {

View file

@ -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[]
@ -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
@ -92,26 +111,49 @@ export class CommandClient extends Client implements CommandClientOptions {
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,7 +200,12 @@ 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(

View file

@ -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
}
}

View file

@ -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> {

View file

@ -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
}
}

View file

@ -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

View file

@ -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()
const res = new Message(this.client, newMsg, this, this.client.user)
await res.mentions.fromPayload(newMsg)
return res
}
}
return new Message(this.client, newMsg, this, this.client.user, mentions)
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
}
}

View file

@ -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')
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
View 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)
}
}

View file

@ -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,

View file

@ -0,0 +1,3 @@
export const awaitSync = async(val: any | Promise<any>): Promise<any> => {
return val instanceof Promise ? await val : val
}