From 9e638d94f104f9a38c6da40c92c17da64d4d46f7 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Tue, 7 May 2024 23:31:16 -0700 Subject: [PATCH] add request batching and drawing module --- src/client.ts | 100 +++++++++++++++++++++++++++++++++++++++++-- src/drawing/index.ts | 42 ++++++++++++++++++ src/drawing/types.ts | 19 ++++++++ 3 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 src/drawing/index.ts create mode 100644 src/drawing/types.ts diff --git a/src/client.ts b/src/client.ts index 10ed816..9bf99e5 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,7 +1,15 @@ +import { MethodCall, TCJSONResponse, TCResponse, TCResponseRaw } from "./types"; import { AskModule } from "./ask"; import { MessagesModule } from "./messages"; import { ForumModule } from "./forum"; -import { MethodCall, TCJSONResponse, TCResponse, TCResponseRaw } from "./types"; +import { DrawingModule } from "./drawing"; + +interface QueuedCall { + methodCall: MethodCall; + creator: new(r: TCResponseRaw) => TCResponse; + resolve: (value: any) => void; + reject: (reason: any) => void; +} /** * Client for Two Cans & String API. @@ -13,6 +21,10 @@ export class Client { #messages: MessagesModule; #ask: AskModule; #forum: ForumModule; + #drawing: DrawingModule; + + #requestQueue: QueuedCall[] = []; + #isBatching: boolean = false; /** @internal */ public _cache: { [key: string]: any; } = { }; @@ -33,6 +45,22 @@ export class Client { return this.#forum; } + public get drawing(): DrawingModule { + return this.#drawing; + } + + public get isBatching(): boolean { + return this.#isBatching; + } + + public set isBatching(value: boolean) { + if (value) { + this.beginBatch(); + } else { + this.endBatch(); + } + } + public constructor(auth?: string) { this.auth = auth; @@ -40,6 +68,7 @@ export class Client { this.#messages = new MessagesModule(this); this.#ask = new AskModule(this); this.#forum = new ForumModule(this); + this.#drawing = new DrawingModule(this); } //public login(username: string, password: string) { @@ -53,7 +82,7 @@ export class Client { * @param args The arguments to pass to the API method. * @internal */ - public async _call( + public _call( creator: new(r: TCResponseRaw) => T, methodName: string, args: { [key: string]: any } @@ -64,19 +93,82 @@ export class Client { payload: args, }; + if (this.#isBatching) { + return new Promise((resolve, reject) => { + this.#requestQueue.push({ + methodCall, + creator, + resolve, + reject, + }); + }); + } + const body = { auth: this.auth, requests: [methodCall], }; + return new Promise((resolve, reject) => { + this.fetch(body) + .then((res) => { + const rawResponse: TCResponseRaw = { + ok: res?.ok ?? false, + ...res?.responses[0], + }; + + resolve(new creator(rawResponse)); + }) + .catch((reason) => reject(reason)); + }); + /* const res = await this.fetch(body); const rawResponse: TCResponseRaw = { ok: res?.ok ?? false, ...res?.responses[0], } as T; - + return new creator(rawResponse); + */ + } + + public beginBatch(): void { + this.#isBatching = true; + } + + public endBatch(process = true): void { + this.#isBatching = false; + if (process) { + this.processBatch(); + } + } + + public processBatch(): void { + const methodCalls = this.#requestQueue.map((c) => c.methodCall); + + const body = { + auth: this.auth, + requests: methodCalls, + }; + + this.fetch(body) + .then((res) => { + this.#requestQueue.forEach((c, index) => { + const individualResponse: TCResponseRaw = { + ok: res?.ok ?? false, + ...res?.responses[index], + }; + + c.resolve(new c.creator(individualResponse)); + }); + }) + .catch((reason) => { + this.#requestQueue.forEach((c) => c.reject(reason)); + }) + .finally(() => { + this.#requestQueue.length = 0; + }); } private async fetch(body: any): Promise { @@ -93,7 +185,7 @@ export class Client { const res = await fetch(Client.BASE_URI, req); if (!res.ok) { - return; + return Promise.reject(); } return await res.json() as TCJSONResponse; diff --git a/src/drawing/index.ts b/src/drawing/index.ts new file mode 100644 index 0000000..47afa04 --- /dev/null +++ b/src/drawing/index.ts @@ -0,0 +1,42 @@ +import { Module } from "../module"; +import { TCResponse } from "../types"; +import { SaveDrawingResponse, ViewDrawingDataResponse } from "./types"; + +export class DrawingModule extends Module { + public async saveDrawing(data: string, makeActive = false) { + this.client._call( + // newImageId: string + SaveDrawingResponse, + "legacy.drawing", + { + action: "save", + data, + makeActive, + } + ); + } + + public async viewDrawingData(imageId: string) { + // response { ok: boolean , pixelData: string } + this.client._call( + // newImageId: string + ViewDrawingDataResponse, + "legacy.drawing", + { + action: "initial-image-data", + imageId, + } + ); + } + + public async sendDrawing(imageId: string, receiver: string) { + this.client._call( + TCResponse, + "drawing.send", + { + imageId, + receiver, + } + ); + } +} diff --git a/src/drawing/types.ts b/src/drawing/types.ts new file mode 100644 index 0000000..399b98e --- /dev/null +++ b/src/drawing/types.ts @@ -0,0 +1,19 @@ +import { TCResponse, TCResponseRaw } from "../types"; + +export class SaveDrawingResponse extends TCResponse { + public newImageId: string; + + public constructor(res: TCResponseRaw) { + super(res); + this.newImageId = res["newImageId"]; + } +} + +export class ViewDrawingDataResponse extends TCResponse { + public data: string; + + public constructor(res: TCResponseRaw) { + super(res); + this.data = res["data"]; + } +}