diff --git a/bun.lock b/bun.lock index 0d45d2e..39ccf8a 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,9 @@ "": { "name": "ipv4.army", "dependencies": { + "@speed-highlight/core": "^1.2.7", "microlight": "^0.0.7", + "reconnecting-websocket": "^4.4.0", "tsx-dom": "^3.1.0", }, "devDependencies": { @@ -35,14 +37,18 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], - "@types/bun": ["@types/bun@1.2.10", "", { "dependencies": { "bun-types": "1.2.10" } }, "sha512-eilv6WFM3M0c9ztJt7/g80BDusK98z/FrFwseZgT4bXCq2vPhXD4z8R3oddmAn+R/Nmz9vBn4kweJKmGTZj+lg=="], + "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="], + + "@types/bun": ["@types/bun@1.2.12", "", { "dependencies": { "bun-types": "1.2.12" } }, "sha512-lY/GQTXDGsolT/TiH72p1tuyUORuRrdV7VwOTOjDOt8uTBJQOJc5zz3ufwwDl0VBaoxotSk4LdP0hhjLJ6ypIQ=="], "@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], - "bun-types": ["bun-types@1.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-b5ITZMnVdf3m1gMvJHG+gIfeJHiQPJak0f7925Hxu6ZN5VKA8AGy4GZ4lM+Xkn6jtWxg5S3ldWvfmXdvnkp3GQ=="], + "bun-types": ["bun-types@1.2.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-tvWMx5vPqbRXgE8WUZI94iS1xAYs8bkqESR9cxBB1Wi+urvfTrF1uzuDgBHFAdO0+d2lmsbG3HmeKMvUyj6pWA=="], "microlight": ["microlight@0.0.7", "", {}, "sha512-kigwsJYoy4mAMkGZpS839/KZ5WWQQm4TzD+eIjR5leS5H0j+EhExvK0Z2Or2ewkBR/t7/AHHhxRyeXi1kurG0g=="], + "reconnecting-websocket": ["reconnecting-websocket@4.4.0", "", {}, "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng=="], + "tsx-dom": ["tsx-dom@3.1.0", "", { "dependencies": { "tsx-dom-types": "2.1.0" } }, "sha512-PGN7iL6zNC4Jj7bA1groSIz5mFB3Rr+SeoywZk2g4+c9uV8wwzCf+5tFQ8SyZxQIBHech3ueB0KxV3OFieqhOA=="], "tsx-dom-types": ["tsx-dom-types@2.1.0", "", {}, "sha512-pZaMTrMRNom+D1b82K+1cWVMuogXrD/ANI42UYxilw27tF+tDCgj7GrD1XLmCxbHPDO2zxfmFuaz04KIEfWydQ=="], diff --git a/hyperate.json b/hyperate.json new file mode 100644 index 0000000..369182b --- /dev/null +++ b/hyperate.json @@ -0,0 +1,4 @@ +{ + "5338": 18153, + "null": 893 +} \ No newline at end of file diff --git a/index.ts b/index.ts index f99d211..aba1cb2 100644 --- a/index.ts +++ b/index.ts @@ -1,8 +1,9 @@ -import { build, serve, gzipSync, file, type BunRequest, gc } from "bun"; +import { build, serve, gzipSync, file, gc } from "bun"; + +import Backend from "./src/back" import pkg from "./package.json"; -const development = process.env.NODE_ENV === "development"; let heartrate = 0; let lanyard = {}; @@ -11,162 +12,75 @@ require("node:fs/promises").rm("./dist", { recursive: true, force: true }).catch // ignore }); -const buildWeb = async () => { - return await build({ - entrypoints: ['./src/index.html'], - outdir: './dist', - minify: !development, - sourcemap: (development ? "inline" : "none"), - splitting: true, - publicPath: "/assets/", - loader: { - ".woff2": "file" - }, - }) +if (!Backend.development) { + await Backend.build() } -if (!development) { - await buildWeb() -} - -const webserver = serve({ +const server = serve({ routes: { - "/": async () => { - if (development) { - await buildWeb() + "/": async (req: Bun.BunRequest, server: Bun.Server) => { + await Backend.postAnalytics(req, server); + + if (Backend.development) { + await Backend.build() } - return new Response(gzipSync(await file("./dist/index.html").arrayBuffer()), { - headers: { - "Content-Type": "text/html", - "Cache-Control": "no-cache", - "Content-Encoding": "gzip", - } - }) + + return await Backend.Responses.file(file("./dist/index.html")); }, - "/assets/:file": async (req: BunRequest<"/assets/:file">) => { - const reqFile = file(`./dist/${req.params.file}`) - return new Response(gzipSync(await reqFile.arrayBuffer()), { - headers: { - "Content-Type": reqFile.type, - "Cache-Control": "public, max-age=31536000", - "Content-Encoding": "gzip", - } - }) + "/assets/:file": async (req: Bun.BunRequest<"/assets/:file">) => { + return await Backend.Responses.file(file(`./dist/${req.params.file}`)); }, - "/public/:file": async (req: BunRequest<"/public/:file">) => { - const reqFile = file(`./public/${req.params.file}`) - let fileRes = await reqFile.text() - - fileRes = fileRes.replace("{{LANYARD}}", `${JSON.stringify({ example: "lanyard data" })}`) - fileRes = fileRes.replace("{{HYPERATE}}", `${JSON.stringify({ example: "hyperate data" })}`) - - return new Response(gzipSync(fileRes), { - headers: { - "Content-Type": reqFile.type, - "Cache-Control": "public, max-age=31536000", - "Content-Encoding": "gzip", - } - }) - }, - - "/public/font/:file": async (req: BunRequest<"/public/font/:file">) => { - const reqFile = file(`./public/font/${req.params.file}`) - return new Response(gzipSync(await reqFile.arrayBuffer()), { - headers: { - "Content-Type": reqFile.type, - "Cache-Control": "public, max-age=31536000", - "Content-Encoding": "gzip", - } - }) + "/public/:file": async (req: Bun.BunRequest<"/public/:file">) => { + return await Backend.Responses.file(file(`./public/${req.params.file}`)); }, "/api/server": () => { const string = JSON.stringify(process) - const json = JSON.parse(string) + const data = JSON.parse(string) // clear possibly data that could be sensitive - json.argv = {} - json.debugPort = 0 - json.env = {} - json.execArgv = [] - json.execPath = "" - json.stderr = {} - json.stdin = {} - json.stdout = {} - json.title = "" + data.env = {} - json.availableMemory = process.availableMemory() - json.constrainedMemory = process.constrainedMemory() - json.cpuUsage = process.cpuUsage() - json.memoryUsage = process.memoryUsage() - json.uptime = process.uptime() - json.package = pkg + data.availableMemory = process.availableMemory() + data.constrainedMemory = process.constrainedMemory() + data.cpuUsage = process.cpuUsage() + data.memoryUsage = process.memoryUsage() + data.uptime = process.uptime() + data.package = pkg - return new Response(gzipSync(JSON.stringify({ data: json })), { - headers: { - "Content-Type": "application/json", - "Cache-Control": "no-cache", - "Content-Encoding": "gzip", - } - }) + return Backend.Responses.json({ data }); }, "/api/health": () => { - return new Response(gzipSync(JSON.stringify({ data: "ok" })), { - headers: { - "Content-Type": "application/json", - "Cache-Control": "no-cache", - "Content-Encoding": "gzip", - } - }) + return Backend.Responses.ok(); }, - "/api/ws": (req, server) => { + "/api/ws": async (req, server) => { if (server.upgrade(req)) { return; } - return new Response("Upgrade failed", { status: 500 }); + await Backend.postAnalytics(req, server); + return Response.redirect("/"); }, - "/api/gc": async (req, server) => { + "/api/gc": async () => { gc(true) - return new Response(gzipSync(JSON.stringify({ data: "triggered" })), { - headers: { - "Content-Type": "application/json", - "Cache-Control": "no-cache", - "Content-Encoding": "gzip", - } - }) - }, - "/api/script.js": async () => { - const req = await fetch("https://plausible.creations.works/js/script.js") - const script = await req.text() - - return new Response(gzipSync(script), { - headers: { - "Content-Type": "application/javascript", - "Content-Encoding": "gzip", - "Cache-Control": "public, max-age=31536000", - } - }) - }, - "/api/script": async (req) => { - const request = new Request(req); - request.headers.delete('cookie'); - return await fetch("https://plausible.creations.works/api/event", request); + return Backend.Responses.ok(); }, "/api/headers": async (req) => { - return new Response(gzipSync(JSON.stringify({ headers: req.headers })), { - headers: { - "Content-Type": "application/json", - "Content-Encoding": "gzip", - "Cache-Control": "no-cache", - } - }) + return Backend.Responses.json({ ...req.headers.toJSON() }); } }, + + fetch: async (request, server) => { + await Backend.postAnalytics(request, server); + + return Response.redirect("/"); + }, + websocket: { + idleTimeout: 1, open: async (ws) => { ws.subscribe("lanyard"); ws.send(JSON.stringify({ type: "lanyard", data: lanyard }), true); @@ -178,91 +92,16 @@ const webserver = serve({ ws.send(JSON.stringify({ type: "echo", data: message }), true) } }, - development, + development: Backend.development, port: 2056 }); -const lanyardSocket = new WebSocket("wss://lanyard.creations.works/socket"); +new Backend.Sockets.Hyperate((data) => { + heartrate = data; + server.publish("hyperate", JSON.stringify({ type: "hyperate", data: { hr: heartrate } }), true); +}) -const setLanyard = (data: object) => { +new Backend.Sockets.Lanyard((data) => { lanyard = data; - - return webserver.publish("lanyard", JSON.stringify({ type: "lanyard", data }), true); -} - -lanyardSocket.onmessage = ({ data }) => { - data = JSON.parse(data); - - switch (data.op) { - case 0: { - setLanyard(data.d) - break; - } - case 1: { - lanyardSocket.send(JSON.stringify({ - op: 2, - d: { - subscribe_to_id: "1273447359417942128" - } - })) - break; - } - } -} - -const hyperate = new WebSocket( - "wss://app.hyperate.io/socket/websocket?token=wv39nM6iyrNJulvpmMQrimYPIXy2dVrYRjkuHpbRapKT2VSh65ngDGHdCdCtmEN9", -); - -let hrTimeout: ReturnType; - -const setHeartrate = async (hr: number) => { - heartrate = hr; - - return webserver.publish("hyperate", JSON.stringify({ type: "hyperate", data: { hr } }), true); -} - -const setHrInterval = () => { - hrTimeout = setTimeout(() => { - setHeartrate(0); - }, 6000); -}; - -hyperate.onopen = () => { - hyperate.send( - JSON.stringify({ - topic: "hr:0BCA", - event: "phx_join", - payload: {}, - ref: 0, - }), - ); - - setInterval(() => { - hyperate.send( - JSON.stringify({ - topic: "phoenix", - event: "heartbeat", - payload: {}, - ref: 0, - }), - ); - }, 10000); - - return setHrInterval(); -}; - -hyperate.onmessage = ({ data }) => { - const { event, payload } = JSON.parse(data); - switch (event) { - case "hr_update": { - clearTimeout(hrTimeout); - setHrInterval(); - setHeartrate(payload.hr); - break; - } - default: { - break; - } - } -}; \ No newline at end of file + server.publish("lanyard", JSON.stringify({ type: "lanyard", data: lanyard }), true); +}); \ No newline at end of file diff --git a/package.json b/package.json index a5b03ce..920a1b7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "private": true, "type": "module", "dependencies": { + "@speed-highlight/core": "^1.2.7", "microlight": "^0.0.7", + "reconnecting-websocket": "^4.4.0", "tsx-dom": "^3.1.0" } } \ No newline at end of file diff --git a/public/font/Black.woff2 b/public/font/Black.woff2 deleted file mode 100644 index 21c9327..0000000 Binary files a/public/font/Black.woff2 and /dev/null differ diff --git a/public/font/BlackItalic.woff2 b/public/font/BlackItalic.woff2 deleted file mode 100644 index c2227f2..0000000 Binary files a/public/font/BlackItalic.woff2 and /dev/null differ diff --git a/public/font/Bold.woff2 b/public/font/Bold.woff2 deleted file mode 100644 index e9eff3d..0000000 Binary files a/public/font/Bold.woff2 and /dev/null differ diff --git a/public/font/BoldItalic.woff2 b/public/font/BoldItalic.woff2 deleted file mode 100644 index e79c96b..0000000 Binary files a/public/font/BoldItalic.woff2 and /dev/null differ diff --git a/public/font/Book.woff2 b/public/font/Book.woff2 deleted file mode 100644 index 6830dc2..0000000 Binary files a/public/font/Book.woff2 and /dev/null differ diff --git a/public/font/BookItalic.woff2 b/public/font/BookItalic.woff2 deleted file mode 100644 index 4b56559..0000000 Binary files a/public/font/BookItalic.woff2 and /dev/null differ diff --git a/public/font/Medium.woff2 b/public/font/Medium.woff2 deleted file mode 100644 index edc03eb..0000000 Binary files a/public/font/Medium.woff2 and /dev/null differ diff --git a/public/font/MediumItalic.woff2 b/public/font/MediumItalic.woff2 deleted file mode 100644 index caeb737..0000000 Binary files a/public/font/MediumItalic.woff2 and /dev/null differ diff --git a/public/font/rename.ts b/public/font/rename.ts deleted file mode 100644 index 5d4bd89..0000000 --- a/public/font/rename.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Glob } from "bun"; - -const woff2 = new Glob("./*.woff2"); - -for await (const file of woff2.scan(".")) { - const font = Bun.file(file); - - const name = font.name?.split("-")[1]; - - await Bun.write(`./${name}`, await font.arrayBuffer()); - console.log(`Renamed ${file} to ${name}`); - - await font.delete(); - console.log(`Deleted original font file: ${file}`); -} - -console.log("Done") \ No newline at end of file diff --git a/src/back/Sockets/Hyperate.ts b/src/back/Sockets/Hyperate.ts new file mode 100644 index 0000000..0badb0c --- /dev/null +++ b/src/back/Sockets/Hyperate.ts @@ -0,0 +1,88 @@ +import ReconnectingWebSocket from 'reconnecting-websocket'; + +export default class { + private _socket: ReconnectingWebSocket; + private _keepAlive: NodeJS.Timeout | null; + private _interval: NodeJS.Timeout | null; + private _callback: (data: number) => void; + + constructor(callback: (data: number) => void) { + this._socket = new ReconnectingWebSocket("wss://app.hyperate.io/socket/websocket?token=wv39nM6iyrNJulvpmMQrimYPIXy2dVrYRjkuHpbRapKT2VSh65ngDGHdCdCtmEN9") + this._keepAlive = null; + this._interval = null; + this._callback = callback; + + this._socket.binaryType = "arraybuffer"; + + this._socket.onopen = () => { + console.log("Hyperate socket opened") + + this._socket.send( + JSON.stringify({ + topic: "hr:84aa0f", + event: "phx_join", + payload: {}, + ref: 0, + }), + ); + + this._keepAlive = setInterval(() => { + this._socket.send( + JSON.stringify({ + topic: "phoenix", + event: "heartbeat", + payload: {}, + ref: 0, + }), + ); + }, 10000); + } + + this._socket.onmessage = ({ data }: MessageEvent) => { + data = JSON.parse(data); + + switch (data.event) { + case "hr_update": { + this._callback(data.payload.hr); + this.heartbeat(); + break; + } + } + } + + this._socket.onerror = () => { + console.error("Hyperate socket error"); + if (this._keepAlive) { + clearInterval(this._keepAlive); + this._keepAlive = null; + } + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + } + + this._socket.onclose = () => { + console.log("Hyperate socket closed"); + if (this._keepAlive) { + clearInterval(this._keepAlive); + this._keepAlive = null; + } + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + } + + } + + private heartbeat() { + if (this._interval) { + clearTimeout(this._interval); + this._interval = null; + } + this._interval = setTimeout(() => { + this._callback(0); + }, 6000); + } +} \ No newline at end of file diff --git a/src/back/Sockets/Lanyard.ts b/src/back/Sockets/Lanyard.ts new file mode 100644 index 0000000..d4999fa --- /dev/null +++ b/src/back/Sockets/Lanyard.ts @@ -0,0 +1,61 @@ +import ReconnectingWebSocket from 'reconnecting-websocket'; + +export default class { + private _socket: ReconnectingWebSocket; + private _keepAlive: NodeJS.Timeout | null; + private _callback: (data: { [key: string]: string }) => void; + + constructor(callback: (data: { [key: string]: string }) => void) { + this._socket = new ReconnectingWebSocket("wss://lanyard.creations.works/socket") + this._keepAlive = null; + this._callback = callback; + + this._socket.binaryType = "arraybuffer"; + + this._socket.onopen = () => { + console.log("Lanyard socket opened") + } + + this._socket.onmessage = ({ data }: MessageEvent) => { + data = JSON.parse(data); + + switch (data.op) { + case 0: { + this._callback(data.d) + break; + } + case 1: { + this._socket.send(JSON.stringify({ + op: 2, + d: { + subscribe_to_id: "1273447359417942128" + } + })) + this._keepAlive = setInterval(() => { + this._socket.send(JSON.stringify({ + op: 3 + })) + }, data.d.heartbeat_interval) + break; + } + } + } + + this._socket.onerror = () => { + console.error("Lanyard socket error"); + if (this._keepAlive) { + clearInterval(this._keepAlive); + this._keepAlive = null; + } + } + + this._socket.onclose = () => { + console.log("Lanyard socket closed"); + if (this._keepAlive) { + clearInterval(this._keepAlive); + this._keepAlive = null; + } + } + + } +} \ No newline at end of file diff --git a/src/back/index.ts b/src/back/index.ts new file mode 100644 index 0000000..7ac97e1 --- /dev/null +++ b/src/back/index.ts @@ -0,0 +1,74 @@ +import Hyperate from "./Sockets/Hyperate"; +import Lanyard from "./Sockets/Lanyard"; + +const development = process.env.NODE_ENV === "development"; + +const build = async () => { + return await Bun.build({ + entrypoints: ['./src/front/index.html'], + outdir: './dist', + minify: !development, + sourcemap: (development ? "inline" : "none"), + splitting: true, + publicPath: "/assets/", + }) +} + +const respOptions = { + headers: { + "Content-Type": "application/json", + "Cache-Control": "no-cache", + "Content-Encoding": "gzip", + } +} +const okResp = new Response(Bun.gzipSync(JSON.stringify({ data: "ok" })), respOptions) +const Responses = { + ok: () => { + return okResp.clone(); + }, + json: (data: { [key: string]: string }) => { + return new Response(Bun.gzipSync(JSON.stringify(data)), respOptions); + }, + file: async (file: Bun.BunFile) => { + return new Response(Bun.gzipSync(await file.arrayBuffer()), { + headers: { + "Content-Type": file.type, + "Cache-Control": "public, max-age=31536000", + "Content-Encoding": "gzip", + } + }); + } +} + +const postAnalytics = async (req: Request | Bun.BunRequest, server: Bun.Server) => { + return await fetch("https://plausible.creations.works/api/event", { + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": req.headers.get("user-agent") || "", + "X-Forwarded-For": String( + req.headers.get("CF-Connecting-IP") || + req.headers.get("X-Real-IP") || + req.headers.get("X-Forwarded-For")?.split(",")[0] || + (typeof server.requestIP(req) === "string" ? server.requestIP(req) : (server.requestIP(req)?.address || "")) + ), + }, + body: JSON.stringify({ + domain: "ipv4.army", + name: "pageview", + url: req.url, + referrer: req.headers.get("referer") || "", + }) + }) +} + +export default { + Sockets: { + Hyperate, + Lanyard + }, + Responses, + build, + development, + postAnalytics, +}; \ No newline at end of file diff --git a/src/components/Lanyard/index.tsx b/src/components/Lanyard/index.tsx deleted file mode 100644 index 01381df..0000000 --- a/src/components/Lanyard/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { createRef } from "tsx-dom"; -import microlight from "microlight"; - -import socket from "../../Socket"; - -const statusTypes: { [key: string]: string } = { - online: "green", - idle: "yellow", - dnd: "red", - invisible: "inherent", - offline: "inherent", -} - -const activityTypes: { [key: number]: string } = { - 0: "Playing", - 1: "Streaming", - 2: "Listening to", - 3: "Watching", - 4: "Custom", - 5: "Competing in", -} - -export default () => { - const paragraph = createRef(); - - socket.addEventListener('lanyard', (event: Event) => { - const lanyard = (event as CustomEvent).detail; - if (paragraph.current) { - paragraph.current.style = `--status-color: ${statusTypes[lanyard.discord_status]};`; - paragraph.current.innerText = JSON.stringify({ - status: lanyard.discord_status, - activities: lanyard.activities.map((act: { type: number, name: string }) => { return `${activityTypes[act.type]} ${act.name}` }), - }, null, 1); - } - microlight.reset(); - }); - return
-

