From 93f40d22e9f62039c9081f0c5ea828fa9da5a7af Mon Sep 17 00:00:00 2001 From: Dragurimu Date: Tue, 9 Aug 2022 22:16:39 -0500 Subject: [PATCH] 1.3.1 --- packages/api-types/README.md | 4 +- packages/api-types/package.json | 2 +- packages/api-types/src/utils/constants.ts | 2 +- packages/cache/README.md | 12 +- packages/cache/package.json | 4 +- packages/cache/src/adapters/cache-adapter.ts | 29 ++- .../src/adapters/memory-cache-adapter.ts | 108 +++++++-- .../cache/src/adapters/redis-cache-adapter.ts | 93 +++++--- packages/cache/src/cache.ts | 215 ++++++++++++++++++ packages/cache/src/index.ts | 2 + packages/cache/src/resources/base-resource.ts | 67 ++++++ .../cache/src/resources/channel-resource.ts | 81 +++++++ .../src/resources/guild-emoji-resource.ts | 51 +++++ .../src/resources/guild-member-resource.ts | 72 ++++++ .../cache/src/resources/guild-resource.ts | 174 ++++++++++++++ .../src/resources/guild-role-resource.ts | 59 +++++ .../src/resources/guild-sticker-resource.ts | 59 +++++ packages/cache/src/resources/index.ts | 13 ++ packages/cache/src/resources/user-resource.ts | 46 ++++ .../cache/src/resources/voice-resource.ts | 57 +++++ packages/core/package.json | 8 +- packages/helpers/package.json | 6 +- packages/rest/package.json | 4 +- packages/ws/package.json | 4 +- 24 files changed, 1092 insertions(+), 80 deletions(-) create mode 100644 packages/cache/src/cache.ts create mode 100644 packages/cache/src/resources/base-resource.ts create mode 100644 packages/cache/src/resources/channel-resource.ts create mode 100644 packages/cache/src/resources/guild-emoji-resource.ts create mode 100644 packages/cache/src/resources/guild-member-resource.ts create mode 100644 packages/cache/src/resources/guild-resource.ts create mode 100644 packages/cache/src/resources/guild-role-resource.ts create mode 100644 packages/cache/src/resources/guild-sticker-resource.ts create mode 100644 packages/cache/src/resources/index.ts create mode 100644 packages/cache/src/resources/user-resource.ts create mode 100644 packages/cache/src/resources/voice-resource.ts diff --git a/packages/api-types/README.md b/packages/api-types/README.md index 122390a..ad66f52 100644 --- a/packages/api-types/README.md +++ b/packages/api-types/README.md @@ -1,5 +1,7 @@ # @biscuitland/api-types + ## Most importantly, api-types is: + 1:1 type definitions package for the [Discord](https://discord.com/developers/docs/intro) API. [](https://github.com/oasisjs/biscuit) @@ -23,7 +25,7 @@ import type { DiscordUser } from '@biscuitland/api-types'; ## Example for [Deno](https://deno.land/) ```ts -import type { DiscordUser } from "https://unpkg.com/@biscuitland/api-types@1.2.0/dist/index.d.ts"; +import type { DiscordUser } from "https://unpkg.com/@biscuitland/api-types@1.3.1/dist/index.d.ts"; ``` We deliver this package through [unpkg](https://unpkg.com/) and it does contain constants and routes too diff --git a/packages/api-types/package.json b/packages/api-types/package.json index 14a5782..08372ad 100644 --- a/packages/api-types/package.json +++ b/packages/api-types/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/api-types", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", diff --git a/packages/api-types/src/utils/constants.ts b/packages/api-types/src/utils/constants.ts index 0291f1d..50cbf3b 100644 --- a/packages/api-types/src/utils/constants.ts +++ b/packages/api-types/src/utils/constants.ts @@ -5,7 +5,7 @@ export const BASE_URL = 'https://discord.com/api'; export const API_VERSION = 10; /** https://github.com/oasisjs/biscuit/releases */ -export const BISCUIT_VERSION = '1.2.0'; +export const BISCUIT_VERSION = '1.3.1'; /** https://discord.com/developers/docs/reference#user-agent */ export const USER_AGENT = `DiscordBot (https://github.com/oasisjs/biscuit, v${BISCUIT_VERSION})`; diff --git a/packages/cache/README.md b/packages/cache/README.md index baeb998..c66165b 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -1,11 +1,13 @@ # @biscuitland/cache -Structures to create a custom cache completely decoupled from the rest of the library. You can choose to use a `MemoryCacheAdapter` or a `RedisCacheAdapter` according to your needs. + +In progress. [](https://github.com/oasisjs/biscuit) [](https://discord.gg/XNw2RZFzaP) ## Links -* [Website](https://biscuitjs.com/) -* [Documentation](https://docs.biscuitjs.com/) -* [Discord](https://discord.gg/XNw2RZFzaP) -* [core](https://www.npmjs.com/package/@biscuitland/core) | [api-types](https://www.npmjs.com/package/@biscuitland/api-types) | [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers) + +- [Website](https://biscuitjs.com/) +- [Documentation](https://docs.biscuitjs.com/) +- [Discord](https://discord.gg/XNw2RZFzaP) +- [core](https://www.npmjs.com/package/@biscuitland/core) | [api-types](https://www.npmjs.com/package/@biscuitland/api-types) | [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers) diff --git a/packages/cache/package.json b/packages/cache/package.json index c224a53..e3ca084 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/cache", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -23,7 +23,7 @@ } }, "dependencies": { - "@biscuitland/api-types": "^1.2.0", + "@biscuitland/api-types": "^1.3.1", "ioredis": "^5.2.2" }, "devDependencies": { diff --git a/packages/cache/src/adapters/cache-adapter.ts b/packages/cache/src/adapters/cache-adapter.ts index 4d3ddb2..b944165 100644 --- a/packages/cache/src/adapters/cache-adapter.ts +++ b/packages/cache/src/adapters/cache-adapter.ts @@ -3,29 +3,48 @@ export interface CacheAdapter { * @inheritDoc */ - get(name: string): Promise; + get(id: string): any | Promise; + get(id: string, guild?: string): string | Promise; /** * @inheritDoc */ - set(name: string, data: unknown): Promise; + set(id: string, data: any, expire?: number): void | Promise; /** * @inheritDoc */ - remove(name: string): Promise; + count(to: string): number | Promise; /** * @inheritDoc */ - clear(): Promise; + remove(id: string): void | Promise; /** * @inheritDoc */ - close?(): Promise; + contains(to: string, id: string): boolean | Promise; + + /** + * @inheritDoc + */ + + getToRelationship(to: string): string[] | Promise; + + /** + * @inheritDoc + */ + + addToRelationship(to: string, id: string): void | Promise; + + /** + * @inheritDoc + */ + + removeToRelationship(to: string, id: string): void | Promise; } diff --git a/packages/cache/src/adapters/memory-cache-adapter.ts b/packages/cache/src/adapters/memory-cache-adapter.ts index 96ef080..66e64df 100644 --- a/packages/cache/src/adapters/memory-cache-adapter.ts +++ b/packages/cache/src/adapters/memory-cache-adapter.ts @@ -1,51 +1,119 @@ -import { CacheAdapter } from './cache-adapter'; +import type { CacheAdapter } from './cache-adapter'; + +interface Options { + expire?: number; +} export class MemoryCacheAdapter implements CacheAdapter { - /** - * @inheritDoc - */ + private static readonly DEFAULTS = { + }; - private readonly data = new Map(); + private readonly relationships = new Map(); + private readonly storage = new Map(); + + readonly options: Options; + + constructor(options?: Options) { + this.options = Object.assign(MemoryCacheAdapter.DEFAULTS, options); + } /** * @inheritDoc */ - async get(name: string): Promise { - const data = this.data.get(name); + get(id: string): T | null { + const data = this.storage.get(id); - if (!data) { - return null; + if (data) { + if (data.expire && data.expire < Date.now()) { + this.storage.delete(id); + } else { + return JSON.parse(data.data); + } } - return JSON.parse(data); + return null; } /** * @inheritDoc */ - async set(name: string, data: unknown): Promise { - const stringData = JSON.stringify(data, (_, v) => - typeof v === 'bigint' ? v.toString() : v - ); - - this.data.set(name, stringData); + set(id: string, data: any, expire = this.options.expire): void { + if (expire) { + this.storage.set(id, { data: JSON.stringify(data), expire: Date.now() + expire }); + } else { + this.storage.set(id, { data: JSON.stringify(data) }); + } } /** * @inheritDoc */ - async remove(name: string): Promise { - this.data.delete(name); + count(to: string): number { + return this.getToRelationship(to).length; } /** * @inheritDoc */ - async clear(): Promise { - this.data.clear(); + remove(id: string): void { + this.storage.delete(id); + } + + /** + * @inheritDoc + */ + + contains(to: string, id: string): boolean { + return this.getToRelationship(to).includes(id); + } + + /** + * @inheritDoc + */ + + getToRelationship(to: string): string[] { + return this.relationships.get(to) || []; + } + + /** + * @inheritDoc + */ + + addToRelationship(to: string, id: string): void { + const data = this.getToRelationship(to); + + if (data.includes(id)) { + return; + } + + data.push(id); + + const has = !!this.relationships.get(to); + + if (!has) { + this.relationships.set(to, data); + } + } + + /** + * @inheritDoc + */ + + removeToRelationship(to: string, id: string): void { + const data = this.getToRelationship(to); + + if (data) { + const idx = data.indexOf(id); + + if (idx === -1) { + return; + } + + data.splice(idx, 1); + } } } diff --git a/packages/cache/src/adapters/redis-cache-adapter.ts b/packages/cache/src/adapters/redis-cache-adapter.ts index 6340c75..3c131b5 100644 --- a/packages/cache/src/adapters/redis-cache-adapter.ts +++ b/packages/cache/src/adapters/redis-cache-adapter.ts @@ -1,28 +1,29 @@ -import type { Redis, RedisOptions } from 'ioredis'; +import type { CacheAdapter } from './cache-adapter'; -import { CacheAdapter } from './cache-adapter'; +import Redis, { RedisOptions } from 'ioredis'; import IORedis from 'ioredis'; -export interface BaseOptions { - prefix?: string; +interface BaseOptions { + namespace: string; + expire?: number; } -export interface BuildOptions extends BaseOptions, RedisOptions {} +interface BuildOptions extends BaseOptions, RedisOptions { } -export interface ClientOptions extends BaseOptions { +interface ClientOptions extends BaseOptions { client: Redis; } - -export type Options = BuildOptions | ClientOptions; + +type Options = BuildOptions | ClientOptions; export class RedisCacheAdapter implements CacheAdapter { - static readonly DEFAULTS = { - prefix: 'biscuitland', + private static readonly DEFAULTS = { + namespace: 'biscuitland' }; private readonly client: Redis; - options: Options; + readonly options: Options; constructor(options?: Options) { this.options = Object.assign(RedisCacheAdapter.DEFAULTS, options); @@ -35,17 +36,12 @@ export class RedisCacheAdapter implements CacheAdapter { } } - _getPrefix(name: string) { - return `${this.options.prefix}:${name}`; - } - /** * @inheritDoc */ - async get(name: string): Promise { - const completKey = this._getPrefix(name); - const data = await this.client.get(completKey); + async get(id: string): Promise { + const data = await this.client.get(this.composite(id)); if (!data) { return null; @@ -58,38 +54,67 @@ export class RedisCacheAdapter implements CacheAdapter { * @inheritDoc */ - async set(name: string, data: unknown): Promise { - const stringData = JSON.stringify(data, (_, v) => - typeof v === 'bigint' ? v.toString() : v - ); - - const completeKey = this._getPrefix(name); - - await this.client.set(completeKey, stringData); + async set(id: string, data: unknown, expire = this.options.expire): Promise { + if (expire) { + await this.client.set(this.composite(id), JSON.stringify(data), 'EX', expire); + } else { + await this.client.set(this.composite(id), JSON.stringify(data)); + } } /** * @inheritDoc */ - async remove(name: string): Promise { - const completKey = this._getPrefix(name); - await this.client.del(completKey); + async count(_to: string): Promise { + throw new Error('Method not implemented.'); } /** * @inheritDoc */ - async clear(): Promise { - this.client.disconnect(); + async remove(id: string): Promise { + await this.client.del(this.composite(id)); } /** * @inheritDoc */ - async close(): Promise { - this.client.disconnect(); + async contains(_to: string, _id: string): Promise { + throw new Error('Method not implemented.'); } -} + + /** + * @inheritDoc + */ + + async getToRelationship(_to: string): Promise { + throw new Error('Method not implemented.'); + } + + /** + * @inheritDoc + */ + + async addToRelationship(_to: string, _id: string): Promise { + throw new Error('Method not implemented.'); + } + + /** + * @inheritDoc + */ + + async removeToRelationship(_to: string, _id: string): Promise { + throw new Error('Method not implemented.'); + } + + /** + * @inheritDoc + */ + + composite(id: string): string { + return `${this.options.namespace}:${id}`; + } +} \ No newline at end of file diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts new file mode 100644 index 0000000..730fd32 --- /dev/null +++ b/packages/cache/src/cache.ts @@ -0,0 +1,215 @@ +import { MemoryCacheAdapter } from './adapters/memory-cache-adapter'; +// import { RedisCacheAdapter } from './adapters/redis-cache-adapter'; + +import { + ChannelResource, + GuildEmojiResource, + GuildMemberResource, + GuildResource, + GuildRoleResource, + GuildStickerResource, + UserResource, + VoiceResource +} from './resources'; + +/** + * add options and adaptor passable by options + * @default MemoryCacheAdapter + */ + +/** + * Add more adapters and options + * Allow passing customizable resources and deleting resources + */ + +/** + * Add presence system (disabled by default) + * Add TTL option (default 7 days) + * Add permissions resource (accessible as a subResource) + */ +export class Cache { + readonly channels: ChannelResource; + + readonly emojis: GuildEmojiResource; + readonly members: GuildMemberResource; + readonly guilds: GuildResource; + readonly roles: GuildRoleResource; + readonly stickers: GuildStickerResource; + + readonly users: UserResource; + readonly voices: VoiceResource; + + ready: boolean; + + constructor() { + this.ready = false; + + /** this change to memory */ + const adapter = new MemoryCacheAdapter(); + + this.channels = new ChannelResource(adapter); + + this.emojis = new GuildEmojiResource(adapter); + this.members = new GuildMemberResource(adapter); + + this.guilds = new GuildResource(adapter); + this.roles = new GuildRoleResource(adapter); + this.stickers = new GuildStickerResource(adapter); + + this.users = new UserResource(adapter); + this.voices = new VoiceResource(adapter); + } + + /** + * @inheritDoc + */ + + async start(event: { t: string; d: any }) { + switch (event.t) { + case 'READY': + await this.users.set(event.d.user.id, event.d.user); + + const guilds: Array | undefined> = []; + + for (const guild of event.d.guilds) { + guilds.push(this.guilds.set(guild.id, guild)); + } + + await Promise.all(guilds); + + this.ready = true; + break; + + case 'USER_UPDATE': + await this.users.set(event.d.id, event.d); + break; + + case 'GUILD_CREATE': + await this.guilds.set(event.d.id, event.d); + break; + + case 'GUILD_UPDATE': + this.guilds.set(event.d.id, event.d); + break; + + case 'GUILD_DELETE': + if (event.d.unavailable) { + await this.guilds.set(event.d.id, event.d); + } else { + await this.guilds.remove(event.d.id); + } + break; + + case 'CHANNEL_CREATE': + // modify [Add elimination system] + await this.channels.set(event.d.id, event.d); + break; + + case 'CHANNEL_UPDATE': + // modify [Add elimination system] + await this.channels.set(event.d.id, event.d); + break; + + case 'CHANNEL_DELETE': + // modify [Add elimination system] + await this.channels.remove(event.d.id); + break; + + case 'GUILD_ROLE_CREATE': + await this.roles.set( + event.d.role.id, + event.d.guild_id, + event.d.role + ); + break; + + case 'GUILD_ROLE_UPDATE': + await this.roles.set( + event.d.role.id, + event.d.guild_id, + event.d.role + ); + break; + + case 'GUILD_ROLE_DELETE': + await this.roles.remove(event.d.role.id, event.d.guild_id); + break; + + case 'GUILD_EMOJIS_UPDATE': + // modify [Add elimination system] + for (const v of event.d.emojis) { + await this.emojis?.set(v.id, event.d.guild_id, v); + } + break; + + case 'GUILD_STICKERS_UPDATE': + // modify [Add elimination system] + for (const v of event.d.stickers) { + await this.stickers?.set(v.id, event.d.guild_id, v); + } + break; + + case 'GUILD_MEMBER_ADD': + await this.members.set( + event.d.user.id, + event.d.guild_id, + event.d + ); + break; + + case 'GUILD_MEMBER_UPDATE': + await this.members.set( + event.d.user.id, + event.d.guild_id, + event.d + ); + break; + + case 'GUILD_MEMBER_REMOVE': + await this.members.remove(event.d.user.id, event.d.guild_id); + break; + + case 'GUILD_MEMBERS_CHUNK': + const members: Array | undefined> = []; + + for (const member of event.d.members) { + members.push( + this.members.set( + member.user.id, + event.d.guild_id, + member + ) + ); + } + + await Promise.all(members); + + break; + + case 'VOICE_STATE_UPDATE': + if (!event.d.guild_id) { + return; + } + + if (event.d.user_id && event.d.member) { + await this.members.set( + event.d.user_id, + event.d.guild_id, + event.d.member + ); + } + + if (event.d.channel_id != null) { + await this.voices.set( + event.d.user_id, + event.d.guild_id, + event.d + ); + } else { + await this.voices.remove(event.d.user_id, event.d.guild_id); + } + + break; + } + } +} diff --git a/packages/cache/src/index.ts b/packages/cache/src/index.ts index e663175..ada9f80 100644 --- a/packages/cache/src/index.ts +++ b/packages/cache/src/index.ts @@ -2,3 +2,5 @@ export { CacheAdapter } from './adapters/cache-adapter'; export { MemoryCacheAdapter } from './adapters/memory-cache-adapter'; export { RedisCacheAdapter } from './adapters/redis-cache-adapter'; + +export { Cache } from './cache'; diff --git a/packages/cache/src/resources/base-resource.ts b/packages/cache/src/resources/base-resource.ts new file mode 100644 index 0000000..23b6c13 --- /dev/null +++ b/packages/cache/src/resources/base-resource.ts @@ -0,0 +1,67 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; + +export class BaseResource { + namespace = 'base'; + + adapter!: CacheAdapter; // replace + + constructor() { + // + } + + /** + * @inheritDoc + */ + + async count(to: string): Promise { + return await this.adapter.count(this.hashId(to)); + } + + /** + * @inheritDoc + */ + + async contains(to: string, id: string): Promise { + return await this.adapter.contains(this.hashId(to), id); + } + + /** + * @inheritDoc + */ + + async getToRelationship(to: string): Promise { + return await this.adapter.getToRelationship(this.hashId(to)); + } + + /** + * @inheritDoc + */ + + async addToRelationship(to: string, id: string): Promise { + await this.adapter.addToRelationship(this.hashId(to), id); + } + + /** + * @inheritDoc // to-do replace + */ + + async removeToRelationship(to: string, id: string): Promise { + await this.adapter.removeToRelationship(this.hashId(to), id); + } + + /** + * @inheritDoc + */ + + hashId(id: string): string { + return `${this.namespace}.${id}`; + } + + /** + * @inheritDoc + */ + + hashGuildId(id: string, guild: string): string { + return `${this.namespace}.${guild}.${id}`; + } +} diff --git a/packages/cache/src/resources/channel-resource.ts b/packages/cache/src/resources/channel-resource.ts new file mode 100644 index 0000000..caed1ca --- /dev/null +++ b/packages/cache/src/resources/channel-resource.ts @@ -0,0 +1,81 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordChannel } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class ChannelResource extends BaseResource { + namespace: 'channel' = 'channel'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string): Promise { + const kv = await this.adapter.get(this.hashId(id)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc // to-do rework + */ + + async set(id: string, data: any, expire?: number): Promise { + delete data.recipients; + + await this.addToRelationship(id); + await this.adapter.set(this.hashId(id), data, expire); + } + + /** + * @inheritDoc + */ + + async count(): Promise { + return await this.adapter.count(this.namespace); + } + + /** + * @inheritDoc + */ + + async remove(id: string): Promise { + await this.adapter.remove(this.hashId(id)); + } + + /** + * @inheritDoc + */ + + async contains(id: string): Promise { + return await this.adapter.contains(this.namespace, id); + } + + /** + * @inheritDoc + */ + + async getToRelationship(): Promise { + return await this.adapter.getToRelationship(this.namespace); + } + + /** + * @inheritDoc + */ + + async addToRelationship(id: string): Promise { + await this.adapter.addToRelationship(this.namespace, id); + } +} diff --git a/packages/cache/src/resources/guild-emoji-resource.ts b/packages/cache/src/resources/guild-emoji-resource.ts new file mode 100644 index 0000000..00844f7 --- /dev/null +++ b/packages/cache/src/resources/guild-emoji-resource.ts @@ -0,0 +1,51 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordEmoji } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class GuildEmojiResource extends BaseResource { + namespace: 'emoji' = 'emoji'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string, guild: string): Promise { + const kv = await this.adapter.get(this.hashGuildId(id, guild)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set( + id: string, + guild: string, + data: any, + expire?: number + ): Promise { + await this.adapter.set(this.hashGuildId(id, guild), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string, guild: string): Promise { + await this.adapter.remove(this.hashGuildId(id, guild)); + } +} diff --git a/packages/cache/src/resources/guild-member-resource.ts b/packages/cache/src/resources/guild-member-resource.ts new file mode 100644 index 0000000..c7fb5c1 --- /dev/null +++ b/packages/cache/src/resources/guild-member-resource.ts @@ -0,0 +1,72 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordMember } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; +import { UserResource } from './user-resource'; + +export class GuildMemberResource extends BaseResource { + namespace: 'member' = 'member'; + + adapter: CacheAdapter; + users: UserResource; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + this.users = new UserResource(adapter); + } + + /** + * @inheritDoc + */ + + async get( + id: string, + guild: string + ): Promise<(DiscordMember & { id: string }) | null> { + const kv = await this.adapter.get(this.hashGuildId(id, guild)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set( + id: string, + guild: string, + data: any, + expire?: number + ): Promise { + if (!data.id) { + data.id = id; + } + + if (data.user) { + await this.users.set(data.user.id, data.user); + } + + if (!data.guild_id) { + data.guild_id = guild; + } + + delete data.user; + + await this.addToRelationship(id, guild); + await this.adapter.set(this.hashGuildId(id, guild), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string, guild: string): Promise { + await this.adapter.remove(this.hashGuildId(id, guild)); + } +} diff --git a/packages/cache/src/resources/guild-resource.ts b/packages/cache/src/resources/guild-resource.ts new file mode 100644 index 0000000..0df4981 --- /dev/null +++ b/packages/cache/src/resources/guild-resource.ts @@ -0,0 +1,174 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordGuild } from '@biscuitland/api-types'; + +import { ChannelResource } from './channel-resource'; +import { GuildEmojiResource } from './guild-emoji-resource'; +import { GuildMemberResource } from './guild-member-resource'; +import { GuildRoleResource } from './guild-role-resource'; +import { GuildStickerResource } from './guild-sticker-resource'; +import { VoiceResource } from './voice-resource'; + +import { BaseResource } from './base-resource'; + +export class GuildResource extends BaseResource { + namespace: 'guild' = 'guild'; + + adapter: CacheAdapter; + + private channels: ChannelResource; + private emojis: GuildEmojiResource; + private members: GuildMemberResource; + private roles: GuildRoleResource; + private stickers: GuildStickerResource; + private voices: VoiceResource; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + + this.channels = new ChannelResource(adapter); + this.emojis = new GuildEmojiResource(adapter); + this.members = new GuildMemberResource(adapter); + this.roles = new GuildRoleResource(adapter); + this.stickers = new GuildStickerResource(adapter); + this.voices = new VoiceResource(adapter); + } + + /** + * @inheritDoc + */ + + async get(id: string): Promise { + const kv = await this.adapter.get(this.hashId(id)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set(id: string, data: any, expire?: number): Promise { + if (data.channels) { + const channels: Array | undefined> = []; + + for (const channel of data.channels) { + await this.channels.set(channel.id, channel); + } + + await Promise.all(channels); + } + + if (data.members) { + const members: Array | undefined> = []; + + for (const member of data.members) { + await this.members.set(member.user.id, id, member); + } + + await Promise.all(members); + } + + if (data.roles) { + const roles: Array | undefined> = []; + + for (const role of data.roles) { + await this.roles.set(role.id, id, role); + } + + await Promise.all(roles); + } + + if (data.stickers) { + const stickers: Array | undefined> = []; + + for (const sticker of data.stickers) { + await this.stickers.set(sticker.id, id, sticker); + } + + await Promise.all(stickers); + } + + if (data.emojis) { + const emojis: Array | undefined> = []; + + for (const emoji of data.emojis) { + await this.emojis.set(emoji.id, id, emoji); + } + + await Promise.all(emojis); + } + + if (data.voice_states) { + const voices: Array> = []; + + for (const voice of data.voice_states) { + if (!voice.guild_id) { + voice.guild_id = id; + } + + voices.push(this.voices.set(voice.user_id, id, voice)); + } + + await Promise.all(voices); + } + + delete data.channels; + delete data.members; + delete data.roles; + delete data.stickers; + delete data.emojis; + + delete data.presences; + + delete data.voice_states; + + await this.addToRelationship(id); + await this.adapter.set(this.hashId(id), data, expire); + } + + /** + * @inheritDoc + */ + + async count(): Promise { + return await this.adapter.count(this.namespace); + } + + /** + * @inheritDoc + */ + + async remove(id: string): Promise { + await this.adapter.remove(this.hashId(id)); + } + + /** + * @inheritDoc + */ + + async contains(id: string): Promise { + return await this.adapter.contains(this.namespace, id); + } + + /** + * @inheritDoc + */ + + async getToRelationship(): Promise { + return await this.adapter.getToRelationship(this.namespace); + } + + /** + * @inheritDoc + */ + + async addToRelationship(id: string): Promise { + await this.adapter.addToRelationship(this.namespace, id); + } +} diff --git a/packages/cache/src/resources/guild-role-resource.ts b/packages/cache/src/resources/guild-role-resource.ts new file mode 100644 index 0000000..80933b4 --- /dev/null +++ b/packages/cache/src/resources/guild-role-resource.ts @@ -0,0 +1,59 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordRole } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class GuildRoleResource extends BaseResource { + namespace: 'role' = 'role'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string, guild: string): Promise { + const kv = await this.adapter.get(this.hashGuildId(id, guild)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set( + id: string, + guild: string, + data: any, + expire?: number + ): Promise { + if (!data.id) { + data.id = id; + } + + if (!data.guild_id) { + data.guild_id = guild; + } + + await this.adapter.set(this.hashGuildId(id, guild), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string, guild: string): Promise { + await this.adapter.remove(this.hashGuildId(id, guild)); + } +} diff --git a/packages/cache/src/resources/guild-sticker-resource.ts b/packages/cache/src/resources/guild-sticker-resource.ts new file mode 100644 index 0000000..9e88c73 --- /dev/null +++ b/packages/cache/src/resources/guild-sticker-resource.ts @@ -0,0 +1,59 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordSticker } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class GuildStickerResource extends BaseResource { + namespace: 'sticker' = 'sticker'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string, guild: string): Promise { + const kv = await this.adapter.get(this.hashGuildId(id, guild)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set( + id: string, + guild: string, + data: any, + expire?: number + ): Promise { + if (!data.id) { + data.id = id; + } + + if (!data.guild_id) { + data.guild_id = guild; + } + + await this.adapter.set(this.hashGuildId(id, guild), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string, guild: string): Promise { + await this.adapter.remove(this.hashGuildId(id, guild)); + } +} diff --git a/packages/cache/src/resources/index.ts b/packages/cache/src/resources/index.ts new file mode 100644 index 0000000..c9bdb3b --- /dev/null +++ b/packages/cache/src/resources/index.ts @@ -0,0 +1,13 @@ +export { BaseResource } from './base-resource'; +export { ChannelResource } from './channel-resource'; + +export { GuildEmojiResource } from './guild-emoji-resource'; +export { GuildMemberResource } from './guild-member-resource'; + +export { GuildResource } from './guild-resource'; + +export { GuildRoleResource } from './guild-role-resource'; +export { GuildStickerResource } from './guild-sticker-resource'; + +export { UserResource } from './user-resource'; +export { VoiceResource } from './voice-resource'; diff --git a/packages/cache/src/resources/user-resource.ts b/packages/cache/src/resources/user-resource.ts new file mode 100644 index 0000000..5d42b9a --- /dev/null +++ b/packages/cache/src/resources/user-resource.ts @@ -0,0 +1,46 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordUser } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class UserResource extends BaseResource { + namespace: 'user' = 'user'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string): Promise { + const kv = await this.adapter.get(this.hashId(id)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set(id: string, data: any, expire?: number): Promise { + await this.adapter.set(this.hashId(id), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string): Promise { + await this.adapter.remove(this.hashId(id)); + } +} diff --git a/packages/cache/src/resources/voice-resource.ts b/packages/cache/src/resources/voice-resource.ts new file mode 100644 index 0000000..b4d2e8f --- /dev/null +++ b/packages/cache/src/resources/voice-resource.ts @@ -0,0 +1,57 @@ +import { CacheAdapter } from '../adapters/cache-adapter'; +import { DiscordVoiceState } from '@biscuitland/api-types'; + +import { BaseResource } from './base-resource'; + +export class VoiceResource extends BaseResource { + namespace: 'voice' = 'voice'; + + adapter: CacheAdapter; + + constructor(adapter: CacheAdapter) { + super(); + + this.adapter = adapter; + } + + /** + * @inheritDoc + */ + + async get(id: string, guild: string): Promise { + const kv = await this.adapter.get(this.hashGuildId(id, guild)); + + if (kv) { + return kv; + } + + return null; + } + + /** + * @inheritDoc + */ + + async set( + id: string, + guild: string, + data: any, + expire?: number + ): Promise { + if (!data.guild_id) { + data.guild_id = guild; + } + + delete data.member; + + await this.adapter.set(this.hashGuildId(id, guild), data, expire); + } + + /** + * @inheritDoc + */ + + async remove(id: string, guild: string): Promise { + await this.adapter.remove(this.hashGuildId(id, guild)); + } +} diff --git a/packages/core/package.json b/packages/core/package.json index 9f8e97b..ec9e685 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/core", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -23,9 +23,9 @@ } }, "dependencies": { - "@biscuitland/api-types": "^1.2.0", - "@biscuitland/rest": "^1.2.0", - "@biscuitland/ws": "^1.2.0" + "@biscuitland/api-types": "^1.3.1", + "@biscuitland/rest": "^1.3.1", + "@biscuitland/ws": "^1.3.1" }, "devDependencies": { "tsup": "^6.1.3" diff --git a/packages/helpers/package.json b/packages/helpers/package.json index 372585e..dfd1a77 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/helpers", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -23,8 +23,8 @@ } }, "dependencies": { - "@biscuitland/api-types": "^1.2.0", - "@biscuitland/core": "^1.2.0" + "@biscuitland/api-types": "^1.3.1", + "@biscuitland/core": "^1.3.1" }, "devDependencies": { "tsup": "^6.1.3" diff --git a/packages/rest/package.json b/packages/rest/package.json index 1fd8351..6d8f2af 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/rest", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -23,7 +23,7 @@ } }, "dependencies": { - "@biscuitland/api-types": "^1.2.0" + "@biscuitland/api-types": "^1.3.1" }, "devDependencies": { "tsup": "^6.1.3" diff --git a/packages/ws/package.json b/packages/ws/package.json index 1f0c799..00181c2 100644 --- a/packages/ws/package.json +++ b/packages/ws/package.json @@ -1,6 +1,6 @@ { "name": "@biscuitland/ws", - "version": "1.3.0", + "version": "1.3.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -23,7 +23,7 @@ } }, "dependencies": { - "@biscuitland/api-types": "^1.2.0", + "@biscuitland/api-types": "^1.3.1", "ws": "^8.8.1" }, "devDependencies": {