viewing asked questions
parent
ce737bfc22
commit
ec8192531d
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,9 @@
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"jsii": {
|
"jsii": {
|
||||||
"excludeTypescript": ["*.test.ts"],
|
"excludeTypescript": [
|
||||||
|
"**/*.test.ts"
|
||||||
|
],
|
||||||
"versionFormat": "full",
|
"versionFormat": "full",
|
||||||
"outdir": "dist",
|
"outdir": "dist",
|
||||||
"tsc": {
|
"tsc": {
|
||||||
|
@ -42,6 +44,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.8.4",
|
"@types/node": "^20.8.4",
|
||||||
"jsii": "^5.2.14",
|
"jsii": "^5.2.14",
|
||||||
"jsii-pacmak": "^1.90.0"
|
"jsii-pacmak": "^1.90.0",
|
||||||
|
"vitest": "^0.34.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { assert, describe, expect, it } from "vitest";
|
||||||
|
import { ListDataResponse, QuestionData, QuestionMetadata, QuestionMetadataRaw, QuestionTextResponse, QuestionType } from "./types";
|
||||||
|
import { TCResponseRaw } from "../types";
|
||||||
|
|
||||||
|
describe("ask module", () => {
|
||||||
|
it("should parse QuestionMetadataRaw into QuestionMetadata", () => {
|
||||||
|
const raw: QuestionMetadataRaw = {
|
||||||
|
"id": 2219026,
|
||||||
|
"ia": true,
|
||||||
|
"r": 1,
|
||||||
|
"nr": 0,
|
||||||
|
"it": 1697785844,
|
||||||
|
"la": 13548862,
|
||||||
|
"at": 1697526644,
|
||||||
|
"p": false,
|
||||||
|
"pn": false,
|
||||||
|
"t": "text"
|
||||||
|
};
|
||||||
|
|
||||||
|
const constructed = new QuestionMetadata(raw);
|
||||||
|
|
||||||
|
expect(constructed).toEqual({
|
||||||
|
id: 2219026,
|
||||||
|
isActive: true,
|
||||||
|
replyCount: 1,
|
||||||
|
newReplyCount: 0,
|
||||||
|
expireTime: 1697785844,
|
||||||
|
creationTime: 1697526644,
|
||||||
|
isPinned: false,
|
||||||
|
questionType: QuestionType.TEXT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse raw ListDataResponse", () => {
|
||||||
|
const response: TCResponseRaw = {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"id": 2200000,
|
||||||
|
"ia": true,
|
||||||
|
"r": 1,
|
||||||
|
"nr": 0,
|
||||||
|
"it": 1697785844,
|
||||||
|
"la": 13548862,
|
||||||
|
"at": 1697526644,
|
||||||
|
"p": false,
|
||||||
|
"pn": false,
|
||||||
|
"t": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2200001,
|
||||||
|
"ia": false,
|
||||||
|
"r": 12,
|
||||||
|
"nr": 0,
|
||||||
|
"it": 1697731297,
|
||||||
|
"la": 13549378,
|
||||||
|
"at": 1697472097,
|
||||||
|
"p": false,
|
||||||
|
"pn": false,
|
||||||
|
"t": "text"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const constructed = new ListDataResponse(response);
|
||||||
|
|
||||||
|
expect(constructed.questions).toBeTruthy();
|
||||||
|
expect(constructed.questions).toHaveLength(2);
|
||||||
|
expect(constructed.questions[0]).toBeTruthy();
|
||||||
|
expect(constructed.questions[0].id).toBe(2200000);
|
||||||
|
expect(constructed.questions[1].id).toBe(2200001);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fetchQuestions method", () => {
|
||||||
|
it("should create question data from metadata and text", () => {
|
||||||
|
const listData: TCResponseRaw = {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"id": 2219000,
|
||||||
|
"ia": true,
|
||||||
|
"r": 1,
|
||||||
|
"nr": 0,
|
||||||
|
"it": 1697785844,
|
||||||
|
"la": 13548862,
|
||||||
|
"at": 1697526644,
|
||||||
|
"p": false,
|
||||||
|
"pn": false,
|
||||||
|
"t": "text"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const questionText: TCResponseRaw = {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"id": 2219000,
|
||||||
|
"b": "among us"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const metadata = new ListDataResponse(listData).questions;
|
||||||
|
const metadataMap = new Map(metadata.map((u) => [u.id, u]));
|
||||||
|
const questions = new QuestionTextResponse(questionText).questions;
|
||||||
|
|
||||||
|
const data = QuestionData._mapFrom(metadataMap, questions);
|
||||||
|
|
||||||
|
assert(metadata.length > 0);
|
||||||
|
assert(data.length > 0);
|
||||||
|
expect(data[0].body).toEqual("among us");
|
||||||
|
expect(data[0].header).toEqual(metadata[0]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Module } from "../module";
|
||||||
|
import { ListDataResponse, QuestionData, QuestionMetadata, QuestionTextResponse } from "./types";
|
||||||
|
|
||||||
|
export class AskModule extends Module {
|
||||||
|
//#questionCache: Cache<QuestionData> = new Cache<QuestionData>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the metadata of all questions. This does nothing by itself.
|
||||||
|
*/
|
||||||
|
public async listData() {
|
||||||
|
return await this.client._call(
|
||||||
|
ListDataResponse,
|
||||||
|
"legacy.askapi",
|
||||||
|
{
|
||||||
|
arg1: "0",
|
||||||
|
arg2: "none",
|
||||||
|
arg3: "0",
|
||||||
|
name: "listdata",
|
||||||
|
rawEmbeddedJsonLolInternalTechDebt: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchQuestions(metadata: QuestionMetadata[]) {
|
||||||
|
const metadataMap = new Map(metadata.map((u) => [u.id, u]));
|
||||||
|
const text = await this.fetchQuestionsText(metadata);
|
||||||
|
//return text.questions
|
||||||
|
// .map((body) => new QuestionData(body, metadataMap.get(body.id)));
|
||||||
|
return QuestionData._mapFrom(metadataMap, text.questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches question content given list of question metadata. Does nothing
|
||||||
|
* by itself.
|
||||||
|
*/
|
||||||
|
public async fetchQuestionsText(metadata: QuestionMetadata[]) {
|
||||||
|
const ids = metadata.map((q) => q.id);
|
||||||
|
return await this.client._call(
|
||||||
|
QuestionTextResponse,
|
||||||
|
"legacy.askapi",
|
||||||
|
{
|
||||||
|
arg1: ids.join("x"),
|
||||||
|
arg2: "0",
|
||||||
|
arg3: "0",
|
||||||
|
name: "qtext",
|
||||||
|
rawEmbeddedJsonLolInternalTechDebt: null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { TCResponse, TCResponseRaw } from "../types";
|
||||||
|
|
||||||
|
//export class FetchQuestionsResponse extends TCResponse {
|
||||||
|
// public constructor(response: TCResponseRaw) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
export class ListDataResponse extends TCResponse {
|
||||||
|
public questions: QuestionMetadata[];
|
||||||
|
|
||||||
|
public constructor(res: TCResponseRaw) {
|
||||||
|
super(res);
|
||||||
|
const data = res["questions"] as QuestionMetadataRaw[] ?? [];
|
||||||
|
this.questions = data.map((meta) => new QuestionMetadata(meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QuestionTextResponse extends TCResponse {
|
||||||
|
public questions: QuestionText[];
|
||||||
|
|
||||||
|
public constructor(res: TCResponseRaw) {
|
||||||
|
super(res);
|
||||||
|
const data = (res.questions ?? []) as QuestionTextRaw[];
|
||||||
|
this.questions = data.map((c) => new QuestionText(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuestionMetadataRaw {
|
||||||
|
readonly id: number;
|
||||||
|
readonly ia: boolean; // is active
|
||||||
|
readonly r: number; // replies
|
||||||
|
readonly nr: number; // new replies
|
||||||
|
readonly it: number; // time expires
|
||||||
|
readonly la: number; // ???
|
||||||
|
readonly at: number; // time asked
|
||||||
|
readonly p: boolean; // pinned
|
||||||
|
readonly pn: boolean; // pinned???
|
||||||
|
readonly t: string; // "text" or "poll"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum QuestionType {
|
||||||
|
TEXT,
|
||||||
|
POLL,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QuestionMetadata {
|
||||||
|
public id: number;
|
||||||
|
public isActive: boolean;
|
||||||
|
public replyCount: number;
|
||||||
|
public newReplyCount: number;
|
||||||
|
public expireTime: number;
|
||||||
|
public creationTime: number;
|
||||||
|
public isPinned: boolean;
|
||||||
|
public questionType: QuestionType;
|
||||||
|
|
||||||
|
public constructor(metadata: QuestionMetadataRaw) {
|
||||||
|
this.id = metadata.id;
|
||||||
|
this.isActive = metadata.ia;
|
||||||
|
this.replyCount = metadata.r;
|
||||||
|
this.newReplyCount = metadata.nr;
|
||||||
|
this.expireTime = metadata.it;
|
||||||
|
this.creationTime = metadata.at;
|
||||||
|
this.isPinned = metadata.p;
|
||||||
|
this.questionType = metadata.t == "poll" ?
|
||||||
|
QuestionType.POLL : QuestionType.TEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QuestionText {
|
||||||
|
public id: number;
|
||||||
|
public text: string;
|
||||||
|
|
||||||
|
public constructor(content: QuestionTextRaw) {
|
||||||
|
this.id = content.id;
|
||||||
|
this.text = content.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuestionTextRaw {
|
||||||
|
readonly id: number;
|
||||||
|
readonly b: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PollMetadataRaw {
|
||||||
|
readonly id: number;
|
||||||
|
readonly options: PollOptionRaw[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PollOptionRaw {
|
||||||
|
readonly n: number;
|
||||||
|
readonly c: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QuestionData {
|
||||||
|
public body: string;
|
||||||
|
public header: QuestionMetadata;
|
||||||
|
|
||||||
|
public constructor(c: QuestionText, m: QuestionMetadata | undefined) {
|
||||||
|
if (!m) {
|
||||||
|
throw "No metadata associated";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.id != m.id) {
|
||||||
|
throw "QuestionContent does not share ID with QuestionMetadata";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.body = c.text;
|
||||||
|
this.header = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
static _mapFrom(
|
||||||
|
metadata: Map<number, QuestionMetadata>,
|
||||||
|
questions: QuestionText[]
|
||||||
|
) {
|
||||||
|
return questions
|
||||||
|
.map((t) => new QuestionData(t, metadata.get(t.id)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/** @internal */
|
||||||
|
export class Cache<T> {
|
||||||
|
#cache: { [key: string]: CacheItem<T> } = { };
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
public query(key: string): CacheItem<T> | undefined {
|
||||||
|
if (key in this.#cache) {
|
||||||
|
return this.#cache[key];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetch(key: string): T | undefined {
|
||||||
|
return this.query(key)?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isItemStale(key: string): boolean {
|
||||||
|
return this.query(key)?.isStale ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public doesItemExist(key: string): boolean {
|
||||||
|
return this.query(key) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isItemValid(key: string): boolean {
|
||||||
|
return this.doesItemExist(key) && !this.isItemStale(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
class CacheItem<T> {
|
||||||
|
public timeToLive: number;
|
||||||
|
public origin: number = 0;
|
||||||
|
public value: T;
|
||||||
|
|
||||||
|
public constructor(value: T, timeToLive: number = 60000) {
|
||||||
|
this.value = value;
|
||||||
|
this.timeToLive = timeToLive;
|
||||||
|
this.origin = new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isStale(): boolean {
|
||||||
|
return new Date().getTime() > this.origin + this.timeToLive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public kill(): void {
|
||||||
|
this.origin = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +1,52 @@
|
||||||
|
import { AskModule } from "./ask";
|
||||||
import { MessagesModule } from "./messages";
|
import { MessagesModule } from "./messages";
|
||||||
import { MethodCall, TCJSONResponse, TCResponse } from "./types";
|
import { MethodCall, TCJSONResponse, TCResponse, TCResponseRaw } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client for Two Cans & String API.
|
* Client for Two Cans & String API.
|
||||||
*/
|
*/
|
||||||
export class Client {
|
export class Client {
|
||||||
static readonly BASE_URI = "https://twocansandstring.com/api";
|
static readonly BASE_URI = "https://twocansandstring.com/api";
|
||||||
static readonly VERSION = "1.68"
|
static readonly VERSION = "1.68";
|
||||||
|
|
||||||
#messages: MessagesModule;
|
#messages: MessagesModule;
|
||||||
|
#ask: AskModule;
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
public _cache: { [key: string]: any; } = { };
|
||||||
|
|
||||||
|
public auth?: string;
|
||||||
|
|
||||||
public get messages(): MessagesModule {
|
public get messages(): MessagesModule {
|
||||||
return this.#messages;
|
return this.#messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public auth: string;
|
public get ask(): AskModule {
|
||||||
|
return this.#ask;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(auth: string) {
|
public constructor(auth?: string) {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
|
||||||
// init modules
|
// init modules
|
||||||
this.#messages = new MessagesModule(this);
|
this.#messages = new MessagesModule(this);
|
||||||
|
this.#ask = new AskModule(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//public login(username: string, password: string) {
|
||||||
|
// this.call();
|
||||||
|
//}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls an API method.
|
* Calls an API method.
|
||||||
* @param methodName The name of the API method to call.
|
* @param methodName The name of the API method to call.
|
||||||
* @param args The arguments to pass to the API method.
|
* @param args The arguments to pass to the API method.
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
public async call<T extends TCResponse>(
|
public async _call<T extends TCResponse>(
|
||||||
methodName: string, args: { [key: string]: any }
|
creator: new(r: TCResponseRaw) => T,
|
||||||
|
methodName: string,
|
||||||
|
args: { [key: string]: any }
|
||||||
): Promise<T>
|
): Promise<T>
|
||||||
{
|
{
|
||||||
const methodCall: MethodCall = {
|
const methodCall: MethodCall = {
|
||||||
|
@ -44,10 +61,12 @@ export class Client {
|
||||||
|
|
||||||
const res = await this.fetch(body);
|
const res = await this.fetch(body);
|
||||||
|
|
||||||
return {
|
const rawResponse: TCResponseRaw = {
|
||||||
ok: res?.ok ?? false,
|
ok: res?.ok ?? false,
|
||||||
...res?.responses[0],
|
...res?.responses[0],
|
||||||
} as T;
|
} as T;
|
||||||
|
|
||||||
|
return new creator(rawResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetch(body: any): Promise<TCJSONResponse | undefined> {
|
private async fetch(body: any): Promise<TCJSONResponse | undefined> {
|
||||||
|
|
|
@ -3,3 +3,5 @@ export * from "./client";
|
||||||
export * as types from "./types";
|
export * as types from "./types";
|
||||||
export * from "./messages";
|
export * from "./messages";
|
||||||
export * as messages from "./messages/types";
|
export * as messages from "./messages/types";
|
||||||
|
export * from "./ask";
|
||||||
|
export * as ask from "./ask/types";
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { FolderListResponse, FolderViewResponse, MessageViewResponse } from "./t
|
||||||
|
|
||||||
export class MessagesModule extends Module {
|
export class MessagesModule extends Module {
|
||||||
public async folderView(folder = "inbox", page = 1) {
|
public async folderView(folder = "inbox", page = 1) {
|
||||||
return await this.client.call<FolderViewResponse>(
|
return await this.client._call(
|
||||||
|
FolderViewResponse,
|
||||||
"messages.folderview",
|
"messages.folderview",
|
||||||
{
|
{
|
||||||
folder,
|
folder,
|
||||||
|
@ -19,7 +20,8 @@ export class MessagesModule extends Module {
|
||||||
markAsRead = true,
|
markAsRead = true,
|
||||||
page = 1
|
page = 1
|
||||||
) {
|
) {
|
||||||
return await this.client.call<MessageViewResponse>(
|
return await this.client._call(
|
||||||
|
MessageViewResponse,
|
||||||
"messages.view",
|
"messages.view",
|
||||||
{
|
{
|
||||||
conversationId,
|
conversationId,
|
||||||
|
@ -35,7 +37,8 @@ export class MessagesModule extends Module {
|
||||||
text: string,
|
text: string,
|
||||||
unanonymize = false
|
unanonymize = false
|
||||||
) {
|
) {
|
||||||
return await this.client.call<TCResponse>(
|
return await this.client._call(
|
||||||
|
TCResponse,
|
||||||
"messages.reply",
|
"messages.reply",
|
||||||
{
|
{
|
||||||
conversationId,
|
conversationId,
|
||||||
|
@ -46,7 +49,8 @@ export class MessagesModule extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async folderList() {
|
public async folderList() {
|
||||||
return await this.client.call<FolderListResponse>(
|
return await this.client._call(
|
||||||
|
FolderListResponse,
|
||||||
"messages.folderlist",
|
"messages.folderlist",
|
||||||
{ }
|
{ }
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +1,27 @@
|
||||||
import { TCResponse, TCUser } from "../types";
|
import { TCResponse, TCResponseRaw, TCUser } from "../types";
|
||||||
|
|
||||||
export interface FolderViewResponse extends TCResponse {
|
export class FolderViewResponse extends TCResponse {
|
||||||
readonly hasMore: boolean;
|
hasMore: boolean;
|
||||||
readonly hasPrevious: boolean;
|
hasPrevious: boolean;
|
||||||
readonly messages: MessagePreview[];
|
messages: MessagePreview[];
|
||||||
|
|
||||||
|
public constructor(response: TCResponseRaw) {
|
||||||
|
super(response);
|
||||||
|
this.hasMore = response["hasMore"] ?? false;
|
||||||
|
this.hasPrevious = response["hasPrevious"] ?? false;
|
||||||
|
this.messages = response["messages"] ?? [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageViewResponse extends TCResponse {
|
export class MessageViewResponse extends TCResponse {
|
||||||
readonly messages: Message[];
|
messages: Message[];
|
||||||
readonly header: MessageThreadHeader;
|
header: MessageThreadHeader;
|
||||||
|
|
||||||
|
public constructor(response: TCResponseRaw) {
|
||||||
|
super(response);
|
||||||
|
this.messages = response["messages"] ?? [];
|
||||||
|
this.header = response["header"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessagePreview {
|
export interface MessagePreview {
|
||||||
|
@ -46,8 +59,13 @@ export interface MessageFolder {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FolderListResponse extends TCResponse {
|
export class FolderListResponse extends TCResponse {
|
||||||
readonly folders: Folder[];
|
folders: Folder[];
|
||||||
|
|
||||||
|
public constructor(response: TCResponseRaw) {
|
||||||
|
super(response);
|
||||||
|
this.folders = response["folders"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Folder {
|
export interface Folder {
|
||||||
|
|
|
@ -6,15 +6,31 @@ export interface MethodCall {
|
||||||
export interface TCJSONResponse {
|
export interface TCJSONResponse {
|
||||||
readonly ok: boolean;
|
readonly ok: boolean;
|
||||||
readonly loginId: string;
|
readonly loginId: string;
|
||||||
readonly responses: TCResponse[];
|
readonly responses: TCResponseRaw[];
|
||||||
readonly profiles?: TCProfile[];
|
readonly profiles?: TCProfile[];
|
||||||
readonly auth: string;
|
readonly auth: string;
|
||||||
readonly ver: string;
|
readonly ver: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TCResponse {
|
export interface TCResponseRaw {
|
||||||
readonly ok: boolean;
|
/** @internal */
|
||||||
readonly profiles?: TCProfile[];
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TCResponse {
|
||||||
|
public ok: boolean = false;
|
||||||
|
public error?: string;
|
||||||
|
public profiles?: TCProfile[] = [];
|
||||||
|
|
||||||
|
public constructor(response: TCResponseRaw) {
|
||||||
|
this.ok = response.ok;
|
||||||
|
this.error = response.error;
|
||||||
|
this.profiles = response.profiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TCResponseConstructor {
|
||||||
|
new(response: TCJSONResponse): TCResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move this to another file
|
// TODO: move this to another file
|
||||||
|
|
Loading…
Reference in New Issue