mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +00:00
1018 lines
31 KiB
TypeScript
1018 lines
31 KiB
TypeScript
import { type If, Logger } from '../common';
|
|
|
|
import type { Adapter } from './adapters';
|
|
|
|
import { Guilds } from './resources/guilds';
|
|
import { Users } from './resources/users';
|
|
|
|
import type { InternalOptions, UsingClient } from '../commands';
|
|
import {
|
|
type APIChannel,
|
|
type APIEmoji,
|
|
type APIGuildMember,
|
|
type APIOverwrite,
|
|
type APISticker,
|
|
type APITextChannel,
|
|
type APIUser,
|
|
ChannelType,
|
|
type GatewayDispatchPayload,
|
|
GatewayIntentBits,
|
|
GuildMemberFlags,
|
|
OverwriteType,
|
|
} from '../types';
|
|
import { Bans } from './resources/bans';
|
|
import { Channels } from './resources/channels';
|
|
import { Emojis } from './resources/emojis';
|
|
import { Members } from './resources/members';
|
|
import { Messages } from './resources/messages';
|
|
import { Overwrites } from './resources/overwrites';
|
|
import { Presences } from './resources/presence';
|
|
import { Roles } from './resources/roles';
|
|
import { StageInstances } from './resources/stage-instances';
|
|
import { Stickers } from './resources/stickers';
|
|
import { VoiceStates } from './resources/voice-states';
|
|
export { BaseResource } from './resources/default/base';
|
|
export { GuildBasedResource } from './resources/default/guild-based';
|
|
export { GuildRelatedResource } from './resources/default/guild-related';
|
|
|
|
export type InferAsyncCache = InternalOptions extends { asyncCache: infer P } ? P : false;
|
|
export type ReturnCache<T> = If<InferAsyncCache, Promise<T>, T>;
|
|
|
|
// GuildBased
|
|
export type GuildBased = 'members' | 'voiceStates';
|
|
|
|
// ClientGuildBased
|
|
export type GuildRelated =
|
|
| 'emojis'
|
|
| 'roles'
|
|
| 'channels'
|
|
| 'stickers'
|
|
| 'presences'
|
|
| 'stageInstances'
|
|
| 'overwrites'
|
|
| 'messages'
|
|
| 'bans';
|
|
|
|
// ClientBased
|
|
export type NonGuildBased = 'users' | 'guilds';
|
|
|
|
// ClientBased
|
|
export type SeyfertBased = 'onPacket';
|
|
|
|
type ReturnManagers = {
|
|
[K in NonGuildBased | GuildBased | GuildRelated]: NonNullable<Awaited<ReturnType<NonNullable<Cache[K]>['get']>>>;
|
|
};
|
|
|
|
export * from './adapters/index';
|
|
|
|
export type CachedEvents =
|
|
| 'READY'
|
|
| 'GUILD_CREATE'
|
|
| 'GUILD_UPDATE'
|
|
| 'GUILD_DELETE'
|
|
| 'CHANNEL_CREATE'
|
|
| 'CHANNEL_UPDATE'
|
|
| 'CHANNEL_DELETE'
|
|
| 'GUILD_ROLE_CREATE'
|
|
| 'GUILD_ROLE_UPDATE'
|
|
| 'GUILD_ROLE_DELETE'
|
|
| 'GUILD_BAN_ADD'
|
|
| 'GUILD_BAN_REMOVE'
|
|
| 'GUILD_EMOJIS_UPDATE'
|
|
| 'GUILD_STICKERS_UPDATE'
|
|
| 'GUILD_MEMBER_ADD'
|
|
| 'GUILD_MEMBER_UPDATE'
|
|
| 'GUILD_MEMBER_REMOVE'
|
|
| 'MESSAGE_CREATE'
|
|
| 'PRESENCE_UPDATE'
|
|
| 'THREAD_DELETE'
|
|
| 'THREAD_CREATE'
|
|
| 'THREAD_UPDATE'
|
|
| 'USER_UPDATE'
|
|
| 'VOICE_STATE_UPDATE'
|
|
| 'STAGE_INSTANCE_CREATE'
|
|
| 'STAGE_INSTANCE_UPDATE'
|
|
| 'STAGE_INSTANCE_DELETE'
|
|
| 'GUILD_MEMBERS_CHUNK';
|
|
|
|
export type DisabledCache = {
|
|
[P in NonGuildBased | GuildBased | GuildRelated | SeyfertBased]?: boolean;
|
|
};
|
|
|
|
export class Cache {
|
|
// non-guild based
|
|
users?: Users;
|
|
guilds?: Guilds;
|
|
|
|
// guild based
|
|
members?: Members;
|
|
voiceStates?: VoiceStates;
|
|
|
|
// guild related
|
|
overwrites?: Overwrites;
|
|
roles?: Roles;
|
|
emojis?: Emojis;
|
|
channels?: Channels;
|
|
stickers?: Stickers;
|
|
presences?: Presences;
|
|
stageInstances?: StageInstances;
|
|
messages?: Messages;
|
|
bans?: Bans;
|
|
|
|
__logger__?: Logger;
|
|
|
|
constructor(
|
|
public intents: number,
|
|
public adapter: Adapter,
|
|
disabledCache: DisabledCache,
|
|
client: UsingClient,
|
|
) {
|
|
this.buildCache(disabledCache, client);
|
|
}
|
|
|
|
buildCache(disabledCache: DisabledCache, client: UsingClient) {
|
|
// non-guild based
|
|
this.users = disabledCache.users ? undefined : new Users(this, client);
|
|
this.guilds = disabledCache.guilds ? undefined : new Guilds(this, client);
|
|
|
|
// guild related
|
|
this.members = disabledCache.members ? undefined : new Members(this, client);
|
|
this.voiceStates = disabledCache.voiceStates ? undefined : new VoiceStates(this, client);
|
|
|
|
// guild based
|
|
this.roles = disabledCache.roles ? undefined : new Roles(this, client);
|
|
this.overwrites = disabledCache.overwrites ? undefined : new Overwrites(this, client);
|
|
this.channels = disabledCache.channels ? undefined : new Channels(this, client);
|
|
this.emojis = disabledCache.emojis ? undefined : new Emojis(this, client);
|
|
this.stickers = disabledCache.stickers ? undefined : new Stickers(this, client);
|
|
this.presences = disabledCache.presences ? undefined : new Presences(this, client);
|
|
this.stageInstances = disabledCache.stageInstances ? undefined : new StageInstances(this, client);
|
|
this.messages = disabledCache.messages ? undefined : new Messages(this, client);
|
|
this.bans = disabledCache.bans ? undefined : new Bans(this, client);
|
|
|
|
this.onPacket = disabledCache.onPacket
|
|
? ((() => {
|
|
//
|
|
}) as any as () => Promise<void>)
|
|
: this.onPacketDefault.bind(this);
|
|
}
|
|
|
|
flush(): ReturnCache<void> {
|
|
return this.adapter.flush() as void;
|
|
}
|
|
|
|
// internal use ./structures
|
|
hasIntent(intent: keyof typeof GatewayIntentBits) {
|
|
return (this.intents & GatewayIntentBits[intent]) === GatewayIntentBits[intent];
|
|
}
|
|
|
|
get hasGuildsIntent() {
|
|
return this.hasIntent('Guilds');
|
|
}
|
|
|
|
get hasRolesIntent() {
|
|
return this.hasGuildsIntent;
|
|
}
|
|
|
|
get hasChannelsIntent() {
|
|
return this.hasGuildsIntent;
|
|
}
|
|
|
|
get hasGuildMembersIntent() {
|
|
return this.hasIntent('GuildMembers');
|
|
}
|
|
|
|
get hasGuildExpressionsIntent() {
|
|
return this.hasIntent('GuildExpressions');
|
|
}
|
|
|
|
get hasVoiceStatesIntent() {
|
|
return this.hasIntent('GuildVoiceStates');
|
|
}
|
|
|
|
get hasPrenseceUpdates() {
|
|
return this.hasIntent('GuildPresences');
|
|
}
|
|
|
|
get hasDirectMessages() {
|
|
return this.hasIntent('DirectMessages');
|
|
}
|
|
|
|
get hasModerationIntent() {
|
|
return this.hasIntent('GuildModeration');
|
|
}
|
|
|
|
async bulkGet(
|
|
keys: (
|
|
| readonly [
|
|
/* type */
|
|
NonGuildBased | GuildRelated,
|
|
/* source id */
|
|
string,
|
|
]
|
|
| readonly [
|
|
/* type */
|
|
GuildBased,
|
|
/* source id */
|
|
string,
|
|
/* guild id */
|
|
string,
|
|
]
|
|
)[],
|
|
): Promise<
|
|
Partial<{
|
|
messages: ReturnManagers['messages'][];
|
|
users: ReturnManagers['users'][];
|
|
guilds: ReturnManagers['guilds'][];
|
|
members: ReturnManagers['members'][];
|
|
voiceStates: ReturnManagers['voiceStates'][];
|
|
emojis: ReturnManagers['emojis'][];
|
|
roles: ReturnManagers['roles'][];
|
|
channels: ReturnManagers['channels'][];
|
|
stickers: ReturnManagers['stickers'][];
|
|
presences: ReturnManagers['presences'][];
|
|
stageInstances: ReturnManagers['stageInstances'][];
|
|
overwrites: ReturnManagers['overwrites'][];
|
|
bans: ReturnManagers['bans'][];
|
|
}>
|
|
> {
|
|
const allData: Partial<Record<NonGuildBased | GuildBased | GuildRelated, string[][]>> = {};
|
|
for (const [type, id, guildId] of keys) {
|
|
switch (type) {
|
|
case 'voiceStates':
|
|
case 'members':
|
|
{
|
|
if (!allData[type]) {
|
|
allData[type] = [];
|
|
}
|
|
allData[type]!.push([id, guildId]);
|
|
}
|
|
break;
|
|
case 'roles':
|
|
case 'stickers':
|
|
case 'channels':
|
|
case 'presences':
|
|
case 'stageInstances':
|
|
case 'emojis':
|
|
case 'users':
|
|
case 'guilds':
|
|
case 'overwrites':
|
|
case 'bans':
|
|
case 'messages':
|
|
{
|
|
if (!allData[type]) {
|
|
allData[type] = [];
|
|
}
|
|
allData[type]!.push([id]);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid type ${type}`);
|
|
}
|
|
}
|
|
|
|
const obj: Partial<{
|
|
[K in keyof ReturnManagers]: ReturnManagers[K][];
|
|
}> = {};
|
|
|
|
for (const i in allData) {
|
|
const key = i as NonGuildBased | GuildBased | GuildRelated;
|
|
const values = allData[key]!;
|
|
obj[key] = [];
|
|
for (const value of values) {
|
|
const g = await this[key]?.get(value[0], value[1]);
|
|
if (!g) {
|
|
continue;
|
|
}
|
|
obj[key]!.push(g as never);
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
async bulkPatch(
|
|
keys: (
|
|
| readonly [
|
|
CacheFrom,
|
|
/* type */
|
|
NonGuildBased,
|
|
/* data */
|
|
any,
|
|
/* source id */
|
|
string,
|
|
]
|
|
| readonly [
|
|
CacheFrom,
|
|
/* type */
|
|
GuildBased | GuildRelated,
|
|
/* data */
|
|
any,
|
|
/* source id */
|
|
string,
|
|
/* guild id */
|
|
string,
|
|
]
|
|
)[],
|
|
) {
|
|
const allData: [string, any][] = [];
|
|
const relationshipsData: Record<string, string[]> = {};
|
|
for (const [from, type, data, id, guildId] of keys) {
|
|
switch (type) {
|
|
case 'roles':
|
|
case 'stickers':
|
|
case 'channels':
|
|
case 'presences':
|
|
case 'stageInstances':
|
|
case 'emojis':
|
|
case 'overwrites':
|
|
case 'bans':
|
|
case 'messages':
|
|
{
|
|
if (!this[type]?.filter(data, id, guildId, from)) continue;
|
|
const hashId = this[type]?.hashId(guildId!);
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
if (type !== 'overwrites' && type !== 'messages') {
|
|
data.guild_id = guildId;
|
|
}
|
|
allData.push([this[type]!.hashId(id), this[type]!.parse(data, id, guildId!)]);
|
|
}
|
|
break;
|
|
case 'voiceStates':
|
|
case 'members':
|
|
{
|
|
if (!this[type]?.filter(data, id, guildId, from)) continue;
|
|
const hashId = this[type]?.hashId(guildId!);
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
data.guild_id = guildId;
|
|
allData.push([this[type]!.hashGuildId(guildId, id), this[type]!.parse(data, id, guildId!)]);
|
|
}
|
|
break;
|
|
case 'users':
|
|
case 'guilds':
|
|
{
|
|
if (!this[type]?.filter(data, id, from)) continue;
|
|
const hashId = this[type]?.namespace;
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
allData.push([this[type]!.hashId(id), data]);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid type ${type}`);
|
|
}
|
|
}
|
|
|
|
await this.adapter.bulkAddToRelationShip(relationshipsData);
|
|
await this.adapter.bulkPatch(allData);
|
|
}
|
|
|
|
async bulkSet(
|
|
keys: (
|
|
| readonly [
|
|
CacheFrom,
|
|
/* type */
|
|
NonGuildBased,
|
|
/* data */
|
|
any,
|
|
/* source id */
|
|
string,
|
|
]
|
|
| readonly [
|
|
CacheFrom,
|
|
/* type */
|
|
GuildBased | GuildRelated,
|
|
/* data */
|
|
any,
|
|
/* source id */
|
|
string,
|
|
/* guild id */
|
|
string,
|
|
]
|
|
)[],
|
|
) {
|
|
const allData: [string, any][] = [];
|
|
const relationshipsData: Record<string, string[]> = {};
|
|
for (const [from, type, data, id, guildId] of keys) {
|
|
switch (type) {
|
|
case 'roles':
|
|
case 'stickers':
|
|
case 'channels':
|
|
case 'presences':
|
|
case 'stageInstances':
|
|
case 'emojis':
|
|
case 'overwrites':
|
|
case 'messages':
|
|
{
|
|
if (!this[type]?.filter(data, id, guildId, from)) continue;
|
|
const hashId = this[type]?.hashId(guildId!);
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
if (type !== 'overwrites' && type !== 'messages') {
|
|
data.guild_id = guildId;
|
|
}
|
|
allData.push([this[type]!.hashId(id), this[type]!.parse(data, id, guildId!)]);
|
|
}
|
|
break;
|
|
case 'bans':
|
|
case 'voiceStates':
|
|
case 'members':
|
|
{
|
|
if (!this[type]?.filter(data, id, guildId, from)) continue;
|
|
const hashId = this[type]?.hashId(guildId!);
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
data.guild_id = guildId;
|
|
allData.push([this[type]!.hashGuildId(guildId, id), this[type]!.parse(data, id, guildId!)]);
|
|
}
|
|
break;
|
|
case 'users':
|
|
case 'guilds':
|
|
{
|
|
if (!this[type]?.filter(data, id, from)) continue;
|
|
const hashId = this[type]?.namespace;
|
|
if (!hashId) {
|
|
continue;
|
|
}
|
|
if (!(hashId in relationshipsData)) {
|
|
relationshipsData[hashId] = [];
|
|
}
|
|
relationshipsData[hashId].push(id);
|
|
allData.push([this[type]!.hashId(id), data]);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid type ${type}`);
|
|
}
|
|
}
|
|
|
|
await this.adapter.bulkAddToRelationShip(relationshipsData);
|
|
await this.adapter.bulkSet(allData);
|
|
}
|
|
|
|
onPacket(event: GatewayDispatchPayload) {
|
|
return this.onPacketDefault(event);
|
|
}
|
|
|
|
protected async onPacketDefault(event: GatewayDispatchPayload) {
|
|
switch (event.t) {
|
|
case 'READY':
|
|
await this.users?.set(CacheFrom.Gateway, event.d.user.id, event.d.user);
|
|
break;
|
|
case 'GUILD_CREATE':
|
|
case 'GUILD_UPDATE':
|
|
case 'RAW_GUILD_CREATE':
|
|
await this.guilds?.patch(CacheFrom.Gateway, event.d.id, { unavailable: false, ...event.d });
|
|
break;
|
|
case 'GUILD_DELETE':
|
|
case 'RAW_GUILD_DELETE':
|
|
if (event.d.unavailable) {
|
|
await this.guilds?.patch(CacheFrom.Gateway, event.d.id, event.d);
|
|
} else {
|
|
await this.guilds?.remove(event.d.id);
|
|
}
|
|
break;
|
|
case 'CHANNEL_CREATE':
|
|
case 'CHANNEL_UPDATE':
|
|
{
|
|
if ('guild_id' in event.d) {
|
|
await this.channels?.set(CacheFrom.Gateway, event.d.id, event.d.guild_id!, event.d);
|
|
if (event.d.permission_overwrites?.length)
|
|
await this.overwrites?.set(
|
|
CacheFrom.Gateway,
|
|
event.d.id,
|
|
event.d.guild_id!,
|
|
event.d.permission_overwrites,
|
|
);
|
|
} else if (event.d.type === ChannelType.DM) {
|
|
await this.channels?.set(CacheFrom.Gateway, event.d.recipients![0]?.id, '@me', event.d);
|
|
}
|
|
}
|
|
break;
|
|
case 'CHANNEL_DELETE':
|
|
await this.channels?.remove(event.d.id, 'guild_id' in event.d ? event.d.guild_id! : '@me');
|
|
break;
|
|
case 'GUILD_ROLE_CREATE':
|
|
case 'GUILD_ROLE_UPDATE':
|
|
await this.roles?.set(CacheFrom.Gateway, 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_BAN_ADD':
|
|
await this.bans?.set(CacheFrom.Gateway, event.d.user.id, event.d.guild_id, event.d);
|
|
break;
|
|
case 'GUILD_BAN_REMOVE':
|
|
await this.bans?.remove(event.d.user.id, event.d.guild_id);
|
|
break;
|
|
case 'GUILD_EMOJIS_UPDATE':
|
|
{
|
|
await this.emojis?.remove(await this.emojis?.keys(event.d.guild_id), event.d.guild_id);
|
|
await this.emojis?.set(
|
|
CacheFrom.Gateway,
|
|
event.d.emojis.map(x => [x.id!, x] as [string, APIEmoji]),
|
|
event.d.guild_id,
|
|
);
|
|
}
|
|
break;
|
|
case 'GUILD_STICKERS_UPDATE':
|
|
{
|
|
await this.stickers?.remove(await this.stickers?.keys(event.d.guild_id), event.d.guild_id);
|
|
await this.stickers?.set(
|
|
CacheFrom.Gateway,
|
|
event.d.stickers.map(x => [x.id, x] as [string, APISticker]),
|
|
event.d.guild_id,
|
|
);
|
|
}
|
|
break;
|
|
case 'GUILD_MEMBERS_CHUNK': {
|
|
const data: Parameters<Cache['bulkSet']>[0] = [];
|
|
|
|
if (this.members) {
|
|
for (const member of event.d.members) {
|
|
data.push(
|
|
[CacheFrom.Gateway, 'members', member, member.user.id, event.d.guild_id],
|
|
[CacheFrom.Gateway, 'users', member.user, member.user.id],
|
|
);
|
|
}
|
|
}
|
|
|
|
if (this.presences && event.d.presences) {
|
|
for (const presence of event.d.presences) {
|
|
data.push([CacheFrom.Gateway, 'presences', presence, presence.user.id, event.d.guild_id]);
|
|
}
|
|
}
|
|
|
|
if (data.length) {
|
|
await this.bulkSet(data);
|
|
}
|
|
break;
|
|
}
|
|
case 'GUILD_MEMBER_ADD':
|
|
case 'GUILD_MEMBER_UPDATE':
|
|
if (event.d.user) await this.members?.set(CacheFrom.Gateway, 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 'PRESENCE_UPDATE':
|
|
// Should update member data?
|
|
await this.presences?.set(CacheFrom.Gateway, event.d.user.id, event.d.guild_id, event.d);
|
|
break;
|
|
|
|
case 'THREAD_CREATE':
|
|
case 'THREAD_UPDATE':
|
|
{
|
|
if (event.d.guild_id) await this.channels?.set(CacheFrom.Gateway, event.d.id, event.d.guild_id, event.d);
|
|
if (event.d.permission_overwrites?.length)
|
|
await this.overwrites?.set(CacheFrom.Gateway, event.d.id, event.d.guild_id!, event.d.permission_overwrites);
|
|
}
|
|
break;
|
|
|
|
case 'THREAD_DELETE':
|
|
await this.channels?.remove(event.d.id, event.d.guild_id);
|
|
break;
|
|
|
|
case 'USER_UPDATE':
|
|
await this.users?.set(CacheFrom.Gateway, event.d.id, event.d);
|
|
break;
|
|
|
|
case 'VOICE_STATE_UPDATE':
|
|
{
|
|
if (!event.d.guild_id) {
|
|
return;
|
|
}
|
|
|
|
if (event.d.channel_id != null) {
|
|
await this.voiceStates?.set(CacheFrom.Gateway, event.d.user_id, event.d.guild_id, event.d);
|
|
} else {
|
|
await this.voiceStates?.remove(event.d.user_id, event.d.guild_id);
|
|
}
|
|
}
|
|
break;
|
|
case 'STAGE_INSTANCE_CREATE':
|
|
case 'STAGE_INSTANCE_UPDATE':
|
|
await this.stageInstances?.set(CacheFrom.Gateway, event.d.id, event.d.guild_id, event.d);
|
|
break;
|
|
case 'STAGE_INSTANCE_DELETE':
|
|
await this.stageInstances?.remove(event.d.id, event.d.guild_id);
|
|
break;
|
|
case 'MESSAGE_CREATE':
|
|
{
|
|
if (this.messages !== undefined) {
|
|
const data: Parameters<Cache['bulkPatch']>[0] = [
|
|
[CacheFrom.Gateway, 'messages', event.d, event.d.id, event.d.channel_id],
|
|
[CacheFrom.Gateway, 'users', event.d.author, event.d.author.id],
|
|
];
|
|
|
|
if (event.d.guild_id) {
|
|
if (event.d.member)
|
|
data.push([CacheFrom.Gateway, 'members', event.d.member, event.d.author.id, event.d.guild_id]);
|
|
}
|
|
|
|
await this.bulkPatch(data);
|
|
}
|
|
}
|
|
break;
|
|
case 'MESSAGE_UPDATE':
|
|
{
|
|
if (this.messages !== undefined) {
|
|
const data: Parameters<Cache['bulkPatch']>[0] = [
|
|
[CacheFrom.Gateway, 'messages', event.d, event.d.id, event.d.channel_id],
|
|
[CacheFrom.Gateway, 'users', event.d.author, event.d.author.id],
|
|
];
|
|
|
|
if (event.d.guild_id) {
|
|
if (event.d.member)
|
|
data.push([CacheFrom.Gateway, 'members', event.d.member, event.d.author.id, event.d.guild_id]);
|
|
}
|
|
|
|
await this.bulkPatch(data);
|
|
}
|
|
}
|
|
break;
|
|
case 'MESSAGE_DELETE':
|
|
await this.messages?.remove(event.d.id, event.d.channel_id);
|
|
break;
|
|
case 'MESSAGE_DELETE_BULK':
|
|
await this.messages?.remove(event.d.ids, event.d.channel_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
async testAdapter() {
|
|
this.__logger__ ??= new Logger({
|
|
name: '[CACHE]',
|
|
});
|
|
await this.adapter.flush();
|
|
// this method will only check the cache for `users`, `members`, and `channels`
|
|
// likewise these have the three types of resources (GuildRelatedResource, GuildBasedResource, BaseResource)
|
|
// will also check `overwrites`, since the latter stores an array not as an object but as data.
|
|
|
|
await this.testUsersAndMembers();
|
|
await this.testChannelsAndOverwrites();
|
|
|
|
this.__logger__.info('The adapter seems to work properly');
|
|
this.__logger__.debug('Flushing adapter');
|
|
|
|
delete this.__logger__;
|
|
|
|
await this.adapter.flush();
|
|
}
|
|
|
|
private async testUsersAndMembers() {
|
|
if (!this.users) throw new Error('Users cache disabled, you should enable it for this.');
|
|
if (!this.members) throw new Error('Members cache disabled, you should enable it for this.');
|
|
|
|
function createUser(name: string): APIUser {
|
|
return {
|
|
avatar: 'xdxd',
|
|
discriminator: '0',
|
|
global_name: name,
|
|
id: `${Math.random()}`.slice(2),
|
|
username: `@seyfert/${name}`,
|
|
};
|
|
}
|
|
function createMember(name: string): APIGuildMember {
|
|
return {
|
|
banner: null,
|
|
avatar: 'xdxd',
|
|
deaf: !false,
|
|
flags: GuildMemberFlags.StartedHomeActions,
|
|
joined_at: new Date().toISOString(),
|
|
mute: !true,
|
|
roles: ['111111111111'],
|
|
user: createUser(name),
|
|
};
|
|
}
|
|
const users: APIUser[] = [
|
|
createUser('witherking_'),
|
|
createUser('vanecia'),
|
|
createUser('socram'),
|
|
createUser('free'),
|
|
createUser('justevil'),
|
|
createUser('nobody'),
|
|
createUser('aaron'),
|
|
createUser('simxnet'),
|
|
createUser('yuzu'),
|
|
createUser('vyrek'),
|
|
createUser('marcrock'),
|
|
];
|
|
for (const user of users) {
|
|
await this.users.set(CacheFrom.Test, user.id, user);
|
|
}
|
|
let count = 0;
|
|
if ((await this.users.values()).length !== users.length)
|
|
throw new Error('users.values() is not of the expected size.');
|
|
if ((await this.users.count()) !== users.length) throw new Error('users.count() is not of the expected amount');
|
|
for (const user of users) {
|
|
const cache = await this.users.raw(user.id);
|
|
if (!cache) throw new Error(`users.raw(${user.id}) has returned undefined!!!!!!`);
|
|
if (cache.username !== user.username)
|
|
throw new Error(
|
|
`users.raw(${user.id}).username is not of the expected value!!!!! (cache (${cache.username})) (expected value: (${user.username}))`,
|
|
);
|
|
if (cache.id !== user.id)
|
|
throw new Error(
|
|
`users.raw(${user.id}).id is not of the expected value!!!!!! (cache (${cache.id})) (expected value: (${user.id}))`,
|
|
);
|
|
await this.users.remove(user.id);
|
|
if ((await this.users.count()) !== users.length - ++count)
|
|
throw new Error(`users.count() should be ${users.length - count}!! please check your remove method`);
|
|
}
|
|
|
|
this.__logger__!.info('the user cache seems to be alright.');
|
|
this.__logger__!.debug('Flushing adapter to clear users cache.');
|
|
|
|
await this.adapter.flush();
|
|
|
|
// unexpected error message
|
|
if ((await this.users.count()) !== 0) throw new Error('users.count() should be 0!! please check your flush method');
|
|
|
|
const guildMembers: Record<string, APIGuildMember[]> = {
|
|
'852531635252494346': [
|
|
createMember("witherking_'s member"),
|
|
createMember("vanecia's member"),
|
|
createMember("nobody's member"),
|
|
],
|
|
'1003825077969764412': [
|
|
createMember("free's member"),
|
|
createMember("socram's member"),
|
|
createMember("marcrock's member"),
|
|
createMember("justevil's member"),
|
|
createMember("vyrek's member"),
|
|
],
|
|
'876711213126520882': [
|
|
createMember("aaron's member"),
|
|
createMember("simxnet's member"),
|
|
createMember("yuzu's member"),
|
|
],
|
|
};
|
|
|
|
for (const guildId in guildMembers) {
|
|
const members = guildMembers[guildId];
|
|
for (const member of members) {
|
|
await this.members.set(CacheFrom.Test, member.user.id, guildId, member);
|
|
}
|
|
if ((await this.members.values(guildId)).length !== members.length)
|
|
throw new Error('members.values(guildId) is not of the expected size.');
|
|
if ((await this.members.count(guildId)) !== members.length)
|
|
throw new Error('members.count(guildId) is not of the expected amount');
|
|
for (const member of members) {
|
|
const cache = await this.members.raw(member.user.id, guildId);
|
|
if (!cache) throw new Error(`members.raw(${member.user.id}, ${guildId}) has returned undefined.`);
|
|
if (cache.roles[0] !== member.roles[0])
|
|
throw new Error(
|
|
`members.raw(${member.user.id}, ${guildId}).roles[0] is not the expected value: ${member.roles[0]} (cache: ${cache.roles[0]})`,
|
|
);
|
|
if (cache.user.username !== member.user.username)
|
|
throw new Error(
|
|
`members.raw(${member.user.id}, ${guildId}).user.username is not the expected value!!!!!! (cache (${cache.user.username})) (expected value: (${member.user.username}))`,
|
|
);
|
|
if (cache.user.id !== member.user.id)
|
|
throw new Error(
|
|
`members.raw(${member.user.id}, ${guildId}).user.id is not the expected value!!!!!! (cache (${cache.user.id})) (expected value: (${member.user.id}))`,
|
|
);
|
|
}
|
|
}
|
|
if ((await this.members.values('*')).length !== Object.values(guildMembers).flat().length)
|
|
throw new Error('members.values(*) is not of the expected size');
|
|
if ((await this.members.count('*')) !== Object.values(guildMembers).flat().length)
|
|
throw new Error('the global amount of members.count(*) is not the expected amount');
|
|
|
|
count = 0;
|
|
for (const guildId in guildMembers) {
|
|
const members = guildMembers[guildId];
|
|
for (const member of members) {
|
|
await this.members.remove(member.user.id, guildId);
|
|
if ((await this.members.count(guildId)) !== members.length - ++count)
|
|
throw new Error(
|
|
`members.count(${guildId}) should be ${members.length - count}!! please check your remove method`,
|
|
);
|
|
}
|
|
count = 0;
|
|
}
|
|
|
|
await this.adapter.flush();
|
|
|
|
// unexpected error message
|
|
if ((await this.users.count()) !== 0)
|
|
throw new Error('users.count() should be zero!! please check your flush method');
|
|
// unexpected error message
|
|
if ((await this.members.count('*')) !== 0)
|
|
throw new Error("members.count('*') should be zero!! please check your flush method");
|
|
|
|
this.__logger__!.info('the member cache seems to be alright.');
|
|
}
|
|
|
|
private async testChannelsAndOverwrites() {
|
|
if (!this.channels) throw new Error('Channels cache disabled, you should enable it for this.');
|
|
if (!this.overwrites) throw new Error('Overwrites cache disabled, you should enable it for this.');
|
|
|
|
function createChannel(name: string): APITextChannel {
|
|
return {
|
|
id: `${Math.random()}`.slice(2),
|
|
name,
|
|
type: ChannelType.GuildText,
|
|
position: Math.random() > 0.5 ? 1 : 0,
|
|
};
|
|
}
|
|
|
|
function createOverwrites(name: string): (APIOverwrite & { channel_id: string })[] {
|
|
const channel_id = `${Math.random()}`.slice(2);
|
|
return [
|
|
{
|
|
id: name,
|
|
allow: '8',
|
|
deny: '2',
|
|
type: OverwriteType.Role,
|
|
channel_id,
|
|
},
|
|
{
|
|
id: `${name}-2`,
|
|
allow: '8',
|
|
deny: '2',
|
|
type: OverwriteType.Role,
|
|
channel_id,
|
|
},
|
|
];
|
|
}
|
|
|
|
const guildChannels: Record<string, APIChannel[]> = {
|
|
'852531635252494346': [
|
|
createChannel("witherking_'s channel"),
|
|
createChannel("vanecia's channel"),
|
|
createChannel("nobody's channel"),
|
|
],
|
|
'1003825077969764412': [
|
|
createChannel("free's channel"),
|
|
createChannel("socram's channel"),
|
|
createChannel("marcrock's channel"),
|
|
createChannel("justevil's channel"),
|
|
createChannel("vyrek's channel"),
|
|
],
|
|
'876711213126520882': [
|
|
createChannel("aaron's channel"),
|
|
createChannel("simxnet's channel"),
|
|
createChannel("yuzu's channel"),
|
|
],
|
|
};
|
|
|
|
for (const guildId in guildChannels) {
|
|
const channels = guildChannels[guildId];
|
|
for (const channel of channels) {
|
|
await this.channels.set(CacheFrom.Test, channel.id, guildId, channel);
|
|
}
|
|
if ((await this.channels.values(guildId)).length !== channels.length)
|
|
throw new Error('channels.values(guildId) is not of the expected size');
|
|
if ((await this.channels.count(guildId)) !== channels.length)
|
|
throw new Error('channels.count(guildId) is not of the expected amount');
|
|
for (const channel of channels) {
|
|
const cache = await this.channels.raw(channel.id);
|
|
if (!cache) throw new Error(`channels.raw(${channel.id}) has returned undefined!!!!!!`);
|
|
if (cache.type !== ChannelType.GuildText)
|
|
throw new Error(
|
|
`channels.raw(${channel.id}).type is not of the expected type: ${channel.type}!!!!!!!! (mismatched type: ${cache.type})`,
|
|
);
|
|
if (cache.name !== channel.name)
|
|
throw new Error(
|
|
`channels.raw(${channel.id}).name is not the expected value!!!!!! (cache (${cache.name})) (expected value: (${channel.name}))`,
|
|
);
|
|
if (cache.id !== channel.id)
|
|
throw new Error(
|
|
`channels.raw(${channel.id}).id is not the expected value!!!!!! (cache (${cache.id})) (expected value: (${channel.id}))`,
|
|
);
|
|
}
|
|
}
|
|
if ((await this.channels.values('*')).length !== Object.values(guildChannels).flat().length)
|
|
throw new Error('channels.values(*) is not of the expected size');
|
|
if ((await this.channels.count('*')) !== Object.values(guildChannels).flat().length)
|
|
throw new Error('channels.count(*) is not of the expected amount');
|
|
|
|
let count = 0;
|
|
for (const guildId in guildChannels) {
|
|
const channels = guildChannels[guildId];
|
|
for (const channel of channels) {
|
|
await this.channels.remove(channel.id, guildId);
|
|
if ((await this.channels.count(guildId)) !== channels.length - ++count)
|
|
throw new Error(
|
|
`channels.count(${guildId}) should be ${channels.length - count}!! please check your remove method`,
|
|
);
|
|
}
|
|
count = 0;
|
|
}
|
|
|
|
// unexpected error message
|
|
if ((await this.channels.count('*')) !== 0)
|
|
throw new Error(`channels.count('*') should be zero!! please check your remove method`);
|
|
|
|
this.__logger__!.info('the channel cache seems to be alright');
|
|
|
|
const guildOverwrites: Record<string, ReturnType<typeof createOverwrites>[]> = {
|
|
'852531635252494346': [
|
|
createOverwrites("witherking_'s channel"),
|
|
createOverwrites("vanecia's channel"),
|
|
createOverwrites("nobody's channel"),
|
|
],
|
|
'1003825077969764412': [
|
|
createOverwrites("free's channel"),
|
|
createOverwrites("socram's channel"),
|
|
createOverwrites("marcrock's channel"),
|
|
createOverwrites("justevil's channel"),
|
|
createOverwrites("vyrek's channel"),
|
|
],
|
|
'876711213126520882': [
|
|
createOverwrites("aaron's channel"),
|
|
createOverwrites("simxnet's channel"),
|
|
createOverwrites("yuzu's channel"),
|
|
],
|
|
};
|
|
for (const guildId in guildOverwrites) {
|
|
const bulkOverwrites = guildOverwrites[guildId];
|
|
for (const overwrites of bulkOverwrites) {
|
|
await this.overwrites.set(CacheFrom.Test, overwrites[0].channel_id, guildId, overwrites);
|
|
}
|
|
if ((await this.overwrites.values(guildId)).length !== bulkOverwrites.length)
|
|
throw new Error('overwrites.values(channelId) is not of the expected size');
|
|
if ((await this.overwrites.count(guildId)) !== bulkOverwrites.length)
|
|
throw new Error('overwrites.count(channelId) is not of the expected amount');
|
|
for (const overwrites of bulkOverwrites) {
|
|
const cache = await this.overwrites.raw(overwrites[0].channel_id);
|
|
if (!cache) throw new Error(`overwrites.raw(${overwrites[0].channel_id}) has returned undefined!!!!!!`);
|
|
if (cache.length !== overwrites.length)
|
|
throw new Error(
|
|
`overwrites.raw(${overwrites[0].channel_id}).length is not of the expected length!!!!!! (cache (${cache.length})) (expected value: (${overwrites.length}))`,
|
|
);
|
|
for (const overwrite of overwrites) {
|
|
if (
|
|
!cache.some(x => {
|
|
return (
|
|
x.allow === overwrite.allow &&
|
|
x.deny === overwrite.deny &&
|
|
x.guild_id === guildId &&
|
|
x.id === overwrite.id &&
|
|
x.type === overwrite.type
|
|
);
|
|
})
|
|
)
|
|
throw new Error("cache wasn't found in the overwrites cache");
|
|
}
|
|
}
|
|
}
|
|
|
|
count = 0;
|
|
|
|
for (const guildId in guildOverwrites) {
|
|
const bulkOverwrites = guildOverwrites[guildId];
|
|
for (const overwrites of bulkOverwrites) {
|
|
await this.overwrites.remove(overwrites[0].channel_id, guildId);
|
|
if ((await this.overwrites.count(guildId)) !== bulkOverwrites.length - ++count)
|
|
throw new Error(
|
|
`overwrites.count(${guildId}) should be ${overwrites.length - count}!! please check your remove method`,
|
|
);
|
|
}
|
|
count = 0;
|
|
}
|
|
|
|
// unexpected error message
|
|
if ((await this.overwrites.count('*')) !== 0)
|
|
throw new Error(`overwrites.count('*') should be zero!! please check your remove method`);
|
|
|
|
this.__logger__!.info('the overwrites cache seems to be alright.');
|
|
}
|
|
}
|
|
|
|
export enum CacheFrom {
|
|
Gateway = 1,
|
|
Rest,
|
|
Test,
|
|
}
|