@haelp/teto
Version:
A typescript-based controllable TETR.IO client.
207 lines (193 loc) • 5.35 kB
text/typescript
import { type APIDefaults } from ".";
import type { Get, Post } from "./basic";
import chalk from "chalk";
export namespace Server {
export interface Signature {
version: string;
countdown: boolean;
novault: boolean;
noceriad: boolean;
norichpresence: boolean;
noreplaydispute: boolean;
supporter_specialthanks_goal: number;
xp_multiplier: number;
catalog: {
supporter: {
price: number;
price_bulk: number;
price_gift: number;
price_gift_bulk: number;
bulk_after: number;
normal_price: number;
normal_price_bulk: number;
normal_price_gift: number;
normal_price_gift_bulk: number;
normal_bulk_after: number;
};
"zenith-tower-ost": {
price: number;
normal_price: number;
};
};
league_mm_roundtime_min: number;
league_mm_roundtime_max: number;
league_additional_settings: Record<string, any>;
league_season: {
current: string;
prev: string;
next: string | null;
next_at: string | null;
ranked: boolean;
};
zenith_duoisfree: boolean;
zenith_freemod: boolean;
zenith_cpu_count: number;
zenith_additional_settings: {
TEMP_zenith_grace: string;
messiness_timeout: number;
};
domain: string;
ch_domain: string;
mode: string;
sentry_enabled: boolean;
serverCycle: string;
domain_hash: string;
client: {
commit: {
id: string;
time: number;
};
branch: string;
build: {
id: string;
time: number;
};
};
}
export interface Environment {
stats: {
players: number;
users: number;
gamesplayed: number;
gametime: number;
};
signature: Signature;
vx: string;
}
}
export const server = (get: Get, _: Post, options: APIDefaults) => {
const getDespool = async (endpoint: string, index: string) => {
const req = await fetch(
encodeURI(
`https://${endpoint}/spool?${Date.now()}-${index}-${Math.floor(1e6 * Math.random())}`
),
{
method: "GET",
headers: {
// Authorization: `Bearer ${options.token}`,
"User-Agent": options.userAgent
}
}
);
const res = new Uint8Array(await req.arrayBuffer());
const parseSpoolData = (binary: Uint8Array) => {
const version = binary[0];
const flags = {
online: binary[1] & 0b10000000,
avoidDueToHighLoad: binary[1] & 0b01000000,
recentlyRestarted: binary[1] & 0b00100000,
backhaulDisrupted: binary[1] & 0b00010000,
unused5: binary[1] & 0b00001000,
unused6: binary[1] & 0b00000100,
unused7: binary[1] & 0b00000010,
unused8: binary[1] & 0b00000001
} as const;
const load = [0, 0, 0];
const latency = binary[5] * 2;
load[0] = (binary[2] / 0x100) * 4;
load[1] = (binary[3] / 0x100) * 4;
load[2] = (binary[4] / 0x100) * 4;
return {
version,
flags,
load,
latency,
str: `v${version} /${flags.avoidDueToHighLoad ? "Av" : ""}${flags.recentlyRestarted ? "Rr" : ""}${flags.backhaulDisrupted ? "Bd" : ""}/ ${load[0]}, ${load[1]}, ${load[2]}`
};
};
return parseSpoolData(res);
};
const findFastestAvailableSpool = async (
spools: { name: string; host: string; flag: string; location: string }[]
): Promise<{
name: string;
host: string;
flag: string;
location: string;
}> => {
try {
return await Promise.any(
spools.map(async (s, index) => {
const spool = await getDespool(s.host, index.toString());
if (spool.flags.avoidDueToHighLoad || spool.flags.recentlyRestarted)
throw new Error("Spool is unstable");
return s;
})
);
} catch {
console.log(
`${chalk.yellow("[🎀\u2009Ribbon]")}: All spools down or recently restarted (unstable). Falling back to root TETR.IO host.`
);
}
return {
name: "tetr.io",
host: "tetr.io",
flag: "NL",
location: "osk"
};
};
return {
environment: async (): Promise<Server.Environment> => {
const result = await get<Server.Environment>({
uri: "server/environment"
});
if (result.success === false) throw new Error(result.error.msg);
return result;
},
spool: async (useSpools: boolean) => {
const res = await get<{
endpoint: string;
spools: {
token: string;
spools: {
name: string;
host: string;
flag: string;
location: string;
}[];
} | null;
}>({
uri: "server/ribbon"
});
if (res.success === false) {
throw new Error(res.error.msg);
}
if (!useSpools || res.spools === null)
return {
host: "tetr.io",
endpoint: res.endpoint.replace("/ribbon/", ""),
token: ""
};
else {
const lowestPingSpool = await findFastestAvailableSpool(
res.spools.spools
);
return {
host: lowestPingSpool.host,
endpoint: res.endpoint.replace("/ribbon/", ""),
token: res.spools.token
};
}
}
};
};