Works now pls test

This commit is contained in:
Scott Gould 2024-09-19 09:03:29 -04:00
parent 69c151162e
commit cc8b3ed638
No known key found for this signature in database
2 changed files with 450 additions and 446 deletions

View file

@ -8,244 +8,248 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
interface UptimeEntry { interface UptimeEntry {
time: number; time: number;
online: boolean; online: boolean;
} }
interface UptimeObject { interface UptimeObject {
[key: string]: UptimeEntry[]; [key: string]: UptimeEntry[];
} }
interface Instance { interface Instance {
name: string; name: string;
urls?: { api: string }; urls?: { api: string };
url?: string; url?: string;
online?: boolean; online?: boolean;
uptime?: { uptime?: {
daytime: number; daytime: number;
weektime: number; weektime: number;
alltime: number; alltime: number;
}; };
} }
let uptimeObject: UptimeObject = loadUptimeObject(); let uptimeObject: UptimeObject = loadUptimeObject();
export { uptimeObject as uptime }; export { uptimeObject as uptime };
function loadUptimeObject(): UptimeObject { function loadUptimeObject(): UptimeObject {
const filePath = path.join(__dirname, "..", "uptime.json"); const filePath = path.join(__dirname, "..", "uptime.json");
if (fs.existsSync(filePath)) { if (fs.existsSync(filePath)) {
try { try {
return JSON.parse(fs.readFileSync(filePath, "utf8")); return JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch (error) { } catch (error) {
console.error("Error reading uptime.json:", error); console.error("Error reading uptime.json:", error);
return {}; return {};
} }
} }
return {}; return {};
} }
function saveUptimeObject(): void { function saveUptimeObject(): void {
fs.writeFile( fs.writeFile(
`${__dirname}/uptime.json`, path.join(__dirname, "..", "uptime.json"),
JSON.stringify(uptimeObject), JSON.stringify(uptimeObject),
(error) => { (error) => {
if (error) { if (error) {
console.error("Error saving uptime.json:", error); console.error("Error saving uptime.json:", error);
} }
} }
); );
} }
function removeUndefinedKey(): void { function removeUndefinedKey(): void {
if (uptimeObject.undefined) { if (uptimeObject.undefined) {
delete uptimeObject.undefined; delete uptimeObject.undefined;
saveUptimeObject(); saveUptimeObject();
} }
} }
removeUndefinedKey(); removeUndefinedKey();
export async function observe(instances: Instance[]): Promise<void> { export async function observe(instances: Instance[]): Promise<void> {
const activeInstances = new Set<string>(); const activeInstances = new Set<string>();
const instancePromises = instances.map((instance) => const instancePromises = instances.map((instance) =>
resolveInstance(instance, activeInstances) resolveInstance(instance, activeInstances)
); );
await Promise.allSettled(instancePromises); await Promise.allSettled(instancePromises);
updateInactiveInstances(activeInstances); updateInactiveInstances(activeInstances);
} }
async function resolveInstance( async function resolveInstance(
instance: Instance, instance: Instance,
activeInstances: Set<string> activeInstances: Set<string>
): Promise<void> { ): Promise<void> {
try { try {
calcStats(instance); calcStats(instance);
const api = await getApiUrl(instance); const api = await getApiUrl(instance);
if (!api) { if (!api) {
handleUnresolvedApi(instance); handleUnresolvedApi(instance);
return; return;
} }
activeInstances.add(instance.name); activeInstances.add(instance.name);
scheduleHealthCheck(instance, api); await checkHealth(instance, api); // Ensure health is checked immediately
} catch (error) { scheduleHealthCheck(instance, api);
console.error("Error resolving instance:", error); } catch (error) {
} console.error("Error resolving instance:", error);
} }
}
async function getApiUrl(instance: Instance): Promise<string | null> { async function getApiUrl(instance: Instance): Promise<string | null> {
if (instance.urls) { if (instance.urls) {
return instance.urls.api; return instance.urls.api;
} }
if (instance.url) { if (instance.url) {
const urls = await getApiUrls(instance.url); const urls = await getApiUrls(instance.url);
return urls ? urls.api : null; return urls ? urls.api : null;
} }
return null; return null;
} }
function handleUnresolvedApi(instance: Instance): void { function handleUnresolvedApi(instance: Instance): void {
setStatus(instance, false); setStatus(instance, false);
console.warn(`${instance.name} does not resolve api URL`, instance); console.warn(`${instance.name} does not resolve api URL`, instance);
setTimeout(() => resolveInstance(instance, new Set()), 1000 * 60 * 30); setTimeout(() => resolveInstance(instance, new Set()), 1000 * 60 * 30);
} }
function scheduleHealthCheck(instance: Instance, api: string): void { function scheduleHealthCheck(instance: Instance, api: string): void {
const checkInterval = 1000 * 60 * 30; const checkInterval = 1000 * 60 * 30;
const initialDelay = Math.random() * 1000 * 60 * 10; const initialDelay = Math.random() * 1000 * 60 * 10;
setTimeout(() => { setTimeout(() => {
checkHealth(instance, api); checkHealth(instance, api);
setInterval(() => checkHealth(instance, api), checkInterval); setInterval(() => checkHealth(instance, api), checkInterval);
}, initialDelay); }, initialDelay);
} }
async function checkHealth( async function checkHealth(
instance: Instance, instance: Instance,
api: string, api: string,
tries = 0 tries = 0
): Promise<void> { ): Promise<void> {
try { try {
const response = await fetch(`${api}ping`, { method: "HEAD" }); const response = await fetch(`${api}/ping`, { method: "HEAD" });
if (response.ok || tries > 3) { console.log(`Checking health for ${instance.name}: ${response.status}`);
setStatus(instance, response.ok); if (response.ok || tries > 3) {
} else { console.log(`Setting status for ${instance.name} to ${response.ok}`);
retryHealthCheck(instance, api, tries); setStatus(instance, response.ok);
} } else {
} catch (error) { retryHealthCheck(instance, api, tries);
console.error("Error checking health:", error); }
if (tries > 3) { } catch (error) {
setStatus(instance, false); console.error(`Error checking health for ${instance.name}:`, error);
} else { if (tries > 3) {
retryHealthCheck(instance, api, tries); setStatus(instance, false);
} } else {
} retryHealthCheck(instance, api, tries);
} }
}
}
function retryHealthCheck( function retryHealthCheck(
instance: Instance, instance: Instance,
api: string, api: string,
tries: number tries: number
): void { ): void {
setTimeout(() => checkHealth(instance, api, tries + 1), 30000); setTimeout(() => checkHealth(instance, api, tries + 1), 30000);
} }
function updateInactiveInstances(activeInstances: Set<string>): void { function updateInactiveInstances(activeInstances: Set<string>): void {
for (const key of Object.keys(uptimeObject)) { for (const key of Object.keys(uptimeObject)) {
if (!activeInstances.has(key)) { if (!activeInstances.has(key)) {
setStatus(key, false); setStatus(key, false);
} }
} }
} }
function calcStats(instance: Instance): void { function calcStats(instance: Instance): void {
const obj = uptimeObject[instance.name]; const obj = uptimeObject[instance.name];
if (!obj) return; if (!obj) return;
const now = Date.now(); const now = Date.now();
const day = now - 1000 * 60 * 60 * 24; const day = now - 1000 * 60 * 60 * 24;
const week = now - 1000 * 60 * 60 * 24 * 7; const week = now - 1000 * 60 * 60 * 24 * 7;
let totalTimePassed = 0; let totalTimePassed = 0;
let alltime = 0; let alltime = 0;
let daytime = 0; let daytime = 0;
let weektime = 0; let weektime = 0;
let online = false; let online = false;
for (let i = 0; i < obj.length; i++) { for (let i = 0; i < obj.length; i++) {
const entry = obj[i]; const entry = obj[i];
online = entry.online; online = entry.online;
const stamp = entry.time; const stamp = entry.time;
const nextStamp = obj[i + 1]?.time || now; const nextStamp = obj[i + 1]?.time || now;
const timePassed = nextStamp - stamp; const timePassed = nextStamp - stamp;
totalTimePassed += timePassed; totalTimePassed += timePassed;
alltime += Number(online) * timePassed; alltime += Number(online) * timePassed;
if (stamp + timePassed > week) { if (stamp + timePassed > week) {
const weekTimePassed = Math.min(timePassed, nextStamp - week); const weekTimePassed = Math.min(timePassed, nextStamp - week);
weektime += Number(online) * weekTimePassed; weektime += Number(online) * weekTimePassed;
if (stamp + timePassed > day) { if (stamp + timePassed > day) {
const dayTimePassed = Math.min(weekTimePassed, nextStamp - day); const dayTimePassed = Math.min(weekTimePassed, nextStamp - day);
daytime += Number(online) * dayTimePassed; daytime += Number(online) * dayTimePassed;
} }
} }
} }
instance.online = online; instance.online = online;
instance.uptime = calculateUptimeStats( instance.uptime = calculateUptimeStats(
totalTimePassed, totalTimePassed,
alltime, alltime,
daytime, daytime,
weektime, weektime,
online online
); );
} }
function calculateUptimeStats( function calculateUptimeStats(
totalTimePassed: number, totalTimePassed: number,
alltime: number, alltime: number,
daytime: number, daytime: number,
weektime: number, weektime: number,
online: boolean online: boolean
): { daytime: number; weektime: number; alltime: number } { ): { daytime: number; weektime: number; alltime: number } {
const dayInMs = 1000 * 60 * 60 * 24; const dayInMs = 1000 * 60 * 60 * 24;
const weekInMs = dayInMs * 7; const weekInMs = dayInMs * 7;
alltime /= totalTimePassed; alltime /= totalTimePassed;
if (totalTimePassed > dayInMs) { if (totalTimePassed > dayInMs) {
daytime = daytime || (online ? dayInMs : 0); daytime = daytime || (online ? dayInMs : 0);
daytime /= dayInMs; daytime /= dayInMs;
if (totalTimePassed > weekInMs) { if (totalTimePassed > weekInMs) {
weektime = weektime || (online ? weekInMs : 0); weektime = weektime || (online ? weekInMs : 0);
weektime /= weekInMs; weektime /= weekInMs;
} else { } else {
weektime = alltime; weektime = alltime;
} }
} else { } else {
weektime = alltime; weektime = alltime;
daytime = alltime; daytime = alltime;
} }
return { daytime, weektime, alltime }; return { daytime, weektime, alltime };
} }
function setStatus(instance: string | Instance, status: boolean): void { function setStatus(instance: string | Instance, status: boolean): void {
const name = typeof instance === "string" ? instance : instance.name; const name = typeof instance === "string" ? instance : instance.name;
let obj = uptimeObject[name]; let obj = uptimeObject[name];
if (!obj) { if (!obj) {
obj = []; obj = [];
uptimeObject[name] = obj; uptimeObject[name] = obj;
} }
if (obj.at(-1)?.online !== status) { const lastEntry = obj.at(-1);
obj.push({ time: Date.now(), online: status }); if (!lastEntry || lastEntry.online !== status) {
saveUptimeObject(); obj.push({ time: Date.now(), online: status });
} saveUptimeObject();
if (typeof instance !== "string") { if (typeof instance !== "string") {
calcStats(instance); calcStats(instance);
} }
} }
}

View file

@ -3,257 +3,257 @@ import { Guild } from "./guild.js";
import { Localuser } from "./localuser.js"; import { Localuser } from "./localuser.js";
class Emoji { class Emoji {
static emojis: { static emojis: {
name: string; name: string;
emojis: { emojis: {
name: string; name: string;
emoji: string; emoji: string;
}[]; }[];
}[]; }[];
name: string; name: string;
id: string; id: string;
animated: boolean; animated: boolean;
owner: Guild | Localuser; owner: Guild | Localuser;
get guild() { get guild() {
if (this.owner instanceof Guild) { if (this.owner instanceof Guild) {
return this.owner; return this.owner;
} }
return; return;
} }
get localuser() { get localuser() {
if (this.owner instanceof Guild) { if (this.owner instanceof Guild) {
return this.owner.localuser; return this.owner.localuser;
} else { } else {
return this.owner; return this.owner;
} }
} }
get info() { get info() {
return this.owner.info; return this.owner.info;
} }
constructor( constructor(
json: { name: string; id: string; animated: boolean }, json: { name: string; id: string; animated: boolean },
owner: Guild | Localuser owner: Guild | Localuser
) { ) {
this.name = json.name; this.name = json.name;
this.id = json.id; this.id = json.id;
this.animated = json.animated; this.animated = json.animated;
this.owner = owner; this.owner = owner;
} }
getHTML(bigemoji: boolean = false) { getHTML(bigemoji: boolean = false) {
const emojiElem = document.createElement("img"); const emojiElem = document.createElement("img");
emojiElem.classList.add("md-emoji"); emojiElem.classList.add("md-emoji");
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji"); emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
emojiElem.crossOrigin = "anonymous"; emojiElem.crossOrigin = "anonymous";
emojiElem.src = emojiElem.src =
this.info.cdn + this.info.cdn +
"/emojis/" + "/emojis/" +
this.id + this.id +
"." + "." +
(this.animated ? "gif" : "png") + (this.animated ? "gif" : "png") +
"?size=32"; "?size=32";
emojiElem.alt = this.name; emojiElem.alt = this.name;
emojiElem.loading = "lazy"; emojiElem.loading = "lazy";
return emojiElem; return emojiElem;
}
static decodeEmojiList(buffer: ArrayBuffer) {
const view = new DataView(buffer, 0);
let i = 0;
function read16() {
const int = view.getUint16(i);
i += 2;
return int;
}
function read8() {
const int = view.getUint8(i);
i += 1;
return int;
}
function readString8() {
return readStringNo(read8());
}
function readString16() {
return readStringNo(read16());
}
function readStringNo(length: number) {
const array = new Uint8Array(length);
for (let i = 0; i < length; i++) {
array[i] = read8();
}
//console.log(array);
return new TextDecoder("utf-8").decode(array.buffer);
}
const build: { name: string; emojis: { name: string; emoji: string }[] }[] =
[];
let cats = read16();
for (; cats !== 0; cats--) {
const name = readString16();
const emojis: {
name: string;
skin_tone_support: boolean;
emoji: string;
}[] = [];
let emojinumber = read16();
for (; emojinumber !== 0; emojinumber--) {
//console.log(emojis);
const name = readString8();
const len = read8();
const skin_tone_support = len > 127;
const emoji = readStringNo(len - Number(skin_tone_support) * 128);
emojis.push({
name,
skin_tone_support,
emoji,
});
}
build.push({
name,
emojis,
});
}
this.emojis = build;
console.log(build);
}
static grabEmoji() {
fetch("/emoji.bin")
.then((e) => {
return e.arrayBuffer();
})
.then((e) => {
Emoji.decodeEmojiList(e);
});
}
static async emojiPicker(
x: number,
y: number,
localuser: Localuser
): Promise<Emoji | string> {
let res: (r: Emoji | string) => void;
const promise: Promise<Emoji | string> = new Promise((r) => {
res = r;
});
const menu = document.createElement("div");
menu.classList.add("flexttb", "emojiPicker");
menu.style.top = y + "px";
menu.style.left = x + "px";
const title = document.createElement("h2");
title.textContent = Emoji.emojis[0].name;
title.classList.add("emojiTitle");
menu.append(title);
const selection = document.createElement("div");
selection.classList.add("flexltr", "dontshrink", "emojirow");
const body = document.createElement("div");
body.classList.add("emojiBody");
let isFirst = true;
localuser.guilds
.filter((guild) => guild.id != "@me" && guild.emojis.length > 0)
.forEach((guild) => {
const select = document.createElement("div");
select.classList.add("emojiSelect");
if (guild.properties.icon) {
const img = document.createElement("img");
img.classList.add("pfp", "servericon", "emoji-server");
img.crossOrigin = "anonymous";
img.src =
localuser.info.cdn +
"/icons/" +
guild.properties.id +
"/" +
guild.properties.icon +
".png?size=48";
img.alt = "Server: " + guild.properties.name;
select.appendChild(img);
} else {
const div = document.createElement("span");
div.textContent = guild.properties.name
.replace(/'s /g, " ")
.replace(/\w+/g, (word) => word[0])
.replace(/\s/g, "");
select.append(div);
}
selection.append(select);
const clickEvent = () => {
title.textContent = guild.properties.name;
body.innerHTML = "";
for (const emojit of guild.emojis) {
const emojiElem = document.createElement("div");
emojiElem.classList.add("emojiSelect");
const emojiClass = new Emoji(
{
id: emojit.id as string,
name: emojit.name,
animated: emojit.animated as boolean,
},
localuser
);
emojiElem.append(emojiClass.getHTML());
body.append(emojiElem);
emojiElem.addEventListener("click", () => {
res(emojiClass);
if (Contextmenu.currentmenu !== "") {
Contextmenu.currentmenu.remove();
}
});
}
};
select.addEventListener("click", clickEvent);
if (isFirst) {
clickEvent();
isFirst = false;
}
});
setTimeout(() => {
if (Contextmenu.currentmenu != "") {
Contextmenu.currentmenu.remove();
}
document.body.append(menu);
Contextmenu.currentmenu = menu;
Contextmenu.keepOnScreen(menu);
}, 10);
let i = 0;
for (const thing of Emoji.emojis) {
const select = document.createElement("div");
select.textContent = thing.emojis[0].emoji;
select.classList.add("emojiSelect");
selection.append(select);
const clickEvent = () => {
title.textContent = thing.name;
body.innerHTML = "";
for (const emojit of thing.emojis) {
const emoji = document.createElement("div");
emoji.classList.add("emojiSelect");
emoji.textContent = emojit.emoji;
body.append(emoji);
emoji.onclick = (_) => {
res(emojit.emoji);
if (Contextmenu.currentmenu !== "") {
Contextmenu.currentmenu.remove();
}
};
}
};
select.onclick = clickEvent;
if (i === 0) {
clickEvent();
}
i++;
}
menu.append(selection);
menu.append(body);
return promise;
}
} }
static decodeEmojiList(buffer: ArrayBuffer) { Emoji.grabEmoji();
const view = new DataView(buffer, 0); export { Emoji };
let i = 0;
function read16() {
const int = view.getUint16(i);
i += 2;
return int;
}
function read8() {
const int = view.getUint8(i);
i += 1;
return int;
}
function readString8() {
return readStringNo(read8());
}
function readString16() {
return readStringNo(read16());
}
function readStringNo(length: number) {
const array = new Uint8Array(length);
for (let i = 0; i < length; i++) {
array[i] = read8();
}
//console.log(array);
return new TextDecoder("utf-8").decode(array.buffer);
}
const build: { name: string; emojis: { name: string; emoji: string }[] }[] =
[];
let cats = read16();
for (; cats !== 0; cats--) {
const name = readString16();
const emojis: {
name: string;
skin_tone_support: boolean;
emoji: string;
}[] = [];
let emojinumber = read16();
for (; emojinumber !== 0; emojinumber--) {
//console.log(emojis);
const name = readString8();
const len = read8();
const skin_tone_support = len > 127;
const emoji = readStringNo(len - Number(skin_tone_support) * 128);
emojis.push({
name,
skin_tone_support,
emoji,
});
}
build.push({
name,
emojis,
});
}
this.emojis = build;
console.log(build);
}
static grabEmoji() {
fetch("/emoji.bin")
.then((e) => {
return e.arrayBuffer();
})
.then((e) => {
Emoji.decodeEmojiList(e);
});
}
static async emojiPicker(
x: number,
y: number,
localuser: Localuser
): Promise<Emoji | string> {
let res: (r: Emoji | string) => void;
const promise: Promise<Emoji | string> = new Promise((r) => {
res = r;
});
const menu = document.createElement("div");
menu.classList.add("flexttb", "emojiPicker");
menu.style.top = y + "px";
menu.style.left = x + "px";
const title = document.createElement("h2");
title.textContent = Emoji.emojis[0].name;
title.classList.add("emojiTitle");
menu.append(title);
const selection = document.createElement("div");
selection.classList.add("flexltr", "dontshrink", "emojirow");
const body = document.createElement("div");
body.classList.add("emojiBody");
let isFirst = true;
localuser.guilds
.filter((guild) => guild.id != "@me" && guild.emojis.length > 0)
.forEach((guild) => {
const select = document.createElement("div");
select.classList.add("emojiSelect");
if (guild.properties.icon) {
const img = document.createElement("img");
img.classList.add("pfp", "servericon", "emoji-server");
img.crossOrigin = "anonymous";
img.src =
localuser.info.cdn +
"/icons/" +
guild.properties.id +
"/" +
guild.properties.icon +
".png?size=48";
img.alt = "Server: " + guild.properties.name;
select.appendChild(img);
} else {
const div = document.createElement("span");
div.textContent = guild.properties.name
.replace(/'s /g, " ")
.replace(/\w+/g, (word) => word[0])
.replace(/\s/g, "");
select.append(div);
}
selection.append(select);
const clickEvent = () => {
title.textContent = guild.properties.name;
body.innerHTML = "";
for (const emojit of guild.emojis) {
const emojiElem = document.createElement("div");
emojiElem.classList.add("emojiSelect");
const emojiClass = new Emoji(
{
id: emojit.id as string,
name: emojit.name,
animated: emojit.animated as boolean,
},
localuser
);
emojiElem.append(emojiClass.getHTML());
body.append(emojiElem);
emojiElem.addEventListener("click", () => {
res(emojiClass);
if (Contextmenu.currentmenu !== "") {
Contextmenu.currentmenu.remove();
}
});
}
};
select.addEventListener("click", clickEvent);
if (isFirst) {
clickEvent();
isFirst = false;
}
});
setTimeout(() => {
if (Contextmenu.currentmenu != "") {
Contextmenu.currentmenu.remove();
}
document.body.append(menu);
Contextmenu.currentmenu = menu;
Contextmenu.keepOnScreen(menu);
}, 10);
let i = 0;
for (const thing of Emoji.emojis) {
const select = document.createElement("div");
select.textContent = thing.emojis[0].emoji;
select.classList.add("emojiSelect");
selection.append(select);
const clickEvent = () => {
title.textContent = thing.name;
body.innerHTML = "";
for (const emojit of thing.emojis) {
const emoji = document.createElement("div");
emoji.classList.add("emojiSelect");
emoji.textContent = emojit.emoji;
body.append(emoji);
emoji.onclick = (_) => {
res(emojit.emoji);
if (Contextmenu.currentmenu !== "") {
Contextmenu.currentmenu.remove();
}
};
}
};
select.onclick = clickEvent;
if (i === 0) {
clickEvent();
}
i++;
}
menu.append(selection);
menu.append(body);
return promise;
}
}
Emoji.grabEmoji();
export { Emoji };