From ab9379f80be6164bcd639a22ef4184d0c9455933 Mon Sep 17 00:00:00 2001 From: Yuzu Date: Fri, 15 Jul 2022 10:55:45 -0500 Subject: [PATCH] feat(cache): refactor some of the caching system --- packages/biscuit/Actions.ts | 1 + packages/cache/Collection.ts | 192 +++++++++++++ packages/cache/channels.ts | 70 +++++ packages/cache/guilds.ts | 53 ++++ packages/cache/members.ts | 26 ++ packages/cache/messages.ts | 150 +++++++++++ packages/cache/mod.ts | 507 +++-------------------------------- packages/cache/users.ts | 9 + 8 files changed, 533 insertions(+), 475 deletions(-) create mode 100644 packages/cache/Collection.ts create mode 100644 packages/cache/channels.ts create mode 100644 packages/cache/guilds.ts create mode 100644 packages/cache/members.ts create mode 100644 packages/cache/messages.ts create mode 100644 packages/cache/users.ts diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index ea000ef..4b86767 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -435,4 +435,5 @@ export interface Events { "webhooksUpdate": Handler<[{ guildId: Snowflake, channelId: Snowflake }]>; "userUpdate": Handler<[User]>; "presenceUpdate": Handler<[Presence]>; + "debug": Handler<[string]>; } diff --git a/packages/cache/Collection.ts b/packages/cache/Collection.ts new file mode 100644 index 0000000..e58ad25 --- /dev/null +++ b/packages/cache/Collection.ts @@ -0,0 +1,192 @@ + +import type { Session, Snowflake } from "./deps.ts"; + +export class Collection extends Map { + constructor(session: Session, entries?: Iterable) { + super(entries); + this.session = session; + } + + readonly session: Session; + + get(key: Snowflake): V | undefined { + return super.get(key); + } + + set(key: Snowflake, value: V): this { + return super.set(key, value); + } + + has(key: Snowflake): boolean { + return super.has(key); + } + + clear(): void { + return super.clear(); + } + + random(): V | undefined; + random(amount: number): V[]; + random(amount?: number): V | V[] | undefined { + const arr = [...this.values()]; + if (typeof amount === "undefined") return arr[Math.floor(Math.random() * arr.length)]; + if (!arr.length) return []; + if (amount && amount > arr.length) amount = arr.length; + return Array.from( + { length: Math.min(amount, arr.length) }, + (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], + ); + } + + find(fn: (value: V, key: Snowflake, structCache: this) => boolean): V | undefined { + for (const [key, value] of this.entries()) { + if (fn(value, key, this)) return value; + } + return undefined; + } + + filter(fn: (value: V, key: Snowflake, structCache: this) => boolean): Collection { + const result = new Collection(this.session); + + for (const [key, value] of this.entries()) { + if (fn(value, key, this)) result.set(key, value); + } + + return result; + } + + forEach(fn: (value: V, key: Snowflake, structCache: this) => T): void { + super.forEach((v: V, k: Snowflake) => { + fn(v, k, this); + }); + } + + clone(): Collection { + return new Collection(this.session, this.entries()); + } + + concat(structures: Collection[]): Collection { + const conc = this.clone(); + + for (const structure of structures) { + if (!structure || !(structure instanceof Collection)) { + continue; + } + + for (const [key, value] of structure.entries()) { + conc.set(key, value); + } + } + return conc; + } + + some(fn: (value: V, key: Snowflake, structCache: this) => boolean): boolean { + for (const [key, value] of this.entries()) { + if (fn(value, key, this)) { + return true; + } + } + return false; + } + + every(fn: (value: V, key: Snowflake, structCache: this) => boolean): boolean { + for (const [key, value] of this.entries()) { + if (!fn(value, key, this)) { + return false; + } + } + return true; + } + + first(): V | undefined; + first(amount: number): V[]; + first(amount?: number): V | V[] | undefined { + if (!amount || amount <= 1) { + return this.values().next().value; + } + const values = [...this.values()]; + amount = Math.min(values.length, amount); + return values.slice(0, amount); + } + + last(): V | undefined; + last(amount: number): V[]; + last(amount?: number): V | V[] | undefined { + const values = [...this.values()]; + if (!amount || amount <= 1) { + return values[values.length - 1]; + } + amount = Math.min(values.length, amount); + return values.slice(-amount); + } + + reverse(): this { + const entries = [...this.entries()].reverse(); + this.clear(); + for (const [key, value] of entries) this.set(key, value); + return this; + } + + map(fn: (value: V, key: Snowflake, collection: this) => T): T[] { + const result: T[] = []; + for (const [key, value] of this.entries()) { + result.push(fn(value, key, this)); + } + return result; + } + + reduce(fn: (acc: T, value: V, key: Snowflake, structCache: this) => T, initV?: T): T { + const entries = this.entries(); + const first = entries.next().value; + let result = initV; + if (result !== undefined) { + result = fn(result, first[1], first[0], this); + } else { + result = first; + } + for (const [key, value] of entries) { + result = fn(result!, value, key, this); + } + return result!; + } + + get size(): number { + return super.size; + } + + get empty(): boolean { + return this.size === 0; + } + + updateFields(key: Snowflake, obj: Partial) { + const value = this.get(key); + + if (!value) { + return; + } + + for (const prop in obj) { + if (obj[prop]) { + value[prop] = obj[prop]!; + } + } + + return this.set(key, value); + } + + getOr(key: Snowflake, or: V): V | undefined { + return this.get(key) ?? or; + } + + retrieve(key: Snowflake, fn: (value: V) => T) { + const value = this.get(key); + + if (!value) { + return; + } + + return fn(value); + } +} + +export default Collection; diff --git a/packages/cache/channels.ts b/packages/cache/channels.ts new file mode 100644 index 0000000..34f8b39 --- /dev/null +++ b/packages/cache/channels.ts @@ -0,0 +1,70 @@ +import type { + ChannelInGuild, + ChannelWithMessagesInGuild, + ChannelTypes, + DiscordChannel, + Snowflake, +} from "./deps.ts"; +import type { CachedMessage } from "./messages.ts"; +import type { CachedGuild } from "./guilds.ts"; +import type { SessionCache } from "./mod.ts"; +import { Collection } from "./Collection.ts"; +import { ChannelFactory, DMChannel, textBasedChannels } from "./deps.ts"; + +export interface CachedGuildChannel extends Omit { + type: ChannelTypes; + messages: Collection; + guild: CachedGuild; + guildId: Snowflake; +} + +export interface CachedGuildChannel extends Omit { + type: ChannelTypes; + guild: CachedGuild; + guildId: Snowflake; +} + +export interface CachedDMChannel extends DMChannel { + messages: Collection; +} + +export function channelBootstrapper(cache: SessionCache, channel: DiscordChannel) { + if (!channel.guild_id) { + cache.dms.set(channel.id, Object.assign(new DMChannel(cache.session, channel), { + messages: new Collection(cache.session), + })) + return; + } + + cache.guilds.retrieve(channel.guild_id, (guild) => { + if (textBasedChannels.includes(channel.type)) { + guild.channels.set( + channel.id, + Object.assign( + ChannelFactory.fromGuildChannel(cache.session, channel) as ChannelWithMessagesInGuild, + { + messages: new Collection(cache.session), + guildId: channel.guild_id!, + get guild(): CachedGuild { + return cache.guilds.get(this.guildId)!; + } + }, + ), + ); + } else { + guild.channels.set( + channel.id, + Object.assign( + ChannelFactory.fromGuildChannel(cache.session, channel), + { + guildId: channel.guild_id!, + get guild(): CachedGuild { + return cache.guilds.get(this.guildId)!; + } + } + ), + ); + } + }); +} + diff --git a/packages/cache/guilds.ts b/packages/cache/guilds.ts new file mode 100644 index 0000000..79fa304 --- /dev/null +++ b/packages/cache/guilds.ts @@ -0,0 +1,53 @@ +import type { + DiscordGuild, + DiscordMemberWithUser, +} from "./deps.ts"; +import type { SessionCache } from "./mod.ts"; +import type { CachedMember } from "./members.ts"; +import type { CachedUser } from "./users.ts"; +import type { CachedGuildChannel } from "./channels.ts"; +import { ChannelFactory, Guild, Member } from "./deps.ts"; +import { Collection } from "./Collection.ts"; + +export interface CachedGuild extends Omit { + channels: Collection; + members: Collection; +} + +export function guildBootstrapper(cache: SessionCache, guild: DiscordGuild) { + const members = new Collection( + cache.session, + guild.members?.map((data) => { + const obj: CachedMember = Object.assign( + new Member(cache.session, data as DiscordMemberWithUser, guild.id), + { + userId: data.user!.id, + get user(): CachedUser | undefined { + return cache.users.get(this.userId); + }, + }, + ); + + return [data.user!.id, obj as CachedMember]; + }), + ); + + const channels = new Collection( + cache.session, + guild.channels?.map((data) => { + const obj = Object.assign(ChannelFactory.from(cache.session, data), { + messages: new Map(), + }); + + return [data.id, obj as CachedGuildChannel]; + }), + ); + + cache.guilds.set( + guild.id, + Object.assign( + new Guild(cache.session, guild), + { members, channels }, + ), + ); +} diff --git a/packages/cache/members.ts b/packages/cache/members.ts new file mode 100644 index 0000000..944aed2 --- /dev/null +++ b/packages/cache/members.ts @@ -0,0 +1,26 @@ +import type { DiscordMemberWithUser, Snowflake } from "./deps.ts"; +import type { SessionCache } from "./mod.ts"; +import type { CachedUser } from "./users.ts"; +import { Member } from "./deps.ts"; + +export interface CachedMember extends Omit { + userId: Snowflake; + user?: CachedUser; +} + +export function memberBootstrapper(cache: SessionCache, member: DiscordMemberWithUser, guildId: Snowflake) { + cache.guilds.retrieve(guildId, (guild) => { + guild.members.set( + member.user.id, + Object.assign( + new Member(cache.session, member, guildId), + { + userId: member.user.id, + get user(): CachedUser | undefined { + return cache.users.get(this.userId); + }, + }, + ), + ); + }); +} diff --git a/packages/cache/messages.ts b/packages/cache/messages.ts new file mode 100644 index 0000000..78acccb --- /dev/null +++ b/packages/cache/messages.ts @@ -0,0 +1,150 @@ +import type { + DiscordEmoji, + DiscordMessage, + DiscordMemberWithUser, + DiscordMessageReactionAdd, + DiscordMessageReactionRemove, + DiscordMessageReactionRemoveAll, + Snowflake +} from "./deps.ts"; +import type { CachedUser } from "./users.ts"; +import type { SessionCache } from "./mod.ts"; +import { Emoji, GuildEmoji, Message, MessageReaction } from "./deps.ts"; +import { memberBootstrapper } from "./members.ts"; + +export interface CachedMessage extends Omit { + authorId: Snowflake; + author?: CachedUser; +} + +export function messageBootstrapper(cache: SessionCache, message: DiscordMessage, partial: boolean) { + if (message.member) { + const member: DiscordMemberWithUser = Object.assign(message.member, { user: message.author }); + + memberBootstrapper(cache, member, message.guild_id!); + } + + if (cache.dms.has(message.channel_id)) { + // is dm + cache.dms.retrieve(message.channel_id, (dm) => { + dm.messages[partial ? "updateFields" : "set"]( + message.id, + Object.assign( + new Message(cache.session, message), + { + authorId: message.author.id, + get author(): CachedUser | undefined { + return cache.users.get(this.authorId); + }, + }, + ), + ); + }); + } else { + // is not dm + cache.guilds.retrieve(message.guild_id!, (guild) => + guild.channels.retrieve(message.channel_id, (dm) => { + dm.messages[partial ? "updateFields" : "set"]( + message.id, + Object.assign( + new Message(cache.session, message), + { + authorId: message.author.id, + get author(): CachedUser | undefined { + return cache.users.get(this.authorId); + }, + }, + ), + ); + })); + } +} + +export function reactionBootstrapper( + cache: SessionCache, + reaction: DiscordMessageReactionAdd | DiscordMessageReactionRemove, + remove: boolean, +) { + cache.emojis.set(reaction.emoji.id ?? reaction.emoji.name!, new Emoji(cache.session, reaction.emoji)); + + function onAdd(message: CachedMessage) { + const reactions = message.reactions.map((r) => r.emoji.name); + + const upsertData = { + count: 1, + emoji: reaction.emoji, + me: reaction.user_id === cache.session.botId, + }; + + if (reactions.length === 0) { + message.reactions = []; + } else if (!reactions.includes(reaction.emoji.name)) { + message.reactions.push(new MessageReaction(cache.session, upsertData)); + } else { + const current = message.reactions?.[reactions.indexOf(reaction.emoji.name)]; + + if (current && message.reactions?.[message.reactions.indexOf(current)]) { + // add 1 to reaction count + ++message.reactions[message.reactions.indexOf(current)].count; + } + } + } + + function onRemove(message: CachedMessage) { + const reactions = message.reactions.map((r) => r.emoji.name); + + if (reactions.indexOf(reaction.emoji.name) !== undefined) { + const current = message.reactions[reactions.indexOf(reaction.emoji.name)]; + + if (current) { + if (current.count > 0) { + current.count--; + } + if (current.count === 0) { + message.reactions.splice(reactions?.indexOf(reaction.emoji.name), 1); + } + } + } + } + + if (reaction.guild_id) { + cache.guilds.retrieve(reaction.guild_id, (guild) => { + guild.channels.retrieve(reaction.channel_id, (channel) => { + channel.messages.retrieve(reaction.message_id, (message) => { + if (remove) onRemove(message); + else onAdd(message); + }); + }); + }); + } else { + cache.dms.retrieve(reaction.channel_id, (channel) => { + channel.messages.retrieve(reaction.message_id, (message) => { + if (remove) onRemove(message); + else onAdd(message); + }); + }); + } +} + +export function reactionBootstrapperDeletions(cache: SessionCache, payload: DiscordMessageReactionRemoveAll) { + if (payload.guild_id) { + cache.guilds.retrieve(payload.guild_id, (guild) => { + guild.channels.retrieve(payload.channel_id, (channel) => { + channel.messages.retrieve(payload.message_id, (message: CachedMessage) => { + message.reactions = []; + }); + }); + }); + } else { + cache.dms.retrieve(payload.channel_id, (channel) => { + channel.messages.retrieve(payload.message_id, (message) => { + message.reactions = []; + }); + }); + } +} + +export function emojiBootstrapper(cache: SessionCache, emoji: DiscordEmoji, guildId: Snowflake) { + if (!emoji.id) return; + cache.emojis.set(emoji.id, new GuildEmoji(cache.session, emoji, guildId)); +} diff --git a/packages/cache/mod.ts b/packages/cache/mod.ts index ac7deb8..ebe38df 100644 --- a/packages/cache/mod.ts +++ b/packages/cache/mod.ts @@ -1,93 +1,34 @@ -import type { - DiscordChannel, - DiscordEmoji, - DiscordGuild, - DiscordMemberWithUser, - DiscordMessage, - DiscordMessageReactionAdd, - DiscordMessageReactionRemove, - DiscordMessageReactionRemoveAll, - DiscordMessageReactionRemoveEmoji, - DiscordUser, - Session, - Snowflake, - SymCache, -} from "./deps.ts"; - +import type { Emoji, Session, SymCache } from "./deps.ts"; +import type { CachedGuild } from "./guilds.ts"; +import type { CachedUser } from "./users.ts"; +import type { CachedDMChannel } from "./channels.ts"; +import { Collection } from "./Collection.ts"; +import { memberBootstrapper } from "./members.ts"; +import { userBootstrapper } from "./users.ts"; +import { channelBootstrapper } from "./channels.ts"; +import { guildBootstrapper } from "./guilds.ts"; import { - ChannelFactory, - ChannelTypes, - DMChannel, - Emoji, - Guild, - GuildChannel, - GuildEmoji, - GuildTextChannel, - Member, - Message, - MessageReaction, - NewsChannel, - textBasedChannels, - ThreadChannel, - User, - VoiceChannel, -} from "./deps.ts"; + messageBootstrapper, + reactionBootstrapper, + reactionBootstrapperDeletions +} from "./messages.ts"; export const cache_sym = Symbol("@cache"); -export interface CachedMessage extends Omit { - authorId: Snowflake; - author?: User; -} - -export interface CachedMember extends Omit { - userId: Snowflake; - user?: User; -} - -export interface CachedGuild extends Omit { - channels: StructCache; - members: StructCache; -} - -export interface CachedGuildChannel extends Omit { - type: ChannelTypes; - messages: StructCache; -} - -export interface CachedGuildChannel extends Omit { - type: ChannelTypes; - messages: StructCache; -} - -export interface CachedGuildChannel extends Omit { - type: ChannelTypes; - messages: StructCache; -} - -export interface CachedGuildChannel extends Omit { - type: ChannelTypes; - messages: StructCache; -} - -export interface CachedDMChannel extends DMChannel { - messages: StructCache; -} - export interface SessionCache extends SymCache { - guilds: StructCache; - users: StructCache; - dms: StructCache; - emojis: StructCache; + guilds: Collection; + users: Collection; + dms: Collection; + emojis: Collection; session: Session; } -export default function (session: Session): SessionCache { +export function enableCache(session: Session): SessionCache { const cache = { - guilds: new StructCache(session), - users: new StructCache(session), - dms: new StructCache(session), - emojis: new StructCache(session), + guilds: new Collection(session), + users: new Collection(session), + dms: new Collection(session), + emojis: new Collection(session), cache: cache_sym, session, }; @@ -96,14 +37,17 @@ export default function (session: Session): SessionCache { // deno-lint-ignore no-explicit-any const raw = data.d as any; + if (!raw) return; + switch (data.t) { + // TODO: add more events + // for now users have to use the bootstrappers that are not implemented yet case "MESSAGE_CREATE": messageBootstrapper(cache, raw, false); break; case "MESSAGE_UPDATE": messageBootstrapper(cache, raw, !raw.edited_timestamp); break; - case "CHANNEL_UPDATE": case "CHANNEL_CREATE": channelBootstrapper(cache, raw); break; @@ -125,402 +69,15 @@ export default function (session: Session): SessionCache { case "MESSAGE_REACTION_REMOVE_ALL": reactionBootstrapperDeletions(cache, raw); break; + case "READY": + userBootstrapper(cache, raw.user); + break; + default: + session.emit("debug", `NOT CACHED: ${JSON.stringify(raw)}`); } }); return cache; } -export class StructCache extends Map { - constructor(session: Session, entries?: Iterable) { - super(entries); - this.session = session; - } - - readonly session: Session; - - get(key: Snowflake): V | undefined { - return super.get(key); - } - - set(key: Snowflake, value: V): this { - return super.set(key, value); - } - - has(key: Snowflake): boolean { - return super.has(key); - } - - clear(): void { - return super.clear(); - } - - random(): V | undefined; - random(amount: number): V[]; - random(amount?: number): V | V[] | undefined { - const arr = [...this.values()]; - if (typeof amount === "undefined") return arr[Math.floor(Math.random() * arr.length)]; - if (!arr.length) return []; - if (amount && amount > arr.length) amount = arr.length; - return Array.from( - { length: Math.min(amount, arr.length) }, - (): V => arr.splice(Math.floor(Math.random() * arr.length), 1)[0], - ); - } - - find(fn: (value: V, key: Snowflake, structCache: this) => boolean): V | undefined { - for (const [key, value] of this.entries()) { - if (fn(value, key, this)) return value; - } - return undefined; - } - - filter(fn: (value: V, key: Snowflake, structCache: this) => boolean): StructCache { - const result = new StructCache(this.session); - for (const [key, value] of this.entries()) { - if (fn(value, key, this)) result.set(key, value); - } - return result; - } - - forEach(fn: (value: V, key: Snowflake, structCache: this) => T): void { - super.forEach((v: V, k: Snowflake) => { - fn(v, k, this); - }); - } - - clone(): StructCache { - return new StructCache(this.session, this.entries()); - } - - concat(structures: StructCache[]): StructCache { - const conc = this.clone(); - - for (const structure of structures) { - if (!structure || !(structure instanceof StructCache)) continue; - for (const [key, value] of structure.entries()) { - conc.set(key, value); - } - } - return conc; - } - - some(fn: (value: V, key: Snowflake, structCache: this) => boolean): boolean { - for (const [key, value] of this.entries()) { - if (fn(value, key, this)) { - return true; - } - } - return false; - } - - every(fn: (value: V, key: Snowflake, structCache: this) => boolean): boolean { - for (const [key, value] of this.entries()) { - if (!fn(value, key, this)) { - return false; - } - } - return true; - } - - first(): V | undefined; - first(amount: number): V[]; - first(amount?: number): V | V[] | undefined { - if (!amount || amount <= 1) { - return this.values().next().value; - } - const values = [...this.values()]; - amount = Math.min(values.length, amount); - return values.slice(0, amount); - } - - last(): V | undefined; - last(amount: number): V[]; - last(amount?: number): V | V[] | undefined { - const values = [...this.values()]; - if (!amount || amount <= 1) { - return values[values.length - 1]; - } - amount = Math.min(values.length, amount); - return values.slice(-amount); - } - - reverse(): this { - const entries = [...this.entries()].reverse(); - this.clear(); - for (const [key, value] of entries) this.set(key, value); - return this; - } - - map(fn: (value: V, key: Snowflake, collection: this) => T): T[] { - const result: T[] = []; - for (const [key, value] of this.entries()) { - result.push(fn(value, key, this)); - } - return result; - } - - reduce(fn: (acc: T, value: V, key: Snowflake, structCache: this) => T, initV?: T): T { - const entries = this.entries(); - const first = entries.next().value; - let result = initV; - if (result !== undefined) { - result = fn(result, first[1], first[0], this); - } else { - result = first; - } - for (const [key, value] of entries) { - result = fn(result!, value, key, this); - } - return result!; - } - - get size(): number { - return super.size; - } - - get empty(): boolean { - return this.size === 0; - } - - updateFields(key: Snowflake, obj: Partial) { - const value = this.get(key); - - if (!value) { - return; - } - - for (const prop in obj) { - if (obj[prop]) { - value[prop] = obj[prop]!; - } - } - - return this.set(key, value); - } - - getOr(key: Snowflake, or: V): V | undefined { - return this.get(key) ?? or; - } - - retrieve(key: Snowflake, fn: (value: V) => T) { - const value = this.get(key); - - if (!value) { - return; - } - - return fn(value); - } -} - -export function reactionBootstrapperDeletions(cache: SessionCache, payload: DiscordMessageReactionRemoveAll) { - if (payload.guild_id) { - cache.guilds.retrieve(payload.guild_id, (guild) => { - guild.channels.retrieve(payload.channel_id, (channel) => { - channel.messages.retrieve(payload.message_id, (message) => { - message.reactions = []; - }); - }); - }); - } else { - cache.dms.retrieve(payload.channel_id, (channel) => { - channel.messages.retrieve(payload.message_id, (message) => { - message.reactions = []; - }); - }); - } -} - -export function reactionBootstrapper( - cache: SessionCache, - reaction: DiscordMessageReactionAdd | DiscordMessageReactionRemove, - remove: boolean, -) { - cache.emojis.set(reaction.emoji.id ?? reaction.emoji.name!, new Emoji(cache.session, reaction.emoji)); - - function onAdd(message: CachedMessage) { - const reactions = message.reactions.map((r) => r.emoji.name); - - const upsertData = { - count: 1, - emoji: reaction.emoji, - me: reaction.user_id === cache.session.botId, - }; - - if (reactions.length === 0) { - message.reactions = []; - } else if (!reactions.includes(reaction.emoji.name)) { - message.reactions.push(new MessageReaction(cache.session, upsertData)); - } else { - const current = message.reactions?.[reactions.indexOf(reaction.emoji.name)]; - - if (current && message.reactions?.[message.reactions.indexOf(current)]) { - // add 1 to reaction count - ++message.reactions[message.reactions.indexOf(current)].count; - } - } - } - - function onRemove(message: CachedMessage) { - const reactions = message.reactions.map((r) => r.emoji.name); - - if (reactions.indexOf(reaction.emoji.name) !== undefined) { - const current = message.reactions[reactions.indexOf(reaction.emoji.name)]; - - if (current) { - if (current.count > 0) { - current.count--; - } - if (current.count === 0) { - message.reactions.splice(reactions?.indexOf(reaction.emoji.name), 1); - } - } - } - } - - if (reaction.guild_id) { - cache.guilds.retrieve(reaction.guild_id, (guild) => { - guild.channels.retrieve(reaction.channel_id, (channel) => { - channel.messages.retrieve(reaction.message_id, (message) => { - if (remove) onRemove(message); - else onAdd(message); - }); - }); - }); - } else { - cache.dms.retrieve(reaction.channel_id, (channel) => { - channel.messages.retrieve(reaction.message_id, (message) => { - if (remove) onRemove(message); - else onAdd(message); - }); - }); - } -} - -export function userBootstrapper(cache: SessionCache, user: DiscordUser) { - cache.users.set(user.id, new User(cache.session, user)); -} - -export function emojiBootstrapper(cache: SessionCache, emoji: DiscordEmoji, guildId: Snowflake) { - if (!emoji.id) return; - cache.emojis.set(emoji.id, new GuildEmoji(cache.session, emoji, guildId)); -} - -export function channelBootstrapper(cache: SessionCache, channel: DiscordChannel) { - if (!channel.guild_id) return; - - cache.guilds.retrieve(channel.guild_id, (guild) => { - if (textBasedChannels.includes(channel.type)) { - // deno-lint-ignore no-explicit-any - guild.channels.set( - channel.id, - Object.assign( - ChannelFactory.fromGuildChannel(cache.session, channel), - { messages: new StructCache(cache.session) }, - ), - ); - } else { - // deno-lint-ignore no-explicit-any - guild.channels.set(channel.id, ChannelFactory.fromGuildChannel(cache.session, channel)); - } - }); -} - -export function memberBootstrapper(cache: SessionCache, member: DiscordMemberWithUser, guildId: Snowflake) { - cache.guilds.retrieve(guildId, (guild) => { - guild.members.set( - member.user.id, - Object.assign( - new Member(cache.session, member, guildId), - { - userId: member.user.id, - get user(): User | undefined { - return cache.users.get(this.userId); - }, - }, - ), - ); - }); -} - -export function messageBootstrapper(cache: SessionCache, message: DiscordMessage, partial: boolean) { - if (message.member) { - const member: DiscordMemberWithUser = Object.assign(message.member, { user: message.author }); - - memberBootstrapper(cache, member, message.guild_id!); - } - - if (cache.dms.has(message.channel_id)) { - // is dm - cache.dms.retrieve(message.channel_id, (dm) => { - dm.messages[partial ? "updateFields" : "set"]( - message.id, - Object.assign( - new Message(cache.session, message), - { - authorId: message.author.id, - get author(): User | undefined { - return cache.users.get(this.authorId); - }, - }, - ), - ); - }); - } else { - // is not dm - cache.guilds.retrieve(message.guild_id!, (guild) => - guild.channels.retrieve(message.channel_id, (dm) => { - dm.messages[partial ? "updateFields" : "set"]( - message.id, - Object.assign( - new Message(cache.session, message), - { - authorId: message.author.id, - get author(): User | undefined { - return cache.users.get(this.authorId); - }, - }, - ), - ); - })); - } -} - -export function guildBootstrapper(cache: SessionCache, guild: DiscordGuild) { - const members = new StructCache( - cache.session, - guild.members?.map((data) => { - const obj: CachedMember = Object.assign( - new Member(cache.session, data as DiscordMemberWithUser, guild.id), - { - userId: data.user!.id, - get user(): User | undefined { - return cache.users.get(this.userId); - }, - }, - ); - - return [data.user!.id, obj as CachedMember]; - }), - ); - - const channels = new StructCache( - cache.session, - guild.channels?.map((data) => { - const obj = Object.assign(ChannelFactory.from(cache.session, data), { - messages: new Map(), - }); - - return [data.id, obj as CachedGuildChannel]; - }), - ); - - cache.guilds.set( - guild.id, - Object.assign( - new Guild(cache.session, guild), - { members, channels }, - ), - ); -} +export default enableCache; diff --git a/packages/cache/users.ts b/packages/cache/users.ts new file mode 100644 index 0000000..d453474 --- /dev/null +++ b/packages/cache/users.ts @@ -0,0 +1,9 @@ +import type { DiscordUser } from "./deps.ts"; +import type { SessionCache } from "./mod.ts"; +import { User } from "./deps.ts"; + +export type CachedUser = User; + +export function userBootstrapper(cache: SessionCache, user: DiscordUser) { + cache.users.set(user.id, new User(cache.session, user)); +}