conflictable completed
This commit is contained in:
parent
f9221bd9a6
commit
a6097f9ccb
@ -1,15 +1,19 @@
|
||||
// Intended to be used with message lists
|
||||
|
||||
import { stringify } from "querystring";
|
||||
import { AutoVerifier } from "./auto-verifier";
|
||||
import { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier";
|
||||
import { Changes, WithEquals } from "./data-types";
|
||||
|
||||
export interface PartialListQuery {
|
||||
export interface PartialMessageListQuery {
|
||||
channelId: string;
|
||||
messageId: string | null;
|
||||
number: number;
|
||||
}
|
||||
|
||||
export interface IDQuery {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class AutoVerifierWithArg<T, K> {
|
||||
private tokenAutoVerifiers = new Map<string, AutoVerifier<T>>();
|
||||
private tokenQueries = new Map<string, K>();
|
||||
@ -23,17 +27,35 @@ export class AutoVerifierWithArg<T, K> {
|
||||
private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise<void>
|
||||
) {}
|
||||
|
||||
static createPartialListAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
||||
primaryFunc: (query: PartialListQuery) => Promise<T[] | null>,
|
||||
trustedFunc: (query: PartialListQuery) => Promise<T[] | null>,
|
||||
changesFunc: (query: PartialListQuery, changes: Changes<T>) => Promise<void>
|
||||
static createStandardPartialMessageListAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
||||
primaryFunc: (query: PartialMessageListQuery) => Promise<T[] | null>,
|
||||
trustedFunc: (query: PartialMessageListQuery) => Promise<T[] | null>,
|
||||
changesFunc: (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<void>
|
||||
) {
|
||||
return new AutoVerifierWithArg<T[], PartialListQuery>(
|
||||
return new AutoVerifierWithArg<T[], PartialMessageListQuery>(
|
||||
query => `ch#${query.channelId} m#${query.messageId}->${query.number}`,
|
||||
query => primaryFunc(query),
|
||||
query => trustedFunc(query),
|
||||
async (query: PartialListQuery, primaryResult: T[] | null, trustedResult: T[] | null) => {
|
||||
await changesFunc(query, AutoVerifier.getChanges(primaryResult, trustedResult));
|
||||
async (query: PartialMessageListQuery, primaryResult: T[] | null, trustedResult: T[] | null) => {
|
||||
let changes = AutoVerifier.getChanges<T>(primaryResult, trustedResult);
|
||||
let changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes);
|
||||
await changesFunc(query, changesType, changes);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static createStandardIDQueriedSingleAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
||||
primaryFunc: (query: IDQuery) => Promise<T | null>,
|
||||
trustedFunc: (query: IDQuery) => Promise<T | null>,
|
||||
changesFunc: (query: IDQuery, changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise<void>
|
||||
) {
|
||||
return new AutoVerifierWithArg<T, IDQuery>(
|
||||
query => `id#${query.id}`,
|
||||
query => primaryFunc(query),
|
||||
query => trustedFunc(query),
|
||||
async (query: IDQuery, primaryResult: T | null, trustedResult: T | null) => {
|
||||
let changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult);
|
||||
await changesFunc(query, changesType, primaryResult, trustedResult);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -4,12 +4,21 @@ import Logger from '../../logger/logger';
|
||||
import { Changes, WithEquals } from './data-types';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
export enum AutoVerifierChangesType {
|
||||
NONE, // Both primaryFunc and trustedFunc returned null
|
||||
PRIMARY_ONLY, // primaryFunc returned non-null and trustedFunc returned null
|
||||
TRUSTED_ONLY, // trustedFunc returned non-null and primaryFunc returned null
|
||||
VERIFIED, // primaryFunc and trustedFunc returned the same non-null result
|
||||
CONFLICT, // primaryFunc and trustedFunc returned conflicting non-null results
|
||||
};
|
||||
|
||||
/**
|
||||
* This is probably complex piece of code in this entire project.
|
||||
* Some complexity comes because the result of the trusted function
|
||||
* can be invalidated while we are awaiting the trusted function.
|
||||
* That complexity stacks on top of the already complex cache-verification
|
||||
* to make this one fiesta of a class.
|
||||
* If you have to edit this it's a very sad day.
|
||||
*/
|
||||
export class AutoVerifier<T> {
|
||||
private trustedPromise: Promise<T | null> | null = null;
|
||||
@ -61,16 +70,61 @@ export class AutoVerifier<T> {
|
||||
return changes;
|
||||
}
|
||||
|
||||
static getListChangesType<T>(primaryResult: T[] | null, trustedResult: T[] | null, changes: Changes<T>): AutoVerifierChangesType {
|
||||
if (primaryResult === null && trustedResult === null) {
|
||||
return AutoVerifierChangesType.NONE;
|
||||
} else if (trustedResult === null) {
|
||||
return AutoVerifierChangesType.PRIMARY_ONLY;
|
||||
} else if (primaryResult === null) {
|
||||
return AutoVerifierChangesType.TRUSTED_ONLY;
|
||||
} else if (changes.added.length === 0 && changes.updated.length === 0 && changes.deleted.length === 0) {
|
||||
return AutoVerifierChangesType.VERIFIED;
|
||||
} else {
|
||||
return AutoVerifierChangesType.CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
static getSingleChangesType<T extends WithEquals<T>>(primaryResult: T | null, trustedResult: T | null): AutoVerifierChangesType {
|
||||
if (primaryResult === null && trustedResult === null) {
|
||||
return AutoVerifierChangesType.NONE;
|
||||
} else if (trustedResult === null) {
|
||||
return AutoVerifierChangesType.PRIMARY_ONLY;
|
||||
} else if (primaryResult === null) {
|
||||
return AutoVerifierChangesType.TRUSTED_ONLY;
|
||||
} else if (primaryResult.equals(trustedResult)) {
|
||||
return AutoVerifierChangesType.VERIFIED;
|
||||
} else {
|
||||
return AutoVerifierChangesType.CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
static createStandardListAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
||||
primaryFunc: () => Promise<T[] | null>,
|
||||
trustedFunc: () => Promise<T[] | null>,
|
||||
changesFunc: (changes: Changes<T>) => Promise<void>
|
||||
changesFunc: (changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<void>
|
||||
) {
|
||||
return new AutoVerifier<T[]>(
|
||||
primaryFunc,
|
||||
trustedFunc,
|
||||
async (primaryResult: T[] | null, trustedResult: T[] | null) => {
|
||||
await changesFunc(AutoVerifier.getChanges(primaryResult, trustedResult));
|
||||
let changes = AutoVerifier.getChanges<T>(primaryResult, trustedResult);
|
||||
let changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes);
|
||||
await changesFunc(changesType, changes);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static createStandardSingleAutoVerifier<T extends WithEquals<T>>(
|
||||
primaryFunc: () => Promise<T | null>,
|
||||
trustedFunc: () => Promise<T | null>,
|
||||
changesFunc: (changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise<void>
|
||||
) {
|
||||
return new AutoVerifier<T>(
|
||||
primaryFunc,
|
||||
trustedFunc,
|
||||
async (primaryResult: T | null, trustedResult: T | null) => {
|
||||
let changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult);
|
||||
await changesFunc(changesType, primaryResult, trustedResult);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -85,7 +139,7 @@ export class AutoVerifier<T> {
|
||||
// If the primary fetchable returns null but has not been verified yet, this will return the result of the trusted fetchable
|
||||
// If the trusted fetchable has not been used to verify the primary fetchable yet, this queries the trusted fetchable and calls verify
|
||||
async fetchAndVerifyIfNeeded(): Promise<T | null> {
|
||||
return await new Promise<T | null>(async (resolve, reject) => {
|
||||
return await new Promise<T | null>(async (resolve: (result: T | null) => void, reject: (error: Error) => void) => {
|
||||
let resolved = false;
|
||||
try {
|
||||
let primaryPromise = this.primaryFunc();
|
||||
@ -128,18 +182,21 @@ export class AutoVerifier<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
resolve(trustedResult);
|
||||
resolved = true;
|
||||
}
|
||||
|
||||
// Make sure to verify BEFORE potentially resolving
|
||||
// This way the conflicts can be resolved before the result is returned
|
||||
await this.verifyFunc(primaryResult, trustedResult);
|
||||
|
||||
if (this.trustedPromise === origTrustedPromise) {
|
||||
this.trustedStatus = 'verified';
|
||||
} else {
|
||||
LOG.warn('RARE ALERT: we got unverified during verification!');
|
||||
// We don't have to re-resolve since we already would have resolved with the correct trusted result
|
||||
}
|
||||
|
||||
if (!resolved && trustedResult) {
|
||||
resolve(trustedResult);
|
||||
resolved = true;
|
||||
}
|
||||
} else {
|
||||
// Some code is already dealing with (or dealt with) verifying the trusted result
|
||||
// Await the same trusted promise and return its result if we didn't get a result
|
||||
|
@ -1,3 +0,0 @@
|
||||
/**
|
||||
* A data source is able to handle events
|
||||
*/
|
38
client/webapp/fetchable-ensured.ts
Normal file
38
client/webapp/fetchable-ensured.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { GuildMetadata, Member, Channel, Message, Resource, Token } from "./data-types";
|
||||
import { Fetchable, GuaranteedFetchable } from "./guild-types";
|
||||
|
||||
export default class EnsuredFetchable implements GuaranteedFetchable {
|
||||
constructor(
|
||||
private fetchable: Fetchable
|
||||
) {}
|
||||
|
||||
private static guarantee<T>(value: T | null): T {
|
||||
if (!value) throw new Error('got null value');
|
||||
return value;
|
||||
}
|
||||
|
||||
async fetchMetadata(): Promise<GuildMetadata> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchMetadata());
|
||||
}
|
||||
async fetchMembers(): Promise<Member[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchMembers());
|
||||
}
|
||||
async fetchChannels(): Promise<Channel[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchChannels());
|
||||
}
|
||||
async fetchMessagesRecent(channelId: string, number: number): Promise<Message[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchMessagesRecent(channelId, number));
|
||||
}
|
||||
async fetchMessagesBefore(channelId: string, messageId: string, number: number): Promise<Message[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchMessagesBefore(channelId, messageId, number));
|
||||
}
|
||||
async fetchMessagesAfter(channelId: string, messageId: string, number: number): Promise<Message[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchMessagesAfter(channelId, messageId, number));
|
||||
}
|
||||
async fetchResource(resourceId: string): Promise<Resource> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchResource(resourceId));
|
||||
}
|
||||
async fetchTokens(): Promise<Token[]> {
|
||||
return EnsuredFetchable.guarantee(await this.fetchable.fetchTokens());
|
||||
}
|
||||
}
|
176
client/webapp/fetchable-pair-verifier.ts
Normal file
176
client/webapp/fetchable-pair-verifier.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import { Changes, Channel, GuildMetadata, Member, Message, Resource, Token } from './data-types';
|
||||
import { AsyncFetchable, Fetchable, Lackable, Conflictable } from './guild-types';
|
||||
import { AutoVerifier, AutoVerifierChangesType } from './auto-verifier';
|
||||
import { AutoVerifierWithArg, PartialMessageListQuery, IDQuery } from './auto-verifier-with-args';
|
||||
import { EventEmitter } from 'tsee';
|
||||
|
||||
export default class GuildPairVerifierGuild extends EventEmitter<Conflictable> implements AsyncFetchable {
|
||||
|
||||
private readonly fetchMetadataVerifier: AutoVerifier<GuildMetadata>;
|
||||
private readonly fetchMembersVerifier: AutoVerifier<Member[]>;
|
||||
private readonly fetchChannelsVerifier: AutoVerifier<Channel[]>;
|
||||
private readonly fetchTokensVerifier: AutoVerifier<Token[]>;
|
||||
|
||||
private readonly fetchResourceVerifier: AutoVerifierWithArg<Resource, IDQuery>
|
||||
|
||||
private readonly fetchMessagesRecentVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>;
|
||||
private readonly fetchMessagesBeforeVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>;
|
||||
private readonly fetchMessagesAfterVerifier: AutoVerifierWithArg<Message[], PartialMessageListQuery>;
|
||||
|
||||
constructor(
|
||||
primary: Fetchable & Lackable,
|
||||
trusted: Fetchable
|
||||
) {
|
||||
super();
|
||||
|
||||
this.fetchMetadataVerifier = AutoVerifier.createStandardSingleAutoVerifier<GuildMetadata>(
|
||||
async () => await primary.fetchMetadata(),
|
||||
async () => await trusted.fetchMetadata(),
|
||||
async (changesType: AutoVerifierChangesType, primaryMetadata: GuildMetadata | null, trustedMetadata: GuildMetadata | null) => {
|
||||
if (changesType === AutoVerifierChangesType.TRUSTED_ONLY) {
|
||||
await primary.handleMetadataChanged(trustedMetadata as GuildMetadata);
|
||||
} else if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
await primary.handleMetadataChanged(trustedMetadata as GuildMetadata);
|
||||
this.emit('conflict-metadata', primaryMetadata as GuildMetadata, trustedMetadata as GuildMetadata);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.fetchMembersVerifier = AutoVerifier.createStandardListAutoVerifier<Member>(
|
||||
async () => await primary.fetchMembers(),
|
||||
async () => await trusted.fetchMembers(),
|
||||
async (changesType: AutoVerifierChangesType, changes: Changes<Member>) => {
|
||||
if (changes.added.length > 0) await primary.handleMembersAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleMembersChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleMembersDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-members', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchChannelsVerifier = AutoVerifier.createStandardListAutoVerifier<Channel>(
|
||||
async () => await primary.fetchChannels(),
|
||||
async () => await trusted.fetchChannels(),
|
||||
async (changesType: AutoVerifierChangesType, changes: Changes<Channel>) => {
|
||||
if (changes.added.length > 0) await primary.handleChannelsAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleChannelsChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleChannelsDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-channels', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchTokensVerifier = AutoVerifier.createStandardListAutoVerifier<Token>(
|
||||
async () => await primary.fetchTokens(),
|
||||
async () => await trusted.fetchTokens(),
|
||||
async (changesType: AutoVerifierChangesType, changes: Changes<Token>) => {
|
||||
if (changes.added.length > 0) await primary.handleTokensAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleTokensChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleTokensDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-tokens', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchResourceVerifier = AutoVerifierWithArg.createStandardIDQueriedSingleAutoVerifier<Resource>(
|
||||
async (query: IDQuery) => await primary.fetchResource(query.id),
|
||||
async (query: IDQuery) => await trusted.fetchResource(query.id),
|
||||
async (query: IDQuery, changesType: AutoVerifierChangesType, primaryResource: Resource | null, trustedResource: Resource | null) => {
|
||||
if (changesType === AutoVerifierChangesType.PRIMARY_ONLY) {
|
||||
await primary.handleResourceDeleted(trustedResource as Resource);
|
||||
} else if (changesType === AutoVerifierChangesType.TRUSTED_ONLY) {
|
||||
await primary.handleResourceAdded(trustedResource as Resource);
|
||||
} else if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
await primary.handleResourceChanged(trustedResource as Resource);
|
||||
this.emit('conflict-resource', primaryResource as Resource, trustedResource as Resource);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesRecentVerifier = AutoVerifierWithArg.createStandardPartialMessageListAutoVerifier(
|
||||
async (query: PartialMessageListQuery) => await primary.fetchMessagesRecent(query.channelId, query.number),
|
||||
async (query: PartialMessageListQuery) => await trusted.fetchMessagesRecent(query.channelId, query.number),
|
||||
async (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) await primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleMessagesDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-messages', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesBeforeVerifier = AutoVerifierWithArg.createStandardPartialMessageListAutoVerifier(
|
||||
async (query: PartialMessageListQuery) => await primary.fetchMessagesBefore(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialMessageListQuery) => await trusted.fetchMessagesBefore(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) await primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleMessagesDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-messages', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesAfterVerifier = AutoVerifierWithArg.createStandardPartialMessageListAutoVerifier(
|
||||
async (query: PartialMessageListQuery) => await primary.fetchMessagesAfter(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialMessageListQuery) => await trusted.fetchMessagesAfter(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) await primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) await primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) await primary.handleMessagesDeleted(changes.deleted);
|
||||
|
||||
if (changesType === AutoVerifierChangesType.CONFLICT) {
|
||||
this.emit('conflict-messages', changes);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
unverify() {
|
||||
this.fetchMetadataVerifier.unverify();
|
||||
this.fetchMembersVerifier.unverify();
|
||||
this.fetchChannelsVerifier.unverify();
|
||||
this.fetchTokensVerifier.unverify();
|
||||
|
||||
this.fetchResourceVerifier.unverifyAll();
|
||||
|
||||
this.fetchMessagesRecentVerifier.unverifyAll();
|
||||
this.fetchMessagesBeforeVerifier.unverifyAll();
|
||||
this.fetchMessagesAfterVerifier.unverifyAll();
|
||||
}
|
||||
|
||||
async fetchMetadata(): Promise<GuildMetadata | null> {
|
||||
return await this.fetchMetadataVerifier.fetchAndVerifyIfNeeded();
|
||||
}
|
||||
async fetchMembers(): Promise<Member[] | null> {
|
||||
return await this.fetchMembersVerifier.fetchAndVerifyIfNeeded();
|
||||
}
|
||||
async fetchChannels(): Promise<Channel[] | null> {
|
||||
return await this.fetchChannelsVerifier.fetchAndVerifyIfNeeded();
|
||||
}
|
||||
async fetchMessagesRecent(channelId: string, number: number): Promise<Message[] | null> {
|
||||
return await this.fetchMessagesRecentVerifier.fetchAndVerifyIfNeded({ channelId, messageId: null, number });
|
||||
}
|
||||
async fetchMessagesBefore(channelId: string, messageId: string, number: number): Promise<Message[] | null> {
|
||||
return await this.fetchMessagesBeforeVerifier.fetchAndVerifyIfNeded({ channelId, messageId, number });
|
||||
}
|
||||
async fetchMessagesAfter(channelId: string, messageId: string, number: number): Promise<Message[] | null> {
|
||||
return await this.fetchMessagesAfterVerifier.fetchAndVerifyIfNeded({ channelId, messageId, number });
|
||||
}
|
||||
async fetchResource(resourceId: string): Promise<Resource | null> {
|
||||
return await this.fetchResourceVerifier.fetchAndVerifyIfNeded({ id: resourceId });
|
||||
}
|
||||
async fetchTokens(): Promise<Token[] | null> {
|
||||
return await this.fetchTokensVerifier.fetchAndVerifyIfNeeded();
|
||||
}
|
||||
}
|
@ -13,17 +13,17 @@ import MessageRAMCache from "./message-ram-cache";
|
||||
import PersonalDB from "./personal-db";
|
||||
import ResourceRAMCache from "./resource-ram-cache";
|
||||
import SocketVerifier from './socket-verifier';
|
||||
import { Connectable, AsyncGuaranteedFetchable } from './guild-types';
|
||||
import { AutoVerifier } from './auto-verifier';
|
||||
import { AutoVerifierWithArg, PartialListQuery } from './auto-verifier-with-args';
|
||||
import GuildPairVerifierGuild from './guild-pair-verifier';
|
||||
import { Connectable, AsyncGuaranteedFetchable, Conflictable, AsyncRequestable } from './guild-types';
|
||||
import PairVerifierFetchable from './fetchable-pair-verifier';
|
||||
import EnsuredFetchable from './fetchable-ensured';
|
||||
import { EventEmitter } from 'tsee';
|
||||
|
||||
export default class GuildController extends Connectable implements AsyncGuaranteedFetchable {
|
||||
export default class CombinedGuild extends EventEmitter<Connectable & Conflictable> implements AsyncGuaranteedFetchable, AsyncRequestable {
|
||||
private readonly ramGuild: RAMGuild;
|
||||
private readonly personalDBGuild: PersonalDBGuild;
|
||||
private readonly socketGuild: SocketGuild;
|
||||
|
||||
private readonly pairVerifiers: GuildPairVerifierGuild[];
|
||||
private readonly pairVerifiers: PairVerifierFetchable[];
|
||||
private readonly fetchable: AsyncGuaranteedFetchable;
|
||||
|
||||
constructor(
|
||||
@ -43,6 +43,7 @@ export default class GuildController extends Connectable implements AsyncGuarant
|
||||
|
||||
// TODO: Only unverify the personaldb->socket connection on d/c?
|
||||
|
||||
// Connect/Disconnect
|
||||
this.socketGuild.on('connect', () => {
|
||||
LOG.info(`g#${this.id} connected`);
|
||||
this.emit('connect');
|
||||
@ -53,83 +54,98 @@ export default class GuildController extends Connectable implements AsyncGuarant
|
||||
this.emit('disconnect');
|
||||
});
|
||||
|
||||
// Metadata
|
||||
this.socketGuild.on('update-metadata', (guildMeta: GuildMetadata) => {
|
||||
LOG.info(`g#${this.id} updated metadata: ${guildMeta}`);
|
||||
this.emit('update-metadata', guildMeta);
|
||||
});
|
||||
|
||||
let listenForList = <T>(
|
||||
event: 'new-messages' | 'update-messages' | 'new-members' | 'update-members' | 'new-channels' | 'update-channels',
|
||||
logFunc: (arg: T) => void,
|
||||
guildUpdater: (args: T[]) => void
|
||||
) => {
|
||||
this.socketGuild.on(event, (args: any[]) => {
|
||||
for (let arg of args) {
|
||||
logFunc(arg);
|
||||
}
|
||||
guildUpdater(args);
|
||||
this.emit(event, args);
|
||||
});
|
||||
}
|
||||
// Members
|
||||
this.socketGuild.on('new-members', async (members: Member[]) => {
|
||||
for (let member of members) {
|
||||
LOG.info(`g#${this.id} ${member}`);
|
||||
}
|
||||
this.ramGuild.handleMembersAdded(members);
|
||||
await this.personalDBGuild.handleMembersAdded(members);
|
||||
this.emit('new-members', members);
|
||||
});
|
||||
this.socketGuild.on('update-members', async (members: Member[]) => {
|
||||
for (let member of members) {
|
||||
LOG.info(`g#${this.id} updated ${member}`);
|
||||
}
|
||||
this.ramGuild.handleMembersChanged(members);
|
||||
await this.personalDBGuild.handleMembersChanged(members);
|
||||
this.emit('update-members', members);
|
||||
});
|
||||
|
||||
listenForList<Member>('new-members',
|
||||
(member: Member) => LOG.info(`g#${this.id} ${member}`),
|
||||
(members: Member[]) => {
|
||||
this.ramGuild.handleMembersAdded(members);
|
||||
this.personalDBGuild.handleMembersAdded(members);
|
||||
// Channels
|
||||
this.socketGuild.on('new-channels', async (channels: Channel[]) => {
|
||||
for (let channel of channels) {
|
||||
LOG.info(`g#${this.id} ${channel}`);
|
||||
}
|
||||
);
|
||||
listenForList<Member>('update-members',
|
||||
(member: Member) => LOG.info(`g#${this.id} updated ${member}`),
|
||||
(members: Member[]) => {
|
||||
this.ramGuild.handleMembersChanged(members);
|
||||
this.personalDBGuild.handleMembersChanged(members);
|
||||
this.ramGuild.handleChannelsAdded(channels);
|
||||
await this.personalDBGuild.handleChannelsAdded(channels);
|
||||
this.emit('new-channels', channels);
|
||||
});
|
||||
this.socketGuild.on('update-channels', async (channels: Channel[]) => {
|
||||
for (let channel of channels) {
|
||||
LOG.info(`g#${this.id} updated ${channel}`);
|
||||
}
|
||||
);
|
||||
this.ramGuild.handleChannelsChanged(channels);
|
||||
await this.personalDBGuild.handleChannelsChanged(channels);
|
||||
this.emit('update-channels', channels);
|
||||
});
|
||||
|
||||
listenForList<Channel>('new-members',
|
||||
(channel: Channel) => LOG.info(`g#${this.id} ${channel}`),
|
||||
(channels: Channel[]) => {
|
||||
this.ramGuild.handleChannelsAdded(channels);
|
||||
this.personalDBGuild.handleChannelsAdded(channels);
|
||||
// Messages
|
||||
this.socketGuild.on('new-messages', async (messages: Message[]) => {
|
||||
for (let message of messages) {
|
||||
LOG.info(`g#${this.id} ${message}`);
|
||||
}
|
||||
);
|
||||
listenForList<Channel>('update-members',
|
||||
(channel: Channel) => LOG.info(`g#${this.id} updated ${channel}`),
|
||||
(channels: Channel[]) => {
|
||||
this.ramGuild.handleChannelsChanged(channels);
|
||||
this.personalDBGuild.handleChannelsChanged(channels);
|
||||
this.ramGuild.handleMessagesAdded(messages);
|
||||
await this.personalDBGuild.handleMessagesAdded(messages);
|
||||
this.emit('new-messages', messages);
|
||||
});
|
||||
this.socketGuild.on('update-messages', async (messages: Message[]) => {
|
||||
for (let message of messages) {
|
||||
LOG.info(`g#${this.id} updated ${message}`);
|
||||
}
|
||||
);
|
||||
this.ramGuild.handleMessagesChanged(messages);
|
||||
await this.personalDBGuild.handleMessagesChanged(messages);
|
||||
this.emit('update-messages', messages);
|
||||
});
|
||||
|
||||
listenForList<Message>('new-messages',
|
||||
(message: Message) => LOG.info(`g#${this.id} ${message}`),
|
||||
(messages: Message[]) => {
|
||||
this.ramGuild.handleMessagesAdded(messages);
|
||||
this.personalDBGuild.handleMessagesAdded(messages);
|
||||
}
|
||||
);
|
||||
listenForList<Message>('update-messages',
|
||||
(message: Message) => LOG.info(`g#${this.id} updated ${message}`),
|
||||
(messages: Message[]) => {
|
||||
this.ramGuild.handleMessagesChanged(messages);
|
||||
this.personalDBGuild.handleMessagesChanged(messages);
|
||||
}
|
||||
);
|
||||
let personalDBSocketPairVerifier = new PairVerifierFetchable(this.personalDBGuild, this.socketGuild);
|
||||
let ramPersonalDBSocketPairVerifier = new PairVerifierFetchable(this.ramGuild, personalDBSocketPairVerifier);
|
||||
|
||||
let personalDBSocketPairVerifier = new GuildPairVerifierGuild(
|
||||
this.personalDBGuild,
|
||||
this.socketGuild
|
||||
);
|
||||
|
||||
let ramPersonalDBSocketPairVerifier = new GuildPairVerifierGuild(
|
||||
this.ramGuild,
|
||||
personalDBSocketPairVerifier
|
||||
);
|
||||
// Forward the conflict events from the last verifier in the chain
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-metadata', (oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => {
|
||||
LOG.info(`g#${this.id} metadata conflict`, { oldGuildMeta, newGuildMeta });
|
||||
this.emit('conflict-metadata', oldGuildMeta, newGuildMeta);
|
||||
});
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-members', (changes: Changes<Member>) => {
|
||||
LOG.info(`g#${this.id} members conflict`, { changes });
|
||||
this.emit('conflict-members', changes);
|
||||
});
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-channels', (changes: Changes<Channel>) => {
|
||||
LOG.info(`g#${this.id} channels conflict`, { changes });
|
||||
this.emit('conflict-channels', changes);
|
||||
});
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-messages', (changes: Changes<Message>) => {
|
||||
LOG.info(`g#${this.id} messages conflict`, { changes });
|
||||
this.emit('conflict-messages', changes);
|
||||
});
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-tokens', (changes: Changes<Token>) => {
|
||||
LOG.info(`g#${this.id} tokens conflict`, { changes });
|
||||
this.emit('conflict-tokens', changes);
|
||||
});
|
||||
ramPersonalDBSocketPairVerifier.on('conflict-resource', (oldResource: Resource, newResource: Resource) => {
|
||||
LOG.warn(`g#${this.id} resource conflict`, { oldResource, newResource });
|
||||
this.emit('conflict-resource', oldResource, newResource);
|
||||
});
|
||||
|
||||
this.pairVerifiers = [ personalDBSocketPairVerifier, ramPersonalDBSocketPairVerifier ];
|
||||
|
||||
this.fetchable = ramPersonalDBSocketPairVerifier;
|
||||
this.fetchable = new EnsuredFetchable(ramPersonalDBSocketPairVerifier);
|
||||
}
|
||||
|
||||
static async create(
|
||||
@ -145,7 +161,7 @@ export default class GuildController extends Connectable implements AsyncGuarant
|
||||
});
|
||||
let socketVerifier = new SocketVerifier(socket, socketConfig.publicKey, socketConfig.privateKey);
|
||||
let memberId = await socketVerifier.verify();
|
||||
return new GuildController(
|
||||
return new CombinedGuild(
|
||||
personalGuildId,
|
||||
memberId,
|
||||
socket,
|
||||
@ -166,14 +182,17 @@ export default class GuildController extends Connectable implements AsyncGuarant
|
||||
if (this.ramGuild.getMembers().size === 0) {
|
||||
await this.fetchMembers();
|
||||
}
|
||||
if (this.ramGuild.getMembers().size === 0) throw new Error('RAM Members was not updated through fetchMembers');
|
||||
}
|
||||
|
||||
async ensureRAMChannels() {
|
||||
if (this.ramGuild.getChannels().size === 0) {
|
||||
await this.fetchChannels();
|
||||
}
|
||||
if (this.ramGuild.getChannels().size === 0) throw new Error('RAM Channels was not updated through fetchChannels');
|
||||
}
|
||||
|
||||
// Fetched through the triple-cache system (RAM -> Disk -> Server)
|
||||
async fetchMetadata(): Promise<GuildMetadata> {
|
||||
return await this.fetchable.fetchMetadata();
|
||||
}
|
||||
@ -198,4 +217,36 @@ export default class GuildController extends Connectable implements AsyncGuarant
|
||||
async fetchTokens(): Promise<Token[]> {
|
||||
return await this.fetchable.fetchTokens();
|
||||
}
|
||||
|
||||
// Simply forwarded to the socket guild
|
||||
async requestSendMessage(channelId: string, text: string): Promise<void> {
|
||||
await this.socketGuild.requestSendMessage(channelId, text);
|
||||
}
|
||||
async requestSendMessageWithResource(channelId: string, text: string | null, resource: Buffer, resourceName: string): Promise<void> {
|
||||
await this.socketGuild.requestSendMessageWithResource(channelId, text, resource, resourceName);
|
||||
}
|
||||
async requestSetStatus(status: string): Promise<void> {
|
||||
await this.socketGuild.requestSetStatus(status);
|
||||
}
|
||||
async requestSetDisplayName(displayName: string): Promise<void> {
|
||||
await this.socketGuild.requestSetDisplayName(displayName);
|
||||
}
|
||||
async requestSetAvatar(avatar: Buffer): Promise<void> {
|
||||
await this.socketGuild.requestSetAvatar(avatar);
|
||||
}
|
||||
async requestSetServerName(serverName: string): Promise<void> {
|
||||
await this.socketGuild.requestSetServerName(serverName);
|
||||
}
|
||||
async requestSetServerIcon(serverIcon: Buffer): Promise<void> {
|
||||
await this.socketGuild.requestSetServerIcon(serverIcon);
|
||||
}
|
||||
async requestDoUpdateChannel(channelId: string, name: string, flavorText: string | null): Promise<void> {
|
||||
await this.socketGuild.requestDoUpdateChannel(channelId, name, flavorText);
|
||||
}
|
||||
async requestDoCreateChannel(name: string, flavorText: string | null): Promise<void> {
|
||||
await this.socketGuild.requestDoCreateChannel(name, flavorText);
|
||||
}
|
||||
async requestDoRevokeToken(token: string): Promise<void> {
|
||||
await this.socketGuild.requestDoRevokeToken(token);
|
||||
}
|
||||
}
|
||||
|
@ -1,172 +0,0 @@
|
||||
import * as electronRemote from '@electron/remote';
|
||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import * as socketio from 'socket.io-client';
|
||||
import PersonalDBGuild from './guild-personal-db';
|
||||
import RAMGuild from './guild-ram';
|
||||
import SocketGuild from './guild-socket';
|
||||
import { Changes, Channel, GuildMetadata, Member, Message, Resource, ServerMetaData, SocketConfig, Token } from './data-types';
|
||||
|
||||
import MessageRAMCache from "./message-ram-cache";
|
||||
import PersonalDB from "./personal-db";
|
||||
import ResourceRAMCache from "./resource-ram-cache";
|
||||
import SocketVerifier from './socket-verifier';
|
||||
import { Connectable, AsyncGuaranteedFetchable, Fetchable, Lackable, GuaranteedFetchable } from './guild-types';
|
||||
import { AutoVerifier } from './auto-verifier';
|
||||
import { AutoVerifierWithArg, PartialListQuery } from './auto-verifier-with-args';
|
||||
|
||||
export default class GuildPairVerifierGuild implements AsyncGuaranteedFetchable {
|
||||
|
||||
// Verifiers for the personaldb to the socket
|
||||
private readonly fetchMetadataVerifier: AutoVerifier<GuildMetadata>;
|
||||
private readonly fetchMembersVerifier: AutoVerifier<Member[]>;
|
||||
private readonly fetchChannelsVerifier: AutoVerifier<Channel[]>;
|
||||
private readonly fetchTokensVerifier: AutoVerifier<Token[]>;
|
||||
|
||||
private readonly fetchResourceVerifier: AutoVerifierWithArg<Resource, { resourceId: string }>
|
||||
|
||||
private readonly fetchMessagesRecentVerifier: AutoVerifierWithArg<Message[], PartialListQuery>;
|
||||
private readonly fetchMessagesBeforeVerifier: AutoVerifierWithArg<Message[], PartialListQuery>;
|
||||
private readonly fetchMessagesAfterVerifier: AutoVerifierWithArg<Message[], PartialListQuery>;
|
||||
|
||||
// TODO: Figure out what to do to verify the resources
|
||||
// TODO: Figure out what to do to verify the messages
|
||||
|
||||
constructor(
|
||||
readonly primary: Fetchable & Lackable,
|
||||
readonly trusted: GuaranteedFetchable
|
||||
) {
|
||||
this.fetchMetadataVerifier = new AutoVerifier<GuildMetadata>(
|
||||
async () => await this.primary.fetchMetadata(),
|
||||
async () => await this.trusted.fetchMetadata(),
|
||||
async (personalDBMetadata: GuildMetadata | null, socketMetadata: GuildMetadata | null) => {
|
||||
if (personalDBMetadata === null && socketMetadata === null) {
|
||||
return;
|
||||
} else if (personalDBMetadata === null) {
|
||||
this.primary.handleMetadataChanged(socketMetadata as GuildMetadata);
|
||||
} else if (socketMetadata === null) {
|
||||
return;
|
||||
} else {
|
||||
this.primary.handleMetadataChanged(socketMetadata as GuildMetadata);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.fetchMembersVerifier = AutoVerifier.createStandardListAutoVerifier<Member>(
|
||||
async () => await this.primary.fetchMembers(),
|
||||
async () => await this.trusted.fetchMembers(),
|
||||
async (changes: Changes<Member>) => {
|
||||
if (changes.added.length > 0) this.primary.handleMembersAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleMembersChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleMembersDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchChannelsVerifier = AutoVerifier.createStandardListAutoVerifier<Channel>(
|
||||
async () => await this.primary.fetchChannels(),
|
||||
async () => await this.trusted.fetchChannels(),
|
||||
async (changes: Changes<Channel>) => {
|
||||
if (changes.added.length > 0) this.primary.handleChannelsAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleChannelsChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleChannelsDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchTokensVerifier = AutoVerifier.createStandardListAutoVerifier<Token>(
|
||||
async () => await this.primary.fetchTokens(),
|
||||
async () => await this.trusted.fetchTokens(),
|
||||
async (changes: Changes<Token>) => {
|
||||
if (changes.added.length > 0) this.primary.handleTokensAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleTokensChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleTokensDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchResourceVerifier = new AutoVerifierWithArg<Resource, { resourceId: string }>(
|
||||
(query: { resourceId: string }) => query.resourceId,
|
||||
async (query: { resourceId: string }) => await this.primary.fetchResource(query.resourceId),
|
||||
async (query: { resourceId: string }) => await this.trusted.fetchResource(query.resourceId),
|
||||
async (query: { resourceId: string }, personalDBResource: Resource | null, socketResource: Resource | null) => {
|
||||
if (personalDBResource === null && socketResource === null) {
|
||||
return;
|
||||
} else if (personalDBResource === null) {
|
||||
this.primary.handleResourceAdded(socketResource as Resource);
|
||||
} else if (socketResource === null) {
|
||||
this.primary.handleResourceDeleted(personalDBResource as Resource);
|
||||
} else {
|
||||
this.primary.handleResourceChanged(socketResource as Resource);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesRecentVerifier = AutoVerifierWithArg.createPartialListAutoVerifier(
|
||||
async (query: PartialListQuery) => await this.primary.fetchMessagesRecent(query.channelId, query.number),
|
||||
async (query: PartialListQuery) => await this.trusted.fetchMessagesRecent(query.channelId, query.number),
|
||||
async (query: PartialListQuery, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) this.primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleMessagesDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesBeforeVerifier = AutoVerifierWithArg.createPartialListAutoVerifier(
|
||||
async (query: PartialListQuery) => await this.primary.fetchMessagesBefore(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialListQuery) => await this.trusted.fetchMessagesBefore(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialListQuery, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) this.primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleMessagesDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
|
||||
this.fetchMessagesAfterVerifier = AutoVerifierWithArg.createPartialListAutoVerifier(
|
||||
async (query: PartialListQuery) => await this.primary.fetchMessagesAfter(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialListQuery) => await this.trusted.fetchMessagesAfter(query.channelId, query.messageId as string, query.number),
|
||||
async (query: PartialListQuery, changes: Changes<Message>) => {
|
||||
if (changes.added.length > 0) this.primary.handleMessagesAdded(changes.added);
|
||||
if (changes.updated.length > 0) this.primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint));
|
||||
if (changes.deleted.length > 0) this.primary.handleMessagesDeleted(changes.deleted);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
unverify() {
|
||||
this.fetchMetadataVerifier.unverify();
|
||||
this.fetchMembersVerifier.unverify();
|
||||
this.fetchChannelsVerifier.unverify();
|
||||
this.fetchTokensVerifier.unverify();
|
||||
|
||||
this.fetchResourceVerifier.unverifyAll();
|
||||
|
||||
this.fetchMessagesRecentVerifier.unverifyAll();
|
||||
this.fetchMessagesBeforeVerifier.unverifyAll();
|
||||
this.fetchMessagesAfterVerifier.unverifyAll();
|
||||
}
|
||||
|
||||
async fetchMetadata(): Promise<GuildMetadata> {
|
||||
return await this.fetchMetadataVerifier.fetchAndVerifyIfNeeded() as GuildMetadata; // can use 'as' since socket is a Guaranteed fetchable
|
||||
}
|
||||
async fetchMembers(): Promise<Member[]> {
|
||||
return await this.fetchMembersVerifier.fetchAndVerifyIfNeeded() as Member[];
|
||||
}
|
||||
async fetchChannels(): Promise<Channel[]> {
|
||||
return await this.fetchChannelsVerifier.fetchAndVerifyIfNeeded() as Channel[];
|
||||
}
|
||||
async fetchMessagesRecent(channelId: string, number: number): Promise<Message[]> {
|
||||
return await this.fetchMessagesRecentVerifier.fetchAndVerifyIfNeded({ channelId, messageId: null, number }) as Message[];
|
||||
}
|
||||
async fetchMessagesBefore(channelId: string, messageId: string, number: number): Promise<Message[]> {
|
||||
return await this.fetchMessagesBeforeVerifier.fetchAndVerifyIfNeded({ channelId, messageId, number }) as Message[];
|
||||
}
|
||||
async fetchMessagesAfter(channelId: string, messageId: string, number: number): Promise<Message[]> {
|
||||
return await this.fetchMessagesAfterVerifier.fetchAndVerifyIfNeded({ channelId, messageId, number }) as Message[];
|
||||
}
|
||||
async fetchResource(resourceId: string): Promise<Resource> {
|
||||
return await this.fetchResourceVerifier.fetchAndVerifyIfNeded({ resourceId }) as Resource;
|
||||
}
|
||||
async fetchTokens(): Promise<Token[]> {
|
||||
return await this.fetchTokensVerifier.fetchAndVerifyIfNeeded() as Token[];
|
||||
}
|
||||
}
|
@ -1,16 +1,14 @@
|
||||
import { AsyncFetchable, AsyncLackable, AsyncLackableConnectable, Connectable } from "./guild-types";
|
||||
import { Channel, GuildMetadata, Member, Message, Resource, ServerMetaData, Token } from "./data-types";
|
||||
import { AsyncFetchable, AsyncLackable } from "./guild-types";
|
||||
import { Channel, GuildMetadata, Member, Message, Resource, Token } from "./data-types";
|
||||
|
||||
import PersonalDB from "./personal-db";
|
||||
|
||||
export default class PersonalDBGuild extends AsyncLackableConnectable implements AsyncFetchable {
|
||||
export default class PersonalDBGuild implements AsyncFetchable, AsyncLackable {
|
||||
constructor(
|
||||
private readonly db: PersonalDB,
|
||||
private readonly guildId: number,
|
||||
private readonly memberId: string, // would be used if we want to cache-update the status/display name of members
|
||||
) {
|
||||
super();
|
||||
}
|
||||
) {}
|
||||
|
||||
// Fetched Methods
|
||||
|
||||
@ -54,68 +52,53 @@ export default class PersonalDBGuild extends AsyncLackableConnectable implements
|
||||
(async () => { await this.db.updateGuildIcon(this.guildId, changedMetaData.iconResourceId); })();
|
||||
}
|
||||
|
||||
async handleMembersAdded(hadData: boolean, addedMembers: Member[]): Promise<void> {
|
||||
async handleMembersAdded(addedMembers: Member[]): Promise<void> {
|
||||
await this.db.addMembers(this.guildId, addedMembers);
|
||||
await super.handleMembersAdded(addedMembers);
|
||||
}
|
||||
async handleMembersChanged(changedMembers: Member[]): Promise<void> {
|
||||
await this.db.updateMembers(this.guildId, changedMembers);
|
||||
await super.handleMembersChanged(changedMembers);
|
||||
}
|
||||
async handleMembersDeleted(deletedMembers: Member[]): Promise<void> {
|
||||
await this.db.deleteMembers(this.guildId, deletedMembers);
|
||||
await super.handleMembersDeleted(deletedMembers);
|
||||
}
|
||||
|
||||
async handleChannelsAdded(hadData: boolean, addedChannels: Channel[]): Promise<void> {
|
||||
async handleChannelsAdded(addedChannels: Channel[]): Promise<void> {
|
||||
await this.db.addChannels(this.guildId, addedChannels);
|
||||
await super.handleChannelsAdded(addedChannels);
|
||||
}
|
||||
async handleChannelsChanged(changedChannels: Channel[]): Promise<void> {
|
||||
await this.db.updateChannels(this.guildId, changedChannels);
|
||||
await super.handleChannelsChanged(changedChannels);
|
||||
}
|
||||
async handleChannelsDeleted(deletedChannels: Channel[]): Promise<void> {
|
||||
await this.db.deleteChannels(this.guildId, deletedChannels);
|
||||
await super.handleChannelsDeleted(deletedChannels);
|
||||
}
|
||||
|
||||
async handleMessagesAdded(hadData: boolean, addedMessages: Message[]): Promise<void> {
|
||||
async handleMessagesAdded(addedMessages: Message[]): Promise<void> {
|
||||
await this.db.addMessages(this.guildId, addedMessages);
|
||||
await super.handleMessagesAdded(addedMessages);
|
||||
}
|
||||
async handleMessagesChanged(changedMessages: Message[]): Promise<void> {
|
||||
await this.db.updateMessages(this.guildId, changedMessages);
|
||||
await super.handleMembersAdded(addedMembers);
|
||||
}
|
||||
async handleMessagesDeleted(deletedMessages: Message[]): Promise<void> {
|
||||
await this.db.deleteMessages(this.guildId, deletedMessages);
|
||||
await super.handleMessagesDeleted(deletedMessages);
|
||||
}
|
||||
|
||||
async handleResourceAdded(hadData: boolean, addedResource: Resource): Promise<void> {
|
||||
async handleResourceAdded(addedResource: Resource): Promise<void> {
|
||||
await this.db.addResources(this.guildId, [ addedResource ]);
|
||||
await super.handleResourceAdded(addedResource);
|
||||
}
|
||||
async handleResourceChanged(changedResource: Resource): Promise<void> {
|
||||
await this.db.updateResources(this.guildId, [ changedResource ]);
|
||||
await super.handleResourceChanged(changedResource);
|
||||
}
|
||||
async handleResourceDeleted(deletedResource: Resource): Promise<void> {
|
||||
await this.db.deleteResources(this.guildId, [ deletedResource ]);
|
||||
await super.handleResourceDeleted(deletedResource);
|
||||
}
|
||||
|
||||
async handleTokensAdded(hadData: boolean, addedTokens: Token[]): Promise<void> {
|
||||
await super.handleTokensAdded(addedTokens);
|
||||
async handleTokensAdded(addedTokens: Token[]): Promise<void> {
|
||||
return; // cache currently does not handle tokens
|
||||
}
|
||||
async handleTokensChanged(changedTokens: Token[]): Promise<void> {
|
||||
await super.handleTokensChanged(changedTokens);
|
||||
return; // cache currently does not handle tokens
|
||||
}
|
||||
async handleTokensDeleted(deletedTokens: Token[]): Promise<void> {
|
||||
await super.handleTokensDeleted(deletedTokens);
|
||||
return; // cache currently does not handle tokens
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||
this.metadata = changedMetaData;
|
||||
}
|
||||
|
||||
handleMembersAdded(hadData: boolean, addedMembers: Member[]): void {
|
||||
handleMembersAdded(addedMembers: Member[]): void {
|
||||
for (let member of addedMembers) {
|
||||
this.members.set(member.id, member);
|
||||
}
|
||||
@ -66,7 +66,7 @@ export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||
}
|
||||
}
|
||||
|
||||
handleChannelsAdded(hadData: boolean, addedChannels: Channel[]): void {
|
||||
handleChannelsAdded(addedChannels: Channel[]): void {
|
||||
for (let channel of addedChannels) {
|
||||
this.channels.set(channel.id, channel);
|
||||
}
|
||||
@ -82,7 +82,7 @@ export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||
}
|
||||
}
|
||||
|
||||
handleMessagesAdded(hadData: boolean, addedMessages: Message[]): void {
|
||||
handleMessagesAdded(addedMessages: Message[]): void {
|
||||
let byChannel = new Map<string, Message[]>();
|
||||
for (let message of addedMessages) {
|
||||
if (!byChannel.has(message.channel.id)) {
|
||||
@ -122,7 +122,7 @@ export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||
}
|
||||
}
|
||||
|
||||
handleResourceAdded(hadData: boolean, addedResource: Resource): void {
|
||||
handleResourceAdded(addedResource: Resource): void {
|
||||
this.resources.putResource(this.guildId, addedResource);
|
||||
}
|
||||
handleResourceChanged(changedResource: Resource): void {
|
||||
@ -135,7 +135,7 @@ export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||
return;
|
||||
}
|
||||
|
||||
handleTokensAdded(hadData: boolean, addedTokens: Token[]): void {
|
||||
handleTokensAdded(addedTokens: Token[]): void {
|
||||
return; // TODO: token ram cache
|
||||
}
|
||||
handleTokensChanged(changedTokens: Token[]): void {
|
||||
|
@ -10,9 +10,10 @@ import { Connectable, AsyncRequestable, AsyncGuaranteedFetchable } from './guild
|
||||
import DedupAwaiter from './dedup-awaiter';
|
||||
import Util from './util';
|
||||
import SocketVerifier from './socket-verifier';
|
||||
import { EventEmitter } from 'tsee';
|
||||
|
||||
// Note: you should not be calling the eventemitter functions on outside classes
|
||||
export default class SocketGuild extends Connectable implements AsyncGuaranteedFetchable, AsyncRequestable {
|
||||
export default class SocketGuild extends EventEmitter<Connectable> implements AsyncGuaranteedFetchable, AsyncRequestable {
|
||||
private queryDedups = new Map<string, DedupAwaiter<any>>();
|
||||
|
||||
constructor(
|
||||
@ -20,7 +21,6 @@ export default class SocketGuild extends Connectable implements AsyncGuaranteedF
|
||||
private verifier: SocketVerifier
|
||||
) {
|
||||
super();
|
||||
// TODO: These requests should be 'bindable' through a connectable structure
|
||||
this.socket.on('connect', async () => {
|
||||
this.emit('connect');
|
||||
});
|
||||
@ -28,18 +28,6 @@ export default class SocketGuild extends Connectable implements AsyncGuaranteedF
|
||||
this.emit('disconnect');
|
||||
});
|
||||
|
||||
// TODO: Make sure the UI updates when we get an added/updated member
|
||||
|
||||
// TODO: The server does not emit update-message
|
||||
this.socket.on('update-message', async (updatedDataMessage: any) => {
|
||||
let updatedMessage = Message.fromDBData(updatedDataMessage);
|
||||
this.emit('update-messages', [ updatedMessage ]);
|
||||
});
|
||||
this.socket.on('new-message', async (dataMessage: any) => {
|
||||
let message = Message.fromDBData(dataMessage);
|
||||
this.emit('new-messages', [ message ]);
|
||||
});
|
||||
|
||||
this.socket.on('update-member', async (updatedDataMember: any) => {
|
||||
let updatedMember = Member.fromDBData(updatedDataMember);
|
||||
this.emit('update-members', [ updatedMember ]);
|
||||
@ -63,6 +51,16 @@ export default class SocketGuild extends Connectable implements AsyncGuaranteedF
|
||||
let guildMeta = GuildMetadata.fromDBData(dataMeta);
|
||||
this.emit('update-metadata', guildMeta);
|
||||
});
|
||||
|
||||
// TODO: The server does not emit update-message
|
||||
this.socket.on('update-message', async (updatedDataMessage: any) => {
|
||||
let updatedMessage = Message.fromDBData(updatedDataMessage);
|
||||
this.emit('update-messages', [ updatedMessage ]);
|
||||
});
|
||||
this.socket.on('new-message', async (dataMessage: any) => {
|
||||
let message = Message.fromDBData(dataMessage);
|
||||
this.emit('new-messages', [ message ]);
|
||||
});
|
||||
}
|
||||
|
||||
// server helper functions
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Channel, GuildMetadata, Member, Message, Resource, Token } from './data-types';
|
||||
import { EventEmitter } from 'tsee';
|
||||
import { Changes, Channel, GuildMetadata, Member, Message, Resource, Token } from './data-types';
|
||||
import { DefaultEventMap, EventEmitter } from 'tsee';
|
||||
|
||||
// Fetchable
|
||||
|
||||
@ -60,23 +60,23 @@ export type Requestable = AsyncRequestable;
|
||||
export interface AsyncLackable {
|
||||
handleMetadataChanged(changedMetaData: GuildMetadata): Promise<void>;
|
||||
|
||||
handleMembersAdded(hadData: boolean, addedMembers: Member[]): Promise<void>;
|
||||
handleMembersAdded(addedMembers: Member[]): Promise<void>;
|
||||
handleMembersChanged(changedMembers: Member[]): Promise<void>;
|
||||
handleMembersDeleted(deletedMembers: Member[]): Promise<void>;
|
||||
|
||||
handleChannelsAdded(hadData: boolean, addedChannels: Channel[]): Promise<void>;
|
||||
handleChannelsAdded(addedChannels: Channel[]): Promise<void>;
|
||||
handleChannelsChanged(changedChannels: Channel[]): Promise<void>;
|
||||
handleChannelsDeleted(deletedChannels: Channel[]): Promise<void>;
|
||||
|
||||
handleMessagesAdded(hadData: boolean, addedMessages: Message[]): Promise<void>;
|
||||
handleMessagesAdded(addedMessages: Message[]): Promise<void>;
|
||||
handleMessagesChanged(changedMessages: Message[]): Promise<void>;
|
||||
handleMessagesDeleted(deletedMessages: Message[]): Promise<void>;
|
||||
|
||||
handleResourceAdded(hadData: boolean, addedResource: Resource): Promise<void>;
|
||||
handleResourceAdded(addedResource: Resource): Promise<void>;
|
||||
handleResourceChanged(changedResource: Resource): Promise<void>;
|
||||
handleResourceDeleted(deletedResource: Resource): Promise<void>;
|
||||
|
||||
handleTokensAdded(hadData: boolean, addedTokens: Token[]): Promise<void>;
|
||||
handleTokensAdded(addedTokens: Token[]): Promise<void>;
|
||||
handleTokensChanged(changedTokens: Token[]): Promise<void>;
|
||||
handleTokensDeleted(deletedTokens: Token[]): Promise<void>;
|
||||
}
|
||||
@ -84,126 +84,55 @@ export interface AsyncLackable {
|
||||
export interface SyncLackable {
|
||||
handleMetadataChanged(changedMetaData: GuildMetadata): void;
|
||||
|
||||
handleMembersAdded(hadData: boolean, addedMembers: Member[]): void;
|
||||
handleMembersAdded(addedMembers: Member[]): void;
|
||||
handleMembersChanged(changedMembers: Member[]): void;
|
||||
handleMembersDeleted(deletedMembers: Member[]): void;
|
||||
|
||||
handleChannelsAdded(hadData: boolean, addedChannels: Channel[]): void;
|
||||
handleChannelsAdded(addedChannels: Channel[]): void;
|
||||
handleChannelsChanged(changedChannels: Channel[]): void;
|
||||
handleChannelsDeleted(deletedChannels: Channel[]): void;
|
||||
|
||||
handleMessagesAdded(hadData: boolean, addedMessages: Message[]): void;
|
||||
handleMessagesAdded(addedMessages: Message[]): void;
|
||||
handleMessagesChanged(changedMessages: Message[]): void;
|
||||
handleMessagesDeleted(deletedMessages: Message[]): void;
|
||||
|
||||
handleResourceAdded(hadData: boolean, addedResource: Resource): void;
|
||||
handleResourceAdded(addedResource: Resource): void;
|
||||
handleResourceChanged(changedResource: Resource): void;
|
||||
handleResourceDeleted(deletedResource: Resource): void;
|
||||
|
||||
handleTokensAdded(hadData: boolean, addedTokens: Token[]): void;
|
||||
handleTokensAdded(addedTokens: Token[]): void;
|
||||
handleTokensChanged(changedTokens: Token[]): void;
|
||||
handleTokensDeleted(deletedTokens: Token[]): void;
|
||||
}
|
||||
|
||||
export type Lackable = AsyncLackable | SyncLackable;
|
||||
|
||||
// Connectable
|
||||
|
||||
export class Connectable extends EventEmitter<{
|
||||
'connect': () => void,
|
||||
'disconnect': () => void,
|
||||
|
||||
'update-metadata': (guildMeta: GuildMetadata) => void
|
||||
// A Connectable can emit server-like events
|
||||
export type Connectable = {
|
||||
'connect': () => void;
|
||||
'disconnect': () => void;
|
||||
|
||||
'new-channels': (channels: Channel[]) => void,
|
||||
'update-channels': (updatedChannels: Channel[]) => void,
|
||||
'update-metadata': (guildMeta: GuildMetadata) => void;
|
||||
|
||||
'new-members': (members: Member[]) => void,
|
||||
'update-members': (updatedMembers: Member[]) => void,
|
||||
'new-channels': (channels: Channel[]) => void;
|
||||
'update-channels': (updatedChannels: Channel[]) => void;
|
||||
|
||||
'new-messages': (messages: Message[]) => void,
|
||||
'update-messages': (updatedMessages: Message[]) => void,
|
||||
}> {
|
||||
constructor() { super(); }
|
||||
'new-members': (members: Member[]) => void;
|
||||
'update-members': (updatedMembers: Member[]) => void;
|
||||
|
||||
'new-messages': (messages: Message[]) => void;
|
||||
'update-messages': (updatedMessages: Message[]) => void;
|
||||
}
|
||||
|
||||
export class AsyncLackableConnectable extends EventEmitter<{
|
||||
'updated-metadata': (guildMeta: GuildMetadata) => void,
|
||||
|
||||
'added-channels': (channels: Channel[]) => void,
|
||||
'updated-channels': (updatedChannels: Channel[]) => void,
|
||||
'deleted-channels': (channels: Channel[]) => void,
|
||||
|
||||
'added-members': (members: Member[]) => void,
|
||||
'updated-members': (updatedMembers: Member[]) => void,
|
||||
'deleted-members': (members: Member[]) => void,
|
||||
|
||||
'added-messages': (messages: Message[]) => void,
|
||||
'updated-messages': (updatedMessages: Message[]) => void,
|
||||
'deleted-messages': (messages: Message[]) => void,
|
||||
|
||||
'added-resource': (resource: Resource) => void,
|
||||
'updated-resource': (updatedResource: Resource) => void,
|
||||
'deleted-resource': (resource: Resource) => void,
|
||||
|
||||
'added-tokens': (tokens: Token[]) => void,
|
||||
'updated-tokens': (updatedTokens: Token[]) => void,
|
||||
'deleted-tokens': (tokens: Token[]) => void,
|
||||
}> implements AsyncLackable {
|
||||
constructor() { super(); }
|
||||
async handleMetadataChanged(changedMetaData: GuildMetadata): Promise<void> {
|
||||
this.emit('updated-metadata', changedMetaData);
|
||||
}
|
||||
|
||||
async handleMembersAdded(hadData: boolean, addedMembers: Member[]): Promise<void> {
|
||||
if (hadData) this.emit('added-members', addedMembers);
|
||||
}
|
||||
async handleMembersChanged(changedMembers: Member[]): Promise<void> {
|
||||
this.emit('updated-members', changedMembers);
|
||||
}
|
||||
async handleMembersDeleted(deletedMembers: Member[]): Promise<void> {
|
||||
this.emit('deleted-members', deletedMembers);
|
||||
}
|
||||
|
||||
async handleChannelsAdded(hadData: boolean, addedChannels: Channel[]): Promise<void> {
|
||||
if (hadData) this.emit('added-channels', addedChannels);
|
||||
}
|
||||
async handleChannelsChanged(changedChannels: Channel[]): Promise<void> {
|
||||
this.emit('updated-channels', changedChannels);
|
||||
}
|
||||
async handleChannelsDeleted(deletedChannels: Channel[]): Promise<void> {
|
||||
this.emit('deleted-channels', deletedChannels);
|
||||
}
|
||||
|
||||
// TODO: These are most likely pretty messy...
|
||||
async handleMessagesAdded(hadData: boolean, addedMessages: Message[]): Promise<void> {
|
||||
if (hadData) this.emit('added-messages', addedMessages);
|
||||
}
|
||||
async handleMessagesChanged(changedMessages: Message[]): Promise<void> {
|
||||
this.emit('updated-messages', changedMessages);
|
||||
}
|
||||
async handleMessagesDeleted(deletedMessages: Message[]): Promise<void> {
|
||||
this.emit('deleted-messages', deletedMessages);
|
||||
}
|
||||
|
||||
async handleResourceAdded(hadData: boolean, addedResource: Resource): Promise<void> {
|
||||
if (hadData) this.emit('added-resource', addedResource);
|
||||
}
|
||||
async handleResourceChanged(changedResource: Resource): Promise<void> {
|
||||
this.emit('updated-resource', changedResource);
|
||||
}
|
||||
async handleResourceDeleted(deletedResource: Resource): Promise<void> {
|
||||
this.emit('deleted-resource', deletedResource);
|
||||
}
|
||||
|
||||
async handleTokensAdded(hadData: boolean, addedTokens: Token[]): Promise<void> {
|
||||
if (hadData) this.emit('added-tokens', addedTokens);
|
||||
}
|
||||
async handleTokensChanged(changedTokens: Token[]): Promise<void> {
|
||||
this.emit('updated-tokens', changedTokens);
|
||||
}
|
||||
async handleTokensDeleted(deletedTokens: Token[]): Promise<void> {
|
||||
this.emit('deleted-tokens', deletedTokens);
|
||||
}
|
||||
// A Conflictable could emit conflict-based events if data changed based on verification
|
||||
// These events should be emitted *after* the conflicts have been resolved
|
||||
export type Conflictable = {
|
||||
'conflict-metadata': (oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => void;
|
||||
'conflict-channels': (changes: Changes<Channel>) => void;
|
||||
'conflict-members': (changes: Changes<Member>) => void;
|
||||
'conflict-messages': (changes: Changes<Message>) => void;
|
||||
'conflict-tokens': (changes: Changes<Token>) => void;
|
||||
'conflict-resource': (oldResource: Resource, newResource: Resource) => void;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user