cordis/client/webapp/guild-combined.ts

253 lines
9.6 KiB
TypeScript
Raw Normal View History

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';
2021-11-21 01:06:50 +00:00
import { Connectable, AsyncGuaranteedFetchable, Conflictable, AsyncRequestable } from './guild-types';
import PairVerifierFetchable from './fetchable-pair-verifier';
import EnsuredFetchable from './fetchable-ensured';
import { EventEmitter } from 'tsee';
2021-11-21 01:06:50 +00:00
export default class CombinedGuild extends EventEmitter<Connectable & Conflictable> implements AsyncGuaranteedFetchable, AsyncRequestable {
private readonly ramGuild: RAMGuild;
private readonly personalDBGuild: PersonalDBGuild;
private readonly socketGuild: SocketGuild;
2021-11-21 01:06:50 +00:00
private readonly pairVerifiers: PairVerifierFetchable[];
private readonly fetchable: AsyncGuaranteedFetchable;
constructor(
private readonly id: number,
private readonly memberId: string,
socket: socketio.Socket,
socketVerifier: SocketVerifier,
messageRAMCache: MessageRAMCache,
resourceRAMCache: ResourceRAMCache,
personalDB: PersonalDB
) {
super();
this.ramGuild = new RAMGuild(messageRAMCache, resourceRAMCache, this.id);
this.personalDBGuild = new PersonalDBGuild(personalDB, this.id, this.memberId);
this.socketGuild = new SocketGuild(socket, socketVerifier);
// TODO: Only unverify the personaldb->socket connection on d/c?
2021-11-21 01:06:50 +00:00
// Connect/Disconnect
this.socketGuild.on('connect', () => {
LOG.info(`g#${this.id} connected`);
this.emit('connect');
});
this.socketGuild.on('disconnect', () => {
LOG.info(`g#${this.id} disconnected`);
this.unverify();
this.emit('disconnect');
});
2021-11-21 01:06:50 +00:00
// Metadata
this.socketGuild.on('update-metadata', (guildMeta: GuildMetadata) => {
LOG.info(`g#${this.id} updated metadata: ${guildMeta}`);
this.emit('update-metadata', guildMeta);
});
2021-11-21 01:06:50 +00:00
// Members
this.socketGuild.on('new-members', async (members: Member[]) => {
for (let member of members) {
LOG.info(`g#${this.id} ${member}`);
}
2021-11-21 01:06:50 +00:00
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}`);
}
2021-11-21 01:06:50 +00:00
this.ramGuild.handleMembersChanged(members);
await this.personalDBGuild.handleMembersChanged(members);
this.emit('update-members', members);
});
2021-11-21 01:06:50 +00:00
// Channels
this.socketGuild.on('new-channels', async (channels: Channel[]) => {
for (let channel of channels) {
LOG.info(`g#${this.id} ${channel}`);
}
2021-11-21 01:06:50 +00:00
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}`);
}
2021-11-21 01:06:50 +00:00
this.ramGuild.handleChannelsChanged(channels);
await this.personalDBGuild.handleChannelsChanged(channels);
this.emit('update-channels', channels);
});
2021-11-21 01:06:50 +00:00
// Messages
this.socketGuild.on('new-messages', async (messages: Message[]) => {
for (let message of messages) {
LOG.info(`g#${this.id} ${message}`);
}
2021-11-21 01:06:50 +00:00
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}`);
}
2021-11-21 01:06:50 +00:00
this.ramGuild.handleMessagesChanged(messages);
await this.personalDBGuild.handleMessagesChanged(messages);
this.emit('update-messages', messages);
});
2021-11-21 01:06:50 +00:00
let personalDBSocketPairVerifier = new PairVerifierFetchable(this.personalDBGuild, this.socketGuild);
let ramPersonalDBSocketPairVerifier = new PairVerifierFetchable(this.ramGuild, personalDBSocketPairVerifier);
2021-11-21 01:06:50 +00:00
// 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 ];
2021-11-21 01:06:50 +00:00
this.fetchable = new EnsuredFetchable(ramPersonalDBSocketPairVerifier);
}
static async create(
personalGuildId: number,
socketConfig: SocketConfig,
messageRAMCache: MessageRAMCache,
resourceRAMCache: ResourceRAMCache,
personalDB: PersonalDB
) {
let socket = socketio.connect(socketConfig.url, {
forceNew: true,
ca: socketConfig.cert
});
let socketVerifier = new SocketVerifier(socket, socketConfig.publicKey, socketConfig.privateKey);
let memberId = await socketVerifier.verify();
2021-11-21 01:06:50 +00:00
return new CombinedGuild(
personalGuildId,
memberId,
socket,
socketVerifier,
messageRAMCache,
resourceRAMCache,
personalDB
);
}
private unverify() {
for (let pairVerifier of this.pairVerifiers) {
pairVerifier.unverify();
}
}
async ensureRAMMembers() {
if (this.ramGuild.getMembers().size === 0) {
await this.fetchMembers();
}
2021-11-21 01:06:50 +00:00
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();
}
2021-11-21 01:06:50 +00:00
if (this.ramGuild.getChannels().size === 0) throw new Error('RAM Channels was not updated through fetchChannels');
}
2021-11-21 01:06:50 +00:00
// Fetched through the triple-cache system (RAM -> Disk -> Server)
async fetchMetadata(): Promise<GuildMetadata> {
return await this.fetchable.fetchMetadata();
}
async fetchMembers(): Promise<Member[]> {
return await this.fetchable.fetchMembers();
}
async fetchChannels(): Promise<Channel[]> {
return await this.fetchable.fetchChannels();
}
async fetchMessagesRecent(channelId: string, number: number): Promise<Message[]> {
return await this.fetchable.fetchMessagesRecent(channelId, number);
}
async fetchMessagesBefore(channelId: string, messageId: string, number: number): Promise<Message[]> {
return await this.fetchable.fetchMessagesBefore(channelId, messageId, number);
}
async fetchMessagesAfter(channelId: string, messageId: string, number: number): Promise<Message[]> {
return await this.fetchable.fetchMessagesAfter(channelId, messageId, number);
}
async fetchResource(resourceId: string): Promise<Resource> {
return await this.fetchable.fetchResource(resourceId);
}
async fetchTokens(): Promise<Token[]> {
return await this.fetchable.fetchTokens();
}
2021-11-21 01:06:50 +00:00
// 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);
}
}