{JSON.stringify({})}

-
; -} \ No newline at end of file diff --git a/src/App.css b/src/front/App.css similarity index 71% rename from src/App.css rename to src/front/App.css index 7bcd83f..86b4f77 100644 --- a/src/App.css +++ b/src/front/App.css @@ -4,7 +4,7 @@ .scanlines:before, .scanlines:after { - display: block; + display: inherit; pointer-events: none; content: ""; position: absolute; @@ -28,8 +28,8 @@ background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, 0.3) 51%); - background-size: 100% 4px; - animation: scanlines 1s steps(60) infinite; + background-size: 100% 6px; + animation: scanlines 2s steps(30) infinite; } /* ANIMATE UNIQUE SCANLINE */ @@ -45,15 +45,15 @@ } } -div { - margin: 0; - padding: 0; +span.shj-syn-str:nth-child(2) { + color: var(--status-color, rgba(150, 150, 150, 0.1)); } -div.scanlines { - position: absolute; +.shj-numbers { + padding: 0px; } -.microlight>span:nth-child(6) { - color: var(--status-color); +.shj-lang-json { + padding: 0px; + background-color: transparent; } \ No newline at end of file diff --git a/src/App.tsx b/src/front/App.tsx similarity index 66% rename from src/App.tsx rename to src/front/App.tsx index 58cdc07..06e9041 100644 --- a/src/App.tsx +++ b/src/front/App.tsx @@ -2,9 +2,9 @@ import Lanyard from './components/Lanyard'; import Hyperate from './components/Hyperate'; export default () => { - return
+ return

seth> cat ./about.txt

-

A Dedicated Backend Developer, with a passion for high-fidelity audio, gaming, and web development.

+

A Dedicated Backend Developer,
with a passion for high-fidelity audio,
gaming, and web development.

seth> curl /tmp/discord-ipc

diff --git a/src/Socket.ts b/src/front/Socket.ts similarity index 73% rename from src/Socket.ts rename to src/front/Socket.ts index c472067..22a39a9 100644 --- a/src/Socket.ts +++ b/src/front/Socket.ts @@ -19,12 +19,24 @@ class Socket extends EventTarget { this.emitHyperate(data.hr); break; } + case "echo": { + console.log("Echo: ", data); + break; + } + default: { + console.error("Unknown message type: ", type, data); + break; + } } }; + this._socket.onclose = () => { + location.reload(); + }; + setInterval(() => { this._socket.send("ping"); - }, 10000); + }, 30 * 1000); } emitLanyard(lanyard: object) { diff --git a/src/components/Hyperate/index.tsx b/src/front/components/Hyperate/index.tsx similarity index 77% rename from src/components/Hyperate/index.tsx rename to src/front/components/Hyperate/index.tsx index a8b220b..8ca5997 100644 --- a/src/components/Hyperate/index.tsx +++ b/src/front/components/Hyperate/index.tsx @@ -1,5 +1,4 @@ import { createRef } from "tsx-dom"; -import microlight from "microlight"; import socket from "../../Socket"; @@ -11,9 +10,8 @@ export default () => { if (paragraph.current) { paragraph.current.innerText = `${heartRate} BPM`; } - microlight.reset(); }); return
-

