Fixed Member Caching, Message#member, Member#roles

This commit is contained in:
DjDeveloperr 2020-11-07 18:35:37 +05:30
parent 71c1499c1d
commit 70824135a7
13 changed files with 103 additions and 85 deletions

View file

@ -1,16 +1,15 @@
import { Gateway, GatewayEventHandler } from '../index.ts' import { Gateway, GatewayEventHandler } from '../index.ts'
import cache from '../../models/cache.ts'
import { Guild } from '../../structures/guild.ts' import { Guild } from '../../structures/guild.ts'
import { User } from '../../structures/user.ts' import { User } from '../../structures/user.ts'
import { GuildBanRemovePayload } from '../../types/gateway.ts' import { GuildBanRemovePayload } from '../../types/gateway.ts'
export const guildBanRemove: GatewayEventHandler = ( export const guildBanRemove: GatewayEventHandler = async (
gateway: Gateway, gateway: Gateway,
d: GuildBanRemovePayload d: GuildBanRemovePayload
) => { ) => {
const guild: Guild = cache.get('guild', d.guild_id) const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
const user: User = const user: User =
cache.get('user', d.user.id) ?? new User(gateway.client, d.user) await gateway.client.users.get(d.user.id) ?? new User(gateway.client, d.user)
if (guild !== undefined) { if (guild !== undefined) {
gateway.client.emit('guildBanRemove', guild, user) gateway.client.emit('guildBanRemove', guild, user)

View file

@ -17,11 +17,26 @@ export const messageCreate: GatewayEventHandler = async (
const user = new User(gateway.client, d.author) const user = new User(gateway.client, d.author)
await gateway.client.users.set(d.author.id, d.author) await gateway.client.users.set(d.author.id, d.author)
let guild let guild
let member
if (d.guild_id !== undefined) { if (d.guild_id !== undefined) {
guild = await gateway.client.guilds.get(d.guild_id) guild = await gateway.client.guilds.get(d.guild_id)
} }
if (guild !== undefined && d.member !== undefined) {
d.member.user = d.author
await guild.members.set(d.author.id, d.member)
member = await guild.members.get(d.author.id)
}
const mentions = new MessageMentions() const mentions = new MessageMentions()
const message = new Message(gateway.client, d, channel as any, user, mentions) const message = new Message(gateway.client, d, channel as any, user, mentions)
message.member = member
if (guild !== undefined) message.guild = guild if (guild !== undefined) message.guild = guild
if (message.member !== undefined) {
if(message.member.user === undefined) {
const user = await gateway.client.users.get(message.member.id)
if(user !== undefined) {
message.member.user = user
}
}
}
gateway.client.emit('messageCreate', message) gateway.client.emit('messageCreate', message)
} }

View file

@ -3,7 +3,9 @@ import { Collection } from "../utils/collection.ts";
export class BaseManager<T, T2> { export class BaseManager<T, T2> {
client: Client client: Client
/** Cache Name or Key used to differentiate caches */
cacheName: string cacheName: string
/** Which data type does this cache have */
DataType: any DataType: any
constructor (client: Client, cacheName: string, DataType: any) { constructor (client: Client, cacheName: string, DataType: any) {

View file

@ -1,3 +1,4 @@
import { User } from "../structures/user.ts";
import { Client } from "../models/client.ts"; import { Client } from "../models/client.ts";
import { Guild } from "../structures/guild.ts"; import { Guild } from "../structures/guild.ts";
import { Member } from "../structures/member.ts"; import { Member } from "../structures/member.ts";
@ -13,11 +14,29 @@ export class MembersManager extends BaseManager<MemberPayload, Member> {
this.guild = guild this.guild = guild
} }
async get (key: string): Promise<Member | undefined> {
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)
}
return res
}
async fetch(id: string): Promise<Member> { async fetch(id: string): Promise<Member> {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
this.client.rest.get(GUILD_MEMBER(this.guild.id, id)).then(data => { this.client.rest.get(GUILD_MEMBER(this.guild.id, id)).then(async data => {
this.set(id, data as MemberPayload) await this.set(id, data as MemberPayload)
resolve(new Member(this.client, 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)
}
resolve(res)
}).catch(e => reject(e)) }).catch(e => reject(e))
}) })
} }

View file

@ -1,48 +0,0 @@
let caches: any = {}
const get = (cacheName: string, key: string): any => {
const gotCache: Map<string, any> = caches[cacheName]
if (gotCache === undefined || !(gotCache instanceof Map)) {
return undefined
}
const gotMap = gotCache.get(key)
return gotMap
}
const set = (cacheName: string, key: string, value: any): any => {
let gotCache: Map<string, any> = caches[cacheName]
if (gotCache === undefined || !(gotCache instanceof Map)) {
gotCache = caches[cacheName] = new Map<string, any>()
}
gotCache.set(key, value)
return value
}
const del = (cacheName: string, key: string): boolean | undefined => {
const gotCache: Map<string, any> = caches[cacheName]
if (gotCache === undefined || !(gotCache instanceof Map)) {
return
}
return gotCache.delete(key)
}
const deleteCache = (cacheName: string): void => {
const gotCache = caches[cacheName]
if (gotCache === undefined) {
return
}
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete caches[cacheName]
}
const resetCaches = (): void => {
caches = {}
}
export default { get, set, del, deleteCache, resetCaches }
export { get, set, del, deleteCache, resetCaches }

View file

@ -13,12 +13,19 @@ import { ActivityGame, ClientActivity, ClientPresence } from "../structures/pres
/** Some Client Options to modify behaviour */ /** Some Client Options to modify behaviour */
export interface ClientOptions { export interface ClientOptions {
/** Token of the Bot/User */
token?: string token?: string
/** Gateway Intents */
intents?: GatewayIntents[] intents?: GatewayIntents[]
/** Cache Adapter to use, defaults to Collections one */
cache?: ICacheAdapter, cache?: ICacheAdapter,
/** Force New Session and don't use cached Session (by persistent caching) */
forceNewSession?: boolean, forceNewSession?: boolean,
/** Startup presence of client */
presence?: ClientPresence | ClientActivity | ActivityGame presence?: ClientPresence | ClientActivity | ActivityGame
/** Whether it's a bot user or not? Use this if selfbot! */
bot?: boolean bot?: boolean
/** Force all requests to Canary API */
canary?: boolean canary?: boolean
} }
@ -26,22 +33,34 @@ export interface ClientOptions {
* Discord Client. * Discord Client.
*/ */
export class Client extends EventEmitter { export class Client extends EventEmitter {
/** Gateway object */
gateway?: Gateway gateway?: Gateway
/** REST Manager - used to make all requests */
rest: RESTManager = new RESTManager(this) rest: RESTManager = new RESTManager(this)
/** User which Client logs in to, undefined until logs in */
user?: User user?: User
/** WebSocket ping of Client */
ping = 0 ping = 0
/** Token of the Bot/User */
token?: string token?: string
/** Cache Adapter */
cache: ICacheAdapter = new DefaultCacheAdapter() cache: ICacheAdapter = new DefaultCacheAdapter()
/** Gateway Intents */
intents?: GatewayIntents[] intents?: GatewayIntents[]
/** Whether to force new session or not */
forceNewSession?: boolean forceNewSession?: boolean
users: UserManager = new UserManager(this) users: UserManager = new UserManager(this)
guilds: GuildManager = new GuildManager(this) guilds: GuildManager = new GuildManager(this)
channels: ChannelsManager = new ChannelsManager(this) channels: ChannelsManager = new ChannelsManager(this)
messages: MessagesManager = new MessagesManager(this) messages: MessagesManager = new MessagesManager(this)
emojis: EmojisManager = new EmojisManager(this) emojis: EmojisManager = new EmojisManager(this)
/** Whether this client will login as bot user or not */
bot: boolean = true bot: boolean = true
/** Whether the REST Manager will use Canary API or not */
canary: boolean = false canary: boolean = false
/** Client's presence. Startup one if set before connecting */
presence: ClientPresence = new ClientPresence() presence: ClientPresence = new ClientPresence()
constructor (options: ClientOptions = {}) { constructor (options: ClientOptions = {}) {
@ -55,11 +74,13 @@ export class Client extends EventEmitter {
if (options.canary === true) this.canary = true if (options.canary === true) this.canary = true
} }
/** Set Cache Adapter */
setAdapter (adapter: ICacheAdapter): Client { setAdapter (adapter: ICacheAdapter): Client {
this.cache = adapter this.cache = adapter
return this return this
} }
/** Change Presence of Client */
setPresence (presence: ClientPresence | ClientActivity | ActivityGame): void { setPresence (presence: ClientPresence | ClientActivity | ActivityGame): void {
if (presence instanceof ClientPresence) { if (presence instanceof ClientPresence) {
this.presence = presence this.presence = presence
@ -67,6 +88,7 @@ export class Client extends EventEmitter {
this.gateway?.sendPresence(this.presence.create()) this.gateway?.sendPresence(this.presence.create())
} }
/** Emit debug event */
debug (tag: string, msg: string): void { debug (tag: string, msg: string): void {
this.emit("debug", `[${tag}] ${msg}`) this.emit("debug", `[${tag}] ${msg}`)
} }

View file

@ -141,7 +141,7 @@ export class CommandClient extends Client {
this.emit('commandUsed', { context: ctx }) this.emit('commandUsed', { context: ctx })
command.execute(ctx) command.execute(ctx)
} catch (e) { } catch (e) {
if (this.texts.ERROR !== undefined) return this.sendProcessedText(msg, this.texts.ERROR, Object.assign(baseReplaces, { error: e.message })) if (this.texts.ERROR !== undefined) this.sendProcessedText(msg, this.texts.ERROR, Object.assign(baseReplaces, { error: e.message }))
this.emit('commandError', { command, parsed, error: e }) this.emit('commandError', { command, parsed, error: e })
} }
} }

View file

@ -1,5 +1,4 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import * as cache from '../models/cache.ts'
interface IInit { interface IInit {
useCache?: boolean useCache?: boolean
@ -25,18 +24,13 @@ export class Base {
this.useCache = useCache this.useCache = useCache
const cacheID = restURLfuncArgs.join(':') const cacheID = restURLfuncArgs.join(':')
if (this.useCache !== undefined) { if (this.useCache !== undefined) {
const cached = cache.get(this.cacheName ?? this.name, cacheID) const cached = await client.cache.get(this.cacheName ?? this.name, cacheID)
if (cached !== undefined) { if (cached !== undefined) {
return cached return cached
} }
} }
const resp = await fetch(endpoint(...restURLfuncArgs), { const jsonParsed = await client.rest.get(endpoint(...restURLfuncArgs))
headers: {
Authorization: `Bot ${client.token}`
}
})
const jsonParsed = await resp.json()
return new this(client, jsonParsed) return new this(client, jsonParsed)
} }
@ -47,12 +41,7 @@ export class Base {
): Promise<this> { ): Promise<this> {
const oldOne = Object.assign(Object.create(this), this) const oldOne = Object.assign(Object.create(this), this)
const resp = await fetch(endpoint(...restURLfuncArgs), { const jsonParsed = await client.rest.get(endpoint(...restURLfuncArgs))
headers: {
Authorization: `Bot ${client.token}`
}
})
const jsonParsed = await resp.json()
this.readFromData(jsonParsed) this.readFromData(jsonParsed)

View file

@ -4,7 +4,6 @@ import { PresenceUpdatePayload } from '../types/gateway.ts'
import { Base } from './base.ts' import { Base } from './base.ts'
import { Emoji } from './emoji.ts' import { Emoji } from './emoji.ts'
import { VoiceState } from './voiceState.ts' import { VoiceState } from './voiceState.ts'
import cache from '../models/cache.ts'
import { RolesManager } from "../managers/roles.ts" import { RolesManager } from "../managers/roles.ts"
import { GuildChannelsManager } from "../managers/guildChannels.ts" import { GuildChannelsManager } from "../managers/guildChannels.ts"
import { MembersManager } from "../managers/members.ts" import { MembersManager } from "../managers/members.ts"
@ -156,10 +155,10 @@ export class Guild extends Base {
// data.roles.map( // data.roles.map(
// v => cache.get('role', v.id) ?? new Role(this.client, v) // v => cache.get('role', v.id) ?? new Role(this.client, v)
// ) ?? this.roles // ) ?? this.roles
this.emojis = // this.emojis =
data.emojis.map( // data.emojis.map(
v => cache.get('emoji', v.id) ?? new Emoji(this.client, v) // v => cache.get('emoji', v.id) ?? new Emoji(this.client, v)
) ?? this.emojis // ) ?? this.emojis
this.features = data.features ?? this.features this.features = data.features ?? this.features
this.mfaLevel = data.mfa_level ?? this.mfaLevel this.mfaLevel = data.mfa_level ?? this.mfaLevel
this.systemChannelID = data.system_channel_id ?? this.systemChannelID this.systemChannelID = data.system_channel_id ?? this.systemChannelID

View file

@ -1,26 +1,28 @@
import cache from '../models/cache.ts'
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { MemberPayload } from '../types/guild.ts' import { MemberPayload } from '../types/guild.ts'
import { Base } from './base.ts' import { Base } from './base.ts'
import { Role } from "./role.ts"
import { User } from './user.ts' import { User } from './user.ts'
export class Member extends Base { export class Member extends Base {
id: string id: string
user: User user: User
nick?: string nick?: string
roles: string[] roleIDs: string[]
roles: Role[] = []
joinedAt: string joinedAt: string
premiumSince?: string premiumSince?: string
deaf: boolean deaf: boolean
mute: boolean mute: boolean
constructor (client: Client, data: MemberPayload) { constructor (client: Client, data: MemberPayload, user: User) {
super(client) super(client)
this.id = data.user.id this.id = data.user.id
this.user = this.user = user
cache.get('user', data.user.id) ?? new User(this.client, data.user) // this.user =
// cache.get('user', data.user.id) ?? new User(this.client, data.user)
this.nick = data.nick this.nick = data.nick
this.roles = data.roles this.roleIDs = data.roles
this.joinedAt = data.joined_at this.joinedAt = data.joined_at
this.premiumSince = data.premium_since this.premiumSince = data.premium_since
this.deaf = data.deaf this.deaf = data.deaf
@ -32,7 +34,7 @@ export class Member extends Base {
protected readFromData (data: MemberPayload): void { protected readFromData (data: MemberPayload): void {
super.readFromData(data.user) super.readFromData(data.user)
this.nick = data.nick ?? this.nick this.nick = data.nick ?? this.nick
this.roles = data.roles ?? this.roles this.roleIDs = data.roles ?? this.roles
this.joinedAt = data.joined_at ?? this.joinedAt this.joinedAt = data.joined_at ?? this.joinedAt
this.premiumSince = data.premium_since ?? this.premiumSince this.premiumSince = data.premium_since ?? this.premiumSince
this.deaf = data.deaf ?? this.deaf this.deaf = data.deaf ?? this.deaf

View file

@ -1,5 +1,6 @@
import { CommandClient, Intents } from '../../mod.ts'; import { CommandClient, Intents } from '../../mod.ts';
import PingCommand from "./cmds/ping.ts"; import PingCommand from "./cmds/ping.ts";
import UserinfoCommand from "./cmds/userinfo.ts";
import { TOKEN } from './config.ts' import { TOKEN } from './config.ts'
const client = new CommandClient({ const client = new CommandClient({
@ -13,6 +14,9 @@ client.on('ready', () => {
console.log(`[Login] Logged in as ${client.user?.tag}!`) console.log(`[Login] Logged in as ${client.user?.tag}!`)
}) })
client.on("commandError", console.log)
client.commands.add(PingCommand) client.commands.add(PingCommand)
client.commands.add(UserinfoCommand)
client.connect(TOKEN, Intents.All) client.connect(TOKEN, Intents.All)

View file

@ -3,9 +3,8 @@ import { CommandContext } from "../../models/command.ts";
export default class PingCommand extends Command { export default class PingCommand extends Command {
name = "ping" name = "ping"
dmOnly = true
execute(ctx: CommandContext): void { execute(ctx: CommandContext): void {
ctx.message.reply(`pong! Latency: ${ctx.client.ping}ms`) ctx.message.reply(`Pong! Latency: ${ctx.client.ping}ms`)
} }
} }

16
src/test/cmds/userinfo.ts Normal file
View file

@ -0,0 +1,16 @@
import { Command, Member, CommandContext, Embed } from "../../../mod.ts";
export default class UserinfoCommand extends Command {
name = "userinfo"
guildOnly = true
execute(ctx: CommandContext): void {
const member: Member = ctx.message.member as any
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(", "))
ctx.channel.send(embed)
}
}