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 { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier"; import { Changes, WithEquals } from "./data-types"; import Q from './q-module'; export interface PartialMessageListQuery { channelId: string; messageId: string | null; number: number; } export interface IDQuery { id: string; } export class AutoVerifierWithArg { private tokenAutoVerifiers = new Map>(); private tokenQueries = new Map(); // TODO: Clear out old autoverifiers that haven't been used for a while. constructor( private tokenizer: (query: K) => string, // must be one-to-one mapping private primaryFunc: (query: K) => Promise, private trustedFunc: (query: K) => Promise, private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise ) {} static createStandardPartialMessageListAutoVerifier & { id: string }>( primaryFunc: (query: PartialMessageListQuery) => Promise, trustedFunc: (query: PartialMessageListQuery) => Promise, changesFunc: (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes) => Promise ) { return new AutoVerifierWithArg( query => `ch#${query.channelId} m#${query.messageId}->${query.number}`, query => primaryFunc(query), query => trustedFunc(query), async (query: PartialMessageListQuery, primaryResult: T[] | null, trustedResult: T[] | null) => { // LOG.debug('messages verify: ', { // query, // // primaryResult: primaryResult?.map((e: any) => e.sent).sort(), // // trustedResult: trustedResult?.map((e: any) => e.sent).sort(), // zipped: primaryResult && trustedResult && primaryResult.length === trustedResult.length && Q.zip( // primaryResult?.sort((a: any, b: any) => a.sent.getTime() - b.sent.getTime()).map((e: any) => e.text), // trustedResult?.sort((a: any, b: any) => a.sent.getTime() - b.sent.getTime()).map((e: any) => e.text) // ) // }); let changes = AutoVerifier.getChanges(primaryResult, trustedResult); //LOG.debug('changes:', { changes }); let changesType = AutoVerifier.getListChangesType(primaryResult, trustedResult, changes); await changesFunc(query, changesType, changes); } ); } static createStandardIDQueriedSingleAutoVerifier & { id: string }>( primaryFunc: (query: IDQuery) => Promise, trustedFunc: (query: IDQuery) => Promise, changesFunc: (query: IDQuery, changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise ) { return new AutoVerifierWithArg( query => `id#${query.id}`, query => primaryFunc(query), query => trustedFunc(query), async (query: IDQuery, primaryResult: T | null, trustedResult: T | null) => { let changesType = AutoVerifier.getSingleChangesType(primaryResult, trustedResult); await changesFunc(query, changesType, primaryResult, trustedResult); } ); } unverifyAll(): void { for (let autoVerifier of this.tokenAutoVerifiers.values()) { autoVerifier.unverify(); } this.tokenAutoVerifiers.clear(); this.tokenQueries.clear(); } unverifySome(toUnverifyFilter: (query: K) => boolean) { let tokensToUnverify = Array.from(this.tokenQueries.values()) .filter(toUnverifyFilter) .map(query => this.tokenizer(query)); for (let token of tokensToUnverify) { this.tokenAutoVerifiers.get(token)?.unverify(); this.tokenAutoVerifiers.delete(token); this.tokenQueries.delete(token); } } async fetchAndVerifyIfNeded(query: K, lazyVerify: boolean = false): Promise { let token = this.tokenizer(query); this.tokenQueries.set(token, query); let autoVerifier = this.tokenAutoVerifiers.get(token); if (!autoVerifier) { autoVerifier = new AutoVerifier( async () => await this.primaryFunc(query), async () => await this.trustedFunc(query), async (primaryResult: T | null, trustedResult: T | null) => await this.verifyFunc(query, primaryResult, trustedResult), ); this.tokenAutoVerifiers.set(token, autoVerifier); } return await autoVerifier.fetchAndVerifyIfNeeded(lazyVerify); } }