0 BPM

+

0 BPM

; } \ No newline at end of file diff --git a/src/front/components/Lanyard/index.tsx b/src/front/components/Lanyard/index.tsx new file mode 100644 index 0000000..29396c6 --- /dev/null +++ b/src/front/components/Lanyard/index.tsx @@ -0,0 +1,59 @@ +import { createRef } from "tsx-dom"; +import { highlightAll } from "@speed-highlight/core"; + +import socket from "../../Socket"; + +const statusTypes: { [key: string]: string } = { + online: "rgb(0, 150, 0)", + idle: "rgb(150, 150, 0)", + dnd: "rgb(150, 0, 0)", + offline: "rgb(150, 150, 150)", +} + +const gradientTypes: { [key: string]: string } = { + online: "rgba(0, 150, 0, 0.1)", + idle: "rgba(150, 150, 0, 0.1)", + dnd: "rgba(150, 0, 0, 0.1)", + offline: "rgba(150, 150, 150, 0.1)", +} +const activityTypes: { [key: number]: string } = { + 0: "Playing", + 1: "Streaming", + 2: "Listening to", + 3: "Watching", + 4: "Custom Status", + 5: "Competing in", +} + +const stringify = (data: { [key: string]: string }) => { + return JSON.stringify(data, null, 2) +} + +export default () => { + const code = createRef(); + + socket.addEventListener('lanyard', (event: Event) => { + const lanyard = (event as CustomEvent).detail; + document.body.style = `--status-color: ${statusTypes[lanyard.discord_status]}; --gradient-color: ${gradientTypes[lanyard.discord_status]};`; + if (code.current) { + code.current.innerHTML = stringify({ + status: lanyard.discord_status, + activities: lanyard.activities.map((act: { + type: number, name: string, details: string, state: string + }) => { + return [ + ... new Set([ + activityTypes[act.type], + act.name, + act.details, + act.state + ]) + ].filter(n => n) + }), + }); + } + highlightAll(); + }); + + return
{"{}"}
+} \ No newline at end of file diff --git a/src/front/index.css b/src/front/index.css new file mode 100644 index 0000000..f1285a9 --- /dev/null +++ b/src/front/index.css @@ -0,0 +1,20 @@ +@import "../../node_modules/@speed-highlight/core/dist/themes/dark.css"; + +@import "./App.css"; + +html, +head, +body { + margin: 0; + padding: 0; + font: 2vh monospace; + height: 100vh; + width: 100vw; +} + +body { + color: #DEDEDE; + text-shadow: 0 0 5px #C8C8C8; + background: radial-gradient(at bottom right, var(--gradient-color, rgba(150, 150, 150, 0.1)) 0%, rgba(0, 0, 0, 1) 100%); + display: flex; +} \ No newline at end of file diff --git a/src/index.html b/src/front/index.html similarity index 72% rename from src/index.html rename to src/front/index.html index 15fdea9..b3b4c13 100644 --- a/src/index.html +++ b/src/front/index.html @@ -4,21 +4,19 @@ - + Seth @ IPv4 dot Army - + - - \ No newline at end of file diff --git a/src/front/index.tsx b/src/front/index.tsx new file mode 100644 index 0000000..eedc7db --- /dev/null +++ b/src/front/index.tsx @@ -0,0 +1,8 @@ +import "tsx-dom"; + +import App from './App'; + +document.body.appendChild(); + +// You're garbage, let me collect you. +fetch("/api/gc") \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index abb8639..0000000 --- a/src/index.css +++ /dev/null @@ -1,18 +0,0 @@ -@import "./App.css"; - -html, -head, -body { - margin: 0; - padding: 0; - font-family: 'Circular Std', sans-serif; - height: 100vh; - width: 100vw; -} - -body { - color: #DEDEDE; - font: 2vh Inconsolata, monospace; - text-shadow: 0 0 5px #C8C8C8; - background: radial-gradient(at bottom right, rgba(0, 150, 0, 0.1) 0%, rgba(0, 0, 0, 1) 100%); -} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index b40f99d..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import "tsx-dom"; - -import App from './App'; - -const font = document.getElementById("font") -if (font) { - font.innerText = `@font-face { - font-family: 'Circular Std'; - src: url('/public/font/Black.woff2') format('woff2'); - font-weight: black; - font-style: normal; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/BlackItalic.woff2') format('woff2'); - font-weight: black; - font-style: italic; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/Bold.woff2') format('woff2'); - font-weight: bold; - font-style: normal; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/BoldItalic.woff2') format('woff2'); - font-weight: bold; - font-style: italic; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/Book.woff2') format('woff2'); - font-weight: normal; - font-style: normal; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/BookItalic.woff2') format('woff2'); - font-weight: normal; - font-style: italic; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/Medium.woff2') format('woff2'); - font-weight: 500; - font-style: normal; - font-display: swap -} - -@font-face { - font-family: 'Circular Std'; - src: url('/public/font/MediumItalic.woff2') format('woff2'); - font-weight: 500; - font-style: italic; - font-display: swap -}` -} -document.body.appendChild(); - -// You're garbage, let me collect you. -fetch("/api/gc") \ No newline at end of file