Works now pls test
This commit is contained in:
parent
69c151162e
commit
cc8b3ed638
2 changed files with 450 additions and 446 deletions
392
src/stats.ts
392
src/stats.ts
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 };
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue