cordis/client/webapp/guild-socket.ts
2021-11-30 20:45:06 -06:00

164 lines
7.0 KiB
TypeScript

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 { Channel, GuildMetadata, Member, Message, Resource, Token } from "./data-types";
import Globals from './globals';
import { Connectable, AsyncRequestable, AsyncGuaranteedFetchable } from './guild-types';
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 EventEmitter<Connectable> implements AsyncGuaranteedFetchable, AsyncRequestable {
private queryDedups = new Map<string, DedupAwaiter<any>>();
constructor(
private socket: socketio.Socket,
public verifier: SocketVerifier
) {
super();
this.socket.on('connect', async () => {
this.emit('connect');
});
this.socket.on('disconnect', () => {
this.emit('disconnect');
});
this.socket.on('update-member', async (updatedDataMember: any) => {
let updatedMember = Member.fromDBData(updatedDataMember);
this.emit('update-members', [ updatedMember ]);
});
// TODO: The server does not emit new-member
this.socket.on('new-member', async (dataMember: any) => {
let member = Member.fromDBData(dataMember);
this.emit('new-members', [ member ]);
});
this.socket.on('update-channel', async (updatedDataChannel: any) => {
let updatedChannel = Channel.fromDBData(updatedDataChannel);
this.emit('update-channels', [ updatedChannel ]);
});
this.socket.on('new-channel', async (dataChannel: any) => {
let channel = Channel.fromDBData(dataChannel);
this.emit('new-channels', [ channel ]);
});
this.socket.on('update-metadata', async (dataMeta: any) => {
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 ]);
});
}
public disconnect() {
this.socket.disconnect();
}
// server helper functions
private async query(timeout: number, endpoint: string, ...args: any[]): Promise<any> {
await this.verifier.ensureVerified();
return await new Promise((resolve, reject) => {
Util.socketEmitTimeout(this.socket, timeout, endpoint, ...args, (errMsg: string | null, serverData: any) => {
if (errMsg) {
reject(new Error(`error fetching server data @${endpoint} / [${args.map(arg => LOG.inspect(arg)).join(', ')}]: ${errMsg}`));
} else {
resolve(serverData);
}
});
});
}
// Note: timeout is only respected for the first deduped request
private async queryDedup(timeout: number, endpoint: string, ...args: (string | number)[]): Promise<any> {
let id = `query-@${endpoint}(${args.join(',')})`;
let dedup = this.queryDedups.get(id);
if (!dedup) {
dedup = new DedupAwaiter(async () => {
let result = await this.query(timeout, endpoint, ...args);
this.queryDedups.delete(id);
return result;
});
}
return await dedup.call();
}
// connectable functions
async fetchMetadata(): Promise<GuildMetadata> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-guild');
return GuildMetadata.fromDBData(data);
}
async fetchMembers(): Promise<Member[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-members');
return data.map((dataMember: any) => Member.fromDBData(dataMember));
}
async fetchChannels(): Promise<Channel[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-channels');
return data.map((dataChannel: any) => Channel.fromDBData(dataChannel));
}
async fetchMessagesRecent(channelId: string, number: number): Promise<Message[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-messages-recent', channelId, number);
return data.map((dataMessage: any) => Message.fromDBData(dataMessage));
}
async fetchMessagesBefore(channelId: string, messageId: string, number: number): Promise<Message[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-messages-before', channelId, messageId, number);
return data.map((dataMessage: any) => Message.fromDBData(dataMessage));
}
async fetchMessagesAfter(channelId: string, messageId: string, number: number): Promise<Message[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-messages-after', channelId, messageId, number);
return data.map((dataMessage: any) => Message.fromDBData(dataMessage));
}
async fetchResource(resourceId: string): Promise<Resource> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-resource', resourceId);
return Resource.fromDBData(data);
}
async fetchTokens(): Promise<Token[]> {
let data = await this.queryDedup(Globals.DEFAULT_SOCKET_TIMEOUT, 'fetch-tokens');
return data.map((dataToken: any) => Token.fromDBData(dataToken));
}
// We don't want to dedup these
async requestSendMessage(channelId: string, text: string): Promise<void> {
let _dataMessage = await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'send-message', channelId, text);
}
async requestSendMessageWithResource(channelId: string, text: string | null, resource: Buffer, resourceName: string): Promise<void> {
let _dataMessage = await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'send-message-with-resource', channelId, text, resource, resourceName);
}
async requestSetStatus(status: string): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'set-status', status);
}
async requestSetDisplayName(displayName: string): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'set-display-name', displayName);
}
async requestSetAvatar(avatar: Buffer): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'set-avatar', avatar);
}
async requestSetGuildName(guildName: string): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'set-name', guildName);
}
async requestSetGuildIcon(guildIcon: Buffer): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'set-icon', guildIcon);
}
async requestDoUpdateChannel(channelId: string, name: string, flavorText: string | null): Promise<void> {
let _changedChannel = await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'update-channel', channelId, name, flavorText);
}
async requestDoCreateChannel(name: string, flavorText: string | null): Promise<void> {
let _newChannel = await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'create-text-channel', name, flavorText);
}
async requestDoRevokeToken(token: string): Promise<void> {
await this.query(Globals.DEFAULT_SOCKET_TIMEOUT, 'revoke-token', token);
}
}