Merge branch 'main', commit 'refs/pull/66/head' of https://github.com/MathMan05/JankClient

This commit is contained in:
MathMan05 2024-09-25 23:40:54 -05:00
commit f36b8b7c50
7 changed files with 126 additions and 126 deletions

View file

@ -20,11 +20,10 @@
"compression": "^1.7.4",
"eslint-plugin-html": "^8.1.1",
"express": "^4.19.2",
"gulp-sourcemaps": "^3.0.0",
"gulp-swc": "^2.2.0",
"node-fetch": "^3.3.2",
"rimraf": "^6.0.1",
"ts-to-jsdoc": "^2.2.0",
"gulp-sourcemaps":"^3.0.0"
"ts-to-jsdoc": "^2.2.0"
},
"devDependencies": {
"@eslint/js": "^9.10.0",

View file

@ -2,19 +2,20 @@
import compression from"compression";
import express, { Request, Response }from"express";
import fs from"node:fs";
import fetch from"node-fetch";
import fs from"node:fs/promises";
import path from"node:path";
import{ observe, uptime }from"./stats.js";
import{ getApiUrls, inviteResponse }from"./utils.js";
import{ fileURLToPath }from"node:url";
const devmode = (process.env.NODE_ENV || 'development')==='development';
import process from"node:process";
const devmode = (process.env.NODE_ENV || "development") === "development";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
interface Instance {
name: string;
[key: string]: any;
name: string;
[key: string]: any;
}
const app = express();
@ -55,9 +56,9 @@ async function updateInstances(): Promise<void>{
updateInstances();
app.use("/getupdates", (_req: Request, res: Response)=>{
app.use("/getupdates", async (_req: Request, res: Response)=>{
try{
const stats = fs.statSync(path.join(__dirname, "webpage"));
const stats = await fs.stat(path.join(__dirname, "webpage"));
res.send(stats.mtimeMs.toString());
}catch(error){
console.error("Error getting updates:", error);
@ -70,7 +71,7 @@ app.use("/services/oembed", (req: Request, res: Response)=>{
});
app.use("/uptime", (req: Request, res: Response)=>{
const instanceUptime = uptime[req.query.name as string];
const instanceUptime = uptime.get(req.query.name as string);
res.send(instanceUptime);
});
@ -106,33 +107,40 @@ app.use("/", async (req: Request, res: Response)=>{
return;
}
const filePath = path.join(__dirname, "webpage", req.path);
if(fs.existsSync(filePath)){
try{
await fs.access(filePath);
if(devmode){
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
if(fs.existsSync(filePath2)){
try{
await fs.access(filePath2);
res.sendFile(filePath2);
return;
}
}catch{}
}
res.sendFile(filePath);
}else if(fs.existsSync(`${filePath}.html`)){
if(devmode){
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
if(fs.existsSync(filePath2+".html")){
res.sendFile(filePath2+".html");
return;
}catch{
try{
await fs.access(`${filePath}.html`);
if(devmode){
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
try{
await fs.access(filePath2 + ".html");
res.sendFile(filePath2 + ".html");
return;
}catch{}
}
}
res.sendFile(`${filePath}.html`);
}else{
if(req.path.startsWith("/src/webpage")){
const filePath2 = path.join(__dirname, "..", req.path);
if(fs.existsSync(`${filePath2}`)){
res.sendFile(filePath2);
return;
res.sendFile(`${filePath}.html`);
}catch{
if(req.path.startsWith("/src/webpage")){
const filePath2 = path.join(__dirname, "..", req.path);
try{
await fs.access(filePath2);
res.sendFile(filePath2);
return;
}catch{}
}
res.sendFile(path.join(__dirname, "webpage", "index.html"));
}
res.sendFile(path.join(__dirname, "webpage", "index.html"));
}
});
@ -141,4 +149,4 @@ app.listen(PORT, ()=>{
console.log(`Server running on port ${PORT}`);
});
export{ getApiUrls };
export{ getApiUrls };

View file

@ -1,8 +1,8 @@
import fs from"node:fs";
import path from"node:path";
import fetch from"node-fetch";
import{ getApiUrls }from"./utils.js";
import{ fileURLToPath }from"node:url";
import{ setTimeout, clearTimeout }from"node:timers";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@ -12,10 +12,6 @@ interface UptimeEntry {
online: boolean;
}
interface UptimeObject {
[key: string]: UptimeEntry[];
}
interface Instance {
name: string;
urls?: { api: string };
@ -28,37 +24,46 @@ interface Instance {
};
}
const uptimeObject: UptimeObject = loadUptimeObject();
const uptimeObject: Map<string, UptimeEntry[]> = loadUptimeObject();
export{ uptimeObject as uptime };
function loadUptimeObject(): UptimeObject{
function loadUptimeObject(): Map<string, UptimeEntry[]>{
const filePath = path.join(__dirname, "..", "uptime.json");
if(fs.existsSync(filePath)){
try{
return JSON.parse(fs.readFileSync(filePath, "utf8"));
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
return new Map(Object.entries(data));
}catch(error){
console.error("Error reading uptime.json:", error);
return{};
return new Map();
}
}
return{};
return new Map();
}
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
function saveUptimeObject(): void{
fs.writeFile(
path.join(__dirname, "..", "uptime.json"),
JSON.stringify(uptimeObject),
error=>{
if(error){
console.error("Error saving uptime.json:", error);
if(saveTimeout){
clearTimeout(saveTimeout);
}
saveTimeout = setTimeout(()=>{
const data = Object.fromEntries(uptimeObject);
fs.writeFile(
path.join(__dirname, "..", "uptime.json"),
JSON.stringify(data),
error=>{
if(error){
console.error("Error saving uptime.json:", error);
}
}
}
);
);
}, 5000); // Batch updates every 5 seconds
}
function removeUndefinedKey(): void{
if(uptimeObject.undefined){
delete uptimeObject.undefined;
if(uptimeObject.has("undefined")){
uptimeObject.delete("undefined");
saveUptimeObject();
}
}
@ -85,7 +90,7 @@ async function resolveInstance(
return;
}
activeInstances.add(instance.name);
await checkHealth(instance, api); // Ensure health is checked immediately
await checkHealth(instance, api);
scheduleHealthCheck(instance, api);
}catch(error){
console.error("Error resolving instance:", error);
@ -127,7 +132,6 @@ async function checkHealth(
const response = await fetch(`${api}/ping`, { method: "HEAD" });
console.log(`Checking health for ${instance.name}: ${response.status}`);
if(response.ok || tries > 3){
console.log(`Setting status for ${instance.name} to ${response.ok}`);
setStatus(instance, response.ok);
}else{
retryHealthCheck(instance, api, tries);
@ -151,7 +155,7 @@ function retryHealthCheck(
}
function updateInactiveInstances(activeInstances: Set<string>): void{
for(const key of Object.keys(uptimeObject)){
for(const key of uptimeObject.keys()){
if(!activeInstances.has(key)){
setStatus(key, false);
}
@ -159,7 +163,7 @@ function updateInactiveInstances(activeInstances: Set<string>): void{
}
function calcStats(instance: Instance): void{
const obj = uptimeObject[instance.name];
const obj = uptimeObject.get(instance.name);
if(!obj)return;
const now = Date.now();
@ -235,11 +239,11 @@ function calculateUptimeStats(
function setStatus(instance: string | Instance, status: boolean): void{
const name = typeof instance === "string" ? instance : instance.name;
let obj = uptimeObject[name];
let obj = uptimeObject.get(name);
if(!obj){
obj = [];
uptimeObject[name] = obj;
uptimeObject.set(name, obj);
}
const lastEntry = obj.at(-1);
@ -251,4 +255,4 @@ function setStatus(instance: string | Instance, status: boolean): void{
calcStats(instance);
}
}
}
}

View file

@ -1,23 +1,22 @@
import fetch from"node-fetch";
import{ Request, Response }from"express";
interface ApiUrls {
api: string;
gateway: string;
cdn: string;
wellknown: string;
api: string;
gateway: string;
cdn: string;
wellknown: string;
}
interface Invite {
guild: {
name: string;
description?: string;
icon?: string;
id: string;
};
inviter?: {
username: string;
};
guild: {
name: string;
description?: string;
icon?: string;
id: string;
};
inviter?: {
username: string;
};
}
export async function getApiUrls(url: string): Promise<ApiUrls | null>{
@ -25,15 +24,11 @@ export async function getApiUrls(url: string): Promise<ApiUrls | null>{
url += "/";
}
try{
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(
res=>res.json() as Promise<ApiUrls>
);
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(res=>res.json());
const api = info.api;
const apiUrl = new URL(api);
const policies: any = await fetch(
`${api}${
apiUrl.pathname.includes("api") ? "" : "api"
}/policies/instance/domains`
`${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`
).then(res=>res.json());
return{
api: policies.apiEndpoint,
@ -47,14 +42,11 @@ export async function getApiUrls(url: string): Promise<ApiUrls | null>{
}
}
export async function inviteResponse(
req: Request,
res: Response
): Promise<void>{
export async function inviteResponse(req: Request, res: Response): Promise<void>{
let url: URL;
if(URL.canParse(req.query.url as string)){
try{
url = new URL(req.query.url as string);
}else{
}catch{
const scheme = req.secure ? "https" : "http";
const host = `${scheme}://${req.get("Host")}`;
url = new URL(host);
@ -70,45 +62,37 @@ export async function inviteResponse(
if(!instance){
throw new Error("Instance not specified");
}
const urls = await getApiUrls(instance);
if(!urls){
throw new Error("Failed to get API URLs");
}
const invite = await fetch(`${urls.api}/invites/${code}`).then(
res=>res.json() as Promise<Invite>
);
const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise<Invite>);
const title = invite.guild.name;
const description = invite.inviter
? `${invite.inviter.username} has invited you to ${invite.guild.name}${
invite.guild.description ? `\n${invite.guild.description}` : ""
}`
: `You've been invited to ${invite.guild.name}${
invite.guild.description ? `\n${invite.guild.description}` : ""
}`;
? `${invite.inviter.username} has invited you to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}`
: `You've been invited to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}`;
const thumbnail = invite.guild.icon
? `${urls.cdn}/icons/${invite.guild.id}/${invite.guild.icon}.png`
: "";
const jsonResponse = {
res.json({
type: "link",
version: "1.0",
title,
thumbnail,
description,
};
res.json(jsonResponse);
});
}catch(error){
console.error("Error processing invite response:", error);
const jsonResponse = {
res.json({
type: "link",
version: "1.0",
title: "Jank Client",
thumbnail: "/logo.webp",
description: "A spacebar client that has DMs, replying and more",
url: url.toString(),
};
res.json(jsonResponse);
});
}
}
}

View file

@ -17,9 +17,8 @@ class Emoji{
get guild(){
if(this.owner instanceof Guild){
return this.owner;
}else{
return undefined;
}
return null;
}
get localuser(){
if(this.owner instanceof Guild){
@ -83,7 +82,7 @@ class Emoji{
array[i] = read8();
}
//console.log(array);
return new TextDecoder("utf-8").decode(array.buffer);
return new TextDecoder("utf8").decode(array.buffer as ArrayBuffer);
}
const build: { name: string; emojis: { name: string; emoji: string }[] }[] =
[];

View file

@ -11,7 +11,7 @@ offset: number
private readonly maxDist = 6000;
HTMLElements: [HTMLElement, string][] = [];
div: HTMLDivElement | null = null;
timeout: NodeJS.Timeout | null = null;
timeout: ReturnType<typeof setTimeout> | null = null;
beenloaded = false;
scrollBottom = 0;
scrollTop = 0;
@ -239,7 +239,6 @@ offset: number
try{
if(!this.div){
res(false);
return false;
}
const out = (await Promise.allSettled([
this.watchForTop(),
@ -250,11 +249,9 @@ offset: number
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
}
res(Boolean(changed));
return Boolean(changed);
}catch(e){
console.error(e);
res(false);
return false;
}finally{
setTimeout(()=>{
this.changePromise = undefined;
@ -281,9 +278,13 @@ offset: number
behavior: "smooth",
block: "center",
});
await new Promise(resolve=>setTimeout(resolve, 1000));
await new Promise(resolve=>{
setTimeout(resolve, 1000);
});
element.classList.remove("jumped");
await new Promise(resolve=>setTimeout(resolve, 100));
await new Promise(resolve=>{
setTimeout(resolve, 100);
});
element.classList.add("jumped");
}else{
element.scrollIntoView();
@ -296,7 +297,9 @@ offset: number
await this.firstElement(id);
this.updatestuff();
await this.watchForChange();
await new Promise(resolve=>setTimeout(resolve, 100));
await new Promise(resolve=>{
setTimeout(resolve, 100);
});
await this.focus(id, true);
}
}

View file

@ -199,7 +199,7 @@ function adduser(user: typeof Specialuser.prototype.json){
return user;
}
const instancein = document.getElementById("instancein") as HTMLInputElement;
let timeout: string | number | NodeJS.Timeout | undefined;
let timeout: ReturnType<typeof setTimeout> | string | number | undefined | null = null;
// let instanceinfo;
const stringURLMap = new Map<string, string>();
@ -231,8 +231,8 @@ async function getapiurls(str: string): Promise<
if(stringURLMap.size!==0){
res();
}
},100)
})
},100);
});
}
if(val){
str = val;
@ -335,7 +335,7 @@ async function checkInstance(instance?: string){
gateway: string;
login: string;
value: string;
}
};
if(instanceinfo){
instanceinfo.value = instanceValue;
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
@ -360,10 +360,12 @@ async function checkInstance(instance?: string){
if(instancein){
console.log(instancein);
instancein.addEventListener("keydown", _=>{
instancein.addEventListener("keydown", ()=>{
const verify = document.getElementById("verify");
verify!.textContent = "Waiting to check Instance";
clearTimeout(timeout);
if(timeout !== null && typeof timeout !== "string"){
clearTimeout(timeout);
}
timeout = setTimeout(()=>checkInstance(), 1000);
});
if(localStorage.getItem("instanceinfo")){
@ -410,7 +412,9 @@ async function login(username: string, password: string, captcha: string){
if(response.captcha_sitekey){
const capt = document.getElementById("h-captcha");
if(!capt!.children.length){
if(capt!.children.length){
eval("hcaptcha.reset()");
}else{
const capty = document.createElement("div");
capty.classList.add("h-captcha");
@ -419,8 +423,6 @@ async function login(username: string, password: string, captcha: string){
script.src = "https://js.hcaptcha.com/1/api.js";
capt!.append(script);
capt!.append(capty);
}else{
eval("hcaptcha.reset()");
}
}else{
console.log(response);
@ -434,6 +436,7 @@ async function login(username: string, password: string, captcha: string){
"",
"",
function(this: HTMLInputElement){
// eslint-disable-next-line no-invalid-this
onetimecode = this.value;
},
],
@ -453,18 +456,18 @@ async function login(username: string, password: string, captcha: string){
}),
})
.then(r=>r.json())
.then(response=>{
if(response.message){
alert(response.message);
.then(res=>{
if(res.message){
alert(res.message);
}else{
console.warn(response);
if(!response.token)return;
console.warn(res);
if(!res.token)return;
adduser({
serverurls: JSON.parse(
localStorage.getItem("instanceinfo")!
),
email: username,
token: response.token,
token: res.token,
}).username = username;
const redir = new URLSearchParams(
window.location.search
@ -579,7 +582,7 @@ export function getInstances(){
}
fetch("/instances.json")
.then(_=>_.json())
.then(res=>res.json())
.then(
(json: {
name: string;