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": {