diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a841f7..25117cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "github-enterprise.uri": "https://git.creations.works" + "github-enterprise.uri": "https://git.creations.works" } diff --git a/biome.json b/biome.json deleted file mode 100644 index 3c4d4f1..0000000 --- a/biome.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": false - }, - "files": { - "ignoreUnknown": false, - "ignore": [] - }, - "formatter": { - "enabled": true, - "indentStyle": "tab", - "lineEnding": "lf" - }, - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double", - "indentStyle": "tab", - "lineEnding": "lf", - "jsxQuoteStyle": "double", - "semicolons": "always" - } - } -} diff --git a/config/environment.ts b/config/environment.ts index 239d2c3..c584b45 100644 --- a/config/environment.ts +++ b/config/environment.ts @@ -1,8 +1,9 @@ export const environment: Environment = { - port: Number.parseInt(process.env.PORT || "8080", 10), + port: parseInt(process.env.PORT || "8080", 10), host: process.env.HOST || "0.0.0.0", development: - process.env.NODE_ENV === "development" || process.argv.includes("--dev"), + process.env.NODE_ENV === "development" || + process.argv.includes("--dev"), }; export const lanyardConfig: LanyardConfig = { diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..d43df76 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,132 @@ +import pluginJs from "@eslint/js"; +import tseslintPlugin from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; +import prettier from "eslint-plugin-prettier"; +import promisePlugin from "eslint-plugin-promise"; +import simpleImportSort from "eslint-plugin-simple-import-sort"; +import unicorn from "eslint-plugin-unicorn"; +import unusedImports from "eslint-plugin-unused-imports"; +import globals from "globals"; + +/** @type {import('eslint').Linter.FlatConfig[]} */ +export default [ + { + files: ["**/*.{js,mjs,cjs}"], + languageOptions: { + globals: globals.node, + }, + ...pluginJs.configs.recommended, + plugins: { + "simple-import-sort": simpleImportSort, + "unused-imports": unusedImports, + promise: promisePlugin, + prettier: prettier, + unicorn: unicorn, + }, + rules: { + "eol-last": ["error", "always"], + "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 1 }], + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", + }, + ], + "promise/always-return": "error", + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/catch-or-return": "error", + "promise/no-nesting": "warn", + "promise/no-promise-in-callback": "warn", + "promise/no-callback-in-promise": "warn", + "prettier/prettier": [ + "error", + { + useTabs: true, + tabWidth: 4, + }, + ], + indent: ["error", "tab", { SwitchCase: 1 }], + "unicorn/filename-case": [ + "error", + { + case: "camelCase", + }, + ], + }, + }, + { + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser: tsParser, + globals: globals.node, + }, + plugins: { + "@typescript-eslint": tseslintPlugin, + "simple-import-sort": simpleImportSort, + "unused-imports": unusedImports, + promise: promisePlugin, + prettier: prettier, + unicorn: unicorn, + }, + rules: { + ...tseslintPlugin.configs.recommended.rules, + quotes: ["error", "double"], + "eol-last": ["error", "always"], + "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 1 }], + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", + }, + ], + "promise/always-return": "error", + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/catch-or-return": "error", + "promise/no-nesting": "warn", + "promise/no-promise-in-callback": "warn", + "promise/no-callback-in-promise": "warn", + "prettier/prettier": [ + "error", + { + useTabs: true, + tabWidth: 4, + }, + ], + indent: ["error", "tab", { SwitchCase: 1 }], + "unicorn/filename-case": [ + "error", + { + case: "camelCase", + }, + ], + "@typescript-eslint/explicit-function-return-type": ["error"], + "@typescript-eslint/explicit-module-boundary-types": ["error"], + "@typescript-eslint/typedef": [ + "error", + { + arrowParameter: true, + variableDeclaration: true, + propertyDeclaration: true, + memberVariableDeclaration: true, + parameter: true, + }, + ], + }, + }, +]; diff --git a/package.json b/package.json index 793fe71..8d6e543 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,24 @@ "scripts": { "start": "bun run src/index.ts", "dev": "bun run --hot src/index.ts --dev", - "lint": "bunx biome check", - "lint:fix": "bunx biome check --fix", + "lint": "eslint", + "lint:fix": "bun lint --fix", "cleanup": "rm -rf logs node_modules bun.lockdb" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@eslint/js": "^9.24.0", "@types/bun": "^1.2.8", "@types/ejs": "^3.1.5", - "globals": "^16.0.0" + "@typescript-eslint/eslint-plugin": "^8.29.0", + "@typescript-eslint/parser": "^8.29.0", + "eslint": "^9.24.0", + "eslint-plugin-prettier": "^5.2.6", + "eslint-plugin-promise": "^7.2.1", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-unicorn": "^58.0.0", + "eslint-plugin-unused-imports": "^4.1.4", + "globals": "^16.0.0", + "prettier": "^3.5.3" }, "peerDependencies": { "typescript": "^5.8.3" diff --git a/public/css/error.css b/public/css/error.css index 65dce6b..a8e591d 100644 --- a/public/css/error.css +++ b/public/css/error.css @@ -12,7 +12,7 @@ body { padding: 2rem; background: #1a1a1d; border-radius: 12px; - box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 20px rgba(0,0,0,0.3); } .error-title { font-size: 2rem; diff --git a/public/css/index.css b/public/css/index.css index 7a1a288..e4acc03 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1,38 +1,7 @@ -:root { - --background: #0e0e10; - --card-bg: #1e1f22; - --card-hover-bg: #2a2a2d; - --border-color: #2e2e30; - - --text-color: #ffffff; - --text-subtle: #bbb; - --text-secondary: #b5bac1; - --text-muted: #888; - --link-color: #00b0f4; - - --status-online: #23a55a; - --status-idle: #f0b232; - --status-dnd: #e03e3e; - --status-offline: #747f8d; - --status-streaming: #b700ff; - - --progress-bg: #f23f43; - --progress-fill: #5865f2; - - --button-bg: #5865f2; - --button-hover-bg: #4752c4; - --button-disabled-bg: #2d2e31; - - --blockquote-color: #aaa; - --code-bg: #2e2e30; - - --readme-bg: #1a1a1d; -} - body { font-family: system-ui, sans-serif; - background-color: var(--background); - color: var(--text-color); + background-color: #0e0e10; + color: #ffffff; margin: 0; padding: 2rem; display: flex; @@ -83,30 +52,26 @@ body { width: 24px; height: 24px; border-radius: 50%; - border: 4px solid var(--background); + border: 4px solid #0e0e10; display: flex; align-items: center; justify-content: center; } .status-indicator.online { - background-color: var(--status-online); + background-color: #23a55a; } .status-indicator.idle { - background-color: var(--status-idle); + background-color: #f0b232; } .status-indicator.dnd { - background-color: var(--status-dnd); + background-color: #f23f43; } .status-indicator.offline { - background-color: var(--status-offline); -} - -.status-indicator.streaming { - background-color: var(--status-streaming); + background-color: #747f8d; } .platform-icon.mobile-only { @@ -126,12 +91,12 @@ body { h1 { font-size: 2.5rem; margin: 0; - color: var(--link-color); + color: #00b0f4; } .custom-status { font-size: 1.2rem; - color: var(--text-subtle); + color: #bbb; margin-top: 0.25rem; word-break: break-word; overflow-wrap: anywhere; @@ -142,6 +107,7 @@ h1 { flex-wrap: wrap; } + .custom-status .custom-emoji { width: 20px; height: 20px; @@ -176,14 +142,14 @@ ul { display: flex; flex-direction: row; gap: 1rem; - background-color: var(--card-bg); + background-color: #1e1f22; padding: 0.75rem 1rem; border-radius: 10px; - border: 1px solid var(--border-color); + box-shadow: 0 1px 0 0 #2e2e30; } .activity:hover { - background: var(--card-hover-bg); + background: #2a2a2d; } .activity-wrapper { @@ -198,28 +164,7 @@ ul { gap: 1rem; } -.activity-image-wrapper { - position: relative; - width: 80px; - height: 80px; -} - -.activity-image-small { - width: 25px; - height: 25px; - border-radius: 50%; - object-fit: cover; - flex-shrink: 0; - border-color: var(--card-bg); - border-width: 2px; - border-style: solid; - - position: absolute; - bottom: -6px; - right: -10px; -} - -.activity-image { +.activity-art { width: 80px; height: 80px; border-radius: 6px; @@ -257,17 +202,17 @@ ul { .activity-name { font-weight: 600; font-size: 1rem; - color: var(--text-color); + color: #fff; } .activity-detail { font-size: 0.875rem; - color: var(--text-secondary); + color: #b5bac1; } .activity-timestamp { font-size: 0.75rem; - color: var(--text-secondary); + color: #b5bac1; text-align: right; margin-left: auto; white-space: nowrap; @@ -275,14 +220,14 @@ ul { .progress-bar { height: 4px; - background-color: var(--border-color); + background-color: #2e2e30; border-radius: 2px; - margin-top: 1rem; + margin-top: 0.5rem; overflow: hidden; } .progress-fill { - background-color: var(--progress-fill); + background-color: #5865f2; transition: width 0.4s ease; height: 100%; } @@ -290,13 +235,14 @@ ul { .progress-bar, .progress-time-labels { width: 100%; + margin-top: 0.5rem; } .progress-time-labels { display: flex; justify-content: space-between; font-size: 0.75rem; - color: var(--text-muted); + color: #888; margin-top: 0.25rem; } @@ -310,7 +256,7 @@ ul { font-size: 0.75rem; text-transform: uppercase; font-weight: 600; - color: var(--blockquote-color); + color: #aaa; margin-bottom: 0.50rem; display: block; } @@ -321,7 +267,7 @@ ul { .progress-time-labels.paused .progress-current::after { content: " ⏸"; - color: var(--status-idle); + color: #f0b232; } .activity-buttons { @@ -332,7 +278,7 @@ ul { } .activity-button { - background-color: var(--progress-fill); + background-color: #5865f2; color: white; border: none; border-radius: 3px; @@ -345,12 +291,12 @@ ul { } .activity-button:hover { - background-color: var(--button-hover-bg); + background-color: #4752c4; text-decoration: none; } .activity-button:disabled { - background-color: var(--button-disabled-bg); + background-color: #2d2e31; cursor: not-allowed; opacity: 0.8; } @@ -379,13 +325,6 @@ ul { width: 100%; } - .activity-image-wrapper { - max-width: 100%; - max-height: 100%; - width: 100%; - height: 100%; - } - .avatar-wrapper { width: 96px; height: 96px; @@ -440,31 +379,21 @@ ul { align-items: center; text-align: center; padding: 1rem; - border-radius: 0; + border-radius:0; } - .activity-image { + .activity-art { width: 100%; - max-width: 100%; + max-width: 300px; height: auto; border-radius: 8px; } - .activity-image-small { - width: 40px; - height: 40px; - } - .activity-content { width: 100%; align-items: center; } - .activity-wrapper-inner { - flex-direction: column; - align-items: center; - } - .activity-header { flex-direction: column; align-items: center; @@ -501,12 +430,12 @@ ul { /* readme :p */ .readme { - max-width: 700px; + max-width: 600px; width: 100%; - background: var(--readme-bg); + background: #1a1a1d; padding: 1.5rem; border-radius: 8px; - border: 1px solid var(--border-color); + box-shadow: 0 0 0 1px #2e2e30; margin-top: 2rem; @@ -517,13 +446,13 @@ ul { .readme h2 { margin-top: 0; - color: var(--link-color); + color: #00b0f4; } .markdown-body { font-size: 1rem; line-height: 1.6; - color: var(--text-color); + color: #ddd; } .markdown-body h1, @@ -532,7 +461,7 @@ ul { .markdown-body h4, .markdown-body h5, .markdown-body h6 { - color: var(--text-color); + color: #ffffff; margin-top: 1.25rem; margin-bottom: 0.5rem; } @@ -542,7 +471,7 @@ ul { } .markdown-body a { - color: var(--link-color); + color: #00b0f4; text-decoration: none; } @@ -551,7 +480,7 @@ ul { } .markdown-body code { - background: var(--border-color); + background: #2e2e30; padding: 0.2em 0.4em; border-radius: 4px; font-family: monospace; @@ -559,7 +488,7 @@ ul { } .markdown-body pre { - background: var(--border-color); + background: #2e2e30; padding: 1rem; border-radius: 6px; overflow-x: auto; @@ -574,9 +503,9 @@ ul { } .markdown-body blockquote { - border-left: 4px solid var(--link-color); + border-left: 4px solid #00b0f4; padding-left: 1rem; - color: var(--blockquote-color); + color: #aaa; margin: 1rem 0; } diff --git a/public/js/index.js b/public/js/index.js index e6a9afe..35dbe32 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,3 +1,5 @@ +/* eslint-disable indent */ + const activityProgressMap = new Map(); function formatTime(ms) { @@ -21,19 +23,19 @@ function formatVerbose(ms) { function updateElapsedAndProgress() { const now = Date.now(); - for (const el of document.querySelectorAll(".activity-timestamp")) { + document.querySelectorAll(".activity-timestamp").forEach((el) => { const start = Number(el.dataset.start); - if (!start) continue; + if (!start) return; const elapsed = now - start; const display = el.querySelector(".elapsed"); if (display) display.textContent = `(${formatVerbose(elapsed)} ago)`; - } + }); - for (const bar of document.querySelectorAll(".progress-bar")) { + document.querySelectorAll(".progress-bar").forEach((bar) => { const start = Number(bar.dataset.start); const end = Number(bar.dataset.end); - if (!start || !end || end <= start) continue; + if (!start || !end || end <= start) return; const duration = end - start; const elapsed = Math.min(now - start, duration); @@ -44,12 +46,12 @@ function updateElapsedAndProgress() { const fill = bar.querySelector(".progress-fill"); if (fill) fill.style.width = `${progress}%`; - } + }); - for (const label of document.querySelectorAll(".progress-time-labels")) { + document.querySelectorAll(".progress-time-labels").forEach((label) => { const start = Number(label.dataset.start); const end = Number(label.dataset.end); - if (!start || !end || end <= start) continue; + if (!start || !end || end <= start) return; const isPaused = now > end; const current = isPaused ? end - start : Math.max(0, now - start); @@ -75,14 +77,14 @@ function updateElapsedAndProgress() { : formatTime(current); } if (totalEl) totalEl.textContent = formatTime(total); - } + }); } updateElapsedAndProgress(); setInterval(updateElapsedAndProgress, 1000); const head = document.querySelector("head"); -const userId = head?.dataset.userId; +let userId = head?.dataset.userId; let instanceUri = head?.dataset.instanceUri; if (userId && instanceUri) { @@ -130,25 +132,6 @@ if (userId && instanceUri) { }); } -function resolveActivityImage(img, applicationId) { - if (!img) return null; - - if (img.startsWith("mp:external/")) { - return `https://media.discordapp.net/external/${img.slice("mp:external/".length)}`; - } - - if (img.includes("/https/")) { - const clean = img.split("/https/")[1]; - return clean ? `https://${clean}` : null; - } - - if (img.startsWith("spotify:")) { - return `https://i.scdn.co/image/${img.split(":")[1]}`; - } - - return `https://cdn.discordapp.com/app-assets/${applicationId}/${img}.png`; -} - function buildActivityHTML(activity) { const start = activity.timestamps?.start; const end = activity.timestamps?.end; @@ -160,18 +143,18 @@ function buildActivityHTML(activity) { ? Math.min(100, Math.floor((elapsed / total) * 100)) : null; + const img = activity.assets?.large_image; let art = null; - let smallArt = null; - if (activity.assets) { - art = resolveActivityImage( - activity.assets.large_image, - activity.application_id, - ); - smallArt = resolveActivityImage( - activity.assets.small_image, - activity.application_id, - ); + if (img?.startsWith("mp:external/")) { + art = `https://media.discordapp.net/external/${img.slice("mp:external/".length)}`; + } else if (img?.includes("/https/")) { + const clean = img.split("/https/")[1]; + if (clean) art = `https://${clean}`; + } else if (img?.startsWith("spotify:")) { + art = `https://i.scdn.co/image/${img.split(":")[1]}`; + } else if (img) { + art = `https://cdn.discordapp.com/app-assets/${activity.application_id}/${img}.png`; } const activityTypeMap = { @@ -206,7 +189,10 @@ function buildActivityHTML(activity) { ? `
${activity.buttons .map((button, index) => { - const label = typeof button === "string" ? button : button.label; + const label = + typeof button === "string" + ? button + : button.label; let url = null; if (typeof button === "object" && button.url) { url = button.url; @@ -239,13 +225,6 @@ function buildActivityHTML(activity) { const secondaryLine = isMusic ? activity.state : activity.details; const tertiaryLine = isMusic ? activity.assets?.large_text : activity.state; - const activityArt = art - ? `
- Art - ${smallArt ? `Small Art` : ""} -
` - : ""; - return `
  • @@ -254,7 +233,7 @@ function buildActivityHTML(activity) { ${activityTimestamp}
    - ${activityArt} + ${art ? `Art` : ""}
    @@ -279,7 +258,9 @@ function buildActivityHTML(activity) { function updatePresence(data) { const avatarWrapper = document.querySelector(".avatar-wrapper"); const statusIndicator = avatarWrapper?.querySelector(".status-indicator"); - const mobileIcon = avatarWrapper?.querySelector(".platform-icon.mobile-only"); + const mobileIcon = avatarWrapper?.querySelector( + ".platform-icon.mobile-only", + ); const userInfo = document.querySelector(".user-info"); const customStatus = userInfo?.querySelector(".custom-status"); @@ -290,16 +271,8 @@ function updatePresence(data) { desktop: data.active_on_discord_desktop, }; - let status = "offline"; - console.log(data.activities.some((activity) => activity.type === 1)); - if (data.activities.some((activity) => activity.type === 1)) { - status = "streaming"; - } else { - status = data.discord_status; - } - if (statusIndicator) { - statusIndicator.className = `status-indicator ${status}`; + statusIndicator.className = `status-indicator ${data.discord_status}`; } if (platform.mobile && !mobileIcon) { @@ -310,7 +283,7 @@ function updatePresence(data) { `; } else if (!platform.mobile && mobileIcon) { mobileIcon.remove(); - avatarWrapper.innerHTML += `
    `; + avatarWrapper.innerHTML += `
    `; } const custom = data.activities?.find((a) => a.type === 4); diff --git a/src/helpers/char.ts b/src/helpers/char.ts index 17657b3..6ecab40 100644 --- a/src/helpers/char.ts +++ b/src/helpers/char.ts @@ -1,6 +1,6 @@ export function timestampToReadable(timestamp?: number): string { const date: Date = - timestamp && !Number.isNaN(timestamp) ? new Date(timestamp) : new Date(); - if (Number.isNaN(date.getTime())) return "Invalid Date"; + timestamp && !isNaN(timestamp) ? new Date(timestamp) : new Date(); + if (isNaN(date.getTime())) return "Invalid Date"; return date.toISOString().replace("T", " ").replace("Z", ""); } diff --git a/src/helpers/ejs.ts b/src/helpers/ejs.ts index 6544009..6b03dd0 100644 --- a/src/helpers/ejs.ts +++ b/src/helpers/ejs.ts @@ -1,5 +1,5 @@ -import { resolve } from "node:path"; import { renderFile } from "ejs"; +import { resolve } from "path"; export async function renderEjsTemplate( viewName: string | string[], diff --git a/src/helpers/lanyard.ts b/src/helpers/lanyard.ts index de78955..258b980 100644 --- a/src/helpers/lanyard.ts +++ b/src/helpers/lanyard.ts @@ -76,7 +76,7 @@ export async function handleReadMe(data: LanyardData): Promise { return null; if (res.headers.has("content-length")) { - const size: number = Number.parseInt( + const size: number = parseInt( res.headers.get("content-length") || "0", 10, ); diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts index 4cbb12b..331be1d 100644 --- a/src/helpers/logger.ts +++ b/src/helpers/logger.ts @@ -1,15 +1,15 @@ -import type { Stats } from "node:fs"; +import { environment } from "@config/environment"; +import { timestampToReadable } from "@helpers/char"; +import type { Stats } from "fs"; import { - type WriteStream, createWriteStream, existsSync, mkdirSync, statSync, -} from "node:fs"; -import { EOL } from "node:os"; -import { basename, join } from "node:path"; -import { environment } from "@config/environment"; -import { timestampToReadable } from "@helpers/char"; + WriteStream, +} from "fs"; +import { EOL } from "os"; +import { basename, join } from "path"; class Logger { private static instance: Logger; @@ -37,7 +37,7 @@ class Logger { mkdirSync(logDir, { recursive: true }); } - let addSeparator = false; + let addSeparator: boolean = false; if (existsSync(logFile)) { const fileStats: Stats = statSync(logFile); @@ -66,9 +66,9 @@ class Logger { private extractFileName(stack: string): string { const stackLines: string[] = stack.split("\n"); - let callerFile = ""; + let callerFile: string = ""; - for (let i = 2; i < stackLines.length; i++) { + for (let i: number = 2; i < stackLines.length; i++) { const line: string = stackLines[i].trim(); if (line && !line.includes("Logger.") && line.includes("(")) { callerFile = line.split("(")[1]?.split(")")[0] || ""; @@ -91,7 +91,7 @@ class Logger { return { filename, timestamp: readableTimestamp }; } - public info(message: string | string[], breakLine = false): void { + public info(message: string | string[], breakLine: boolean = false): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -110,7 +110,7 @@ class Logger { this.writeConsoleMessageColored(logMessageParts, breakLine); } - public warn(message: string | string[], breakLine = false): void { + public warn(message: string | string[], breakLine: boolean = false): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -131,7 +131,7 @@ class Logger { public error( message: string | Error | (string | Error)[], - breakLine = false, + breakLine: boolean = false, ): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -161,7 +161,7 @@ class Logger { bracketMessage2: string, message: string | string[], color: string, - breakLine = false, + breakLine: boolean = false, ): void { const stack: string = new Error().stack || ""; const { timestamp } = this.getCallerInfo(stack); @@ -189,7 +189,7 @@ class Logger { private writeConsoleMessageColored( logMessageParts: ILogMessageParts, - breakLine = false, + breakLine: boolean = false, ): void { const logMessage: string = Object.keys(logMessageParts) .map((key: string) => { diff --git a/src/index.ts b/src/index.ts index 60606d4..6d2801d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,11 @@ import { logger } from "@helpers/logger"; import { serverHandler } from "@/server"; async function main(): Promise { - serverHandler.initialize(); + try { + serverHandler.initialize(); + } catch (error) { + throw error; + } } main().catch((error: Error) => { diff --git a/src/routes/[id].ts b/src/routes/[id].ts index bdfc975..25d8f30 100644 --- a/src/routes/[id].ts +++ b/src/routes/[id].ts @@ -29,20 +29,13 @@ async function handler(request: ExtendedRequest): Promise { } const presence: LanyardData = data.data; - const readme: string | Promise | null = await handleReadMe(presence); - - let status: string; - if (presence.activities.some((activity) => activity.type === 1)) { - status = "streaming"; - } else { - status = presence.discord_status; - } + const readme: string | Promise | null = + await handleReadMe(presence); const ejsTemplateData: EjsTemplateData = { - title: presence.discord_user.global_name || presence.discord_user.username, - username: - presence.discord_user.global_name || presence.discord_user.username, - status: status, + title: `${presence.discord_user.username || "Unknown"}`, + username: presence.discord_user.username, + status: presence.discord_status, activities: presence.activities, user: presence.discord_user, platform: { diff --git a/src/routes/api/colors.ts b/src/routes/api/colors.ts index 9e05bd3..d8817ca 100644 --- a/src/routes/api/colors.ts +++ b/src/routes/api/colors.ts @@ -24,7 +24,10 @@ async function handler(request: ExtendedRequest): Promise { try { res = await fetch(url); } catch { - return Response.json({ error: "Failed to fetch image" }, { status: 500 }); + return Response.json( + { error: "Failed to fetch image" }, + { status: 500 }, + ); } if (!res.ok) { diff --git a/src/routes/index.ts b/src/routes/index.ts index 829d105..692fc59 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -28,20 +28,15 @@ async function handler(): Promise { } const presence: LanyardData = data.data; - const readme: string | Promise | null = await handleReadMe(presence); - - let status: string; - if (presence.activities.some((activity) => activity.type === 1)) { - status = "streaming"; - } else { - status = presence.discord_status; - } + const readme: string | Promise | null = + await handleReadMe(presence); const ejsTemplateData: EjsTemplateData = { - title: presence.discord_user.global_name || presence.discord_user.username, + title: + presence.discord_user.global_name || presence.discord_user.username, username: presence.discord_user.global_name || presence.discord_user.username, - status: status, + status: presence.discord_status, activities: presence.activities, user: presence.discord_user, platform: { diff --git a/src/server.ts b/src/server.ts index fc8d4b5..2298034 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,4 +1,3 @@ -import { resolve } from "node:path"; import { environment } from "@config/environment"; import { logger } from "@helpers/logger"; import { @@ -7,6 +6,7 @@ import { type MatchedRoute, type Serve, } from "bun"; +import { resolve } from "path"; import { webSocketHandler } from "@/websocket"; @@ -77,16 +77,21 @@ class ServerHandler { if (await file.exists()) { const fileContent: ArrayBuffer = await file.arrayBuffer(); - const contentType: string = file.type || "application/octet-stream"; + const contentType: string = + file.type || "application/octet-stream"; return new Response(fileContent, { headers: { "Content-Type": contentType }, }); + } else { + logger.warn(`File not found: ${filePath}`); + return new Response("Not Found", { status: 404 }); } - logger.warn(`File not found: ${filePath}`); - return new Response("Not Found", { status: 404 }); } catch (error) { - logger.error([`Error serving static file: ${pathname}`, error as Error]); + logger.error([ + `Error serving static file: ${pathname}`, + error as Error, + ]); return new Response("Internal Server Error", { status: 500 }); } } @@ -112,7 +117,8 @@ class ServerHandler { try { const routeModule: RouteModule = await import(filePath); - const contentType: string | null = request.headers.get("Content-Type"); + const contentType: string | null = + request.headers.get("Content-Type"); const actualContentType: string | null = contentType ? contentType.split(";")[0].trim() : null; @@ -139,7 +145,9 @@ class ServerHandler { if ( (Array.isArray(routeModule.routeDef.method) && - !routeModule.routeDef.method.includes(request.method)) || + !routeModule.routeDef.method.includes( + request.method, + )) || (!Array.isArray(routeModule.routeDef.method) && routeModule.routeDef.method !== request.method) ) { @@ -164,7 +172,9 @@ class ServerHandler { if (Array.isArray(expectedContentType)) { matchesAccepts = expectedContentType.includes("*/*") || - expectedContentType.includes(actualContentType || ""); + expectedContentType.includes( + actualContentType || "", + ); } else { matchesAccepts = expectedContentType === "*/*" || @@ -203,7 +213,10 @@ class ServerHandler { } } } catch (error: unknown) { - logger.error([`Error handling route ${request.url}:`, error as Error]); + logger.error([ + `Error handling route ${request.url}:`, + error as Error, + ]); response = Response.json( { diff --git a/src/views/index.ejs b/src/views/index.ejs index 0611579..4b1e1bb 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -76,32 +76,20 @@ const total = (start && end) ? end - start : null; const progress = (total && elapsed > 0) ? Math.min(100, Math.floor((elapsed / total) * 100)) : null; + const img = activity.assets?.large_image; let art = null; - let smallArt = null; - function resolveActivityImage(img, applicationId) { - if (!img) return null; - - if (img.startsWith("mp:external/")) { - return `https://media.discordapp.net/external/${img.slice("mp:external/".length)}`; - } - - if (img.includes("/https/")) { - const clean = img.split("/https/")[1]; - return clean ? `https://${clean}` : null; - } - - if (img.startsWith("spotify:")) { - return `https://i.scdn.co/image/${img.split(":")[1]}`; - } - - return `https://cdn.discordapp.com/app-assets/${applicationId}/${img}.png`; + if (img?.startsWith("mp:external/")) { + art = `https://media.discordapp.net/external/${img.slice("mp:external/".length)}`; + } else if (img?.includes("/https/")) { + const clean = img.split("/https/")[1]; + if (clean) art = `https://${clean}`; + } else if (img?.startsWith("spotify:")) { + art = `https://i.scdn.co/image/${img.split(":")[1]}`; + } else if (img) { + art = `https://cdn.discordapp.com/app-assets/${activity.application_id}/${img}.png`; } - if (activity.assets) { - art = resolveActivityImage(activity.assets.large_image, activity.application_id); - smallArt = resolveActivityImage(activity.assets.small_image, activity.application_id); - } const activityTypeMap = { 0: "Playing", @@ -136,12 +124,7 @@
    <% if (art) { %> -
    - Art> - <% if (smallArt) { %> - Small Art> - <% } %> -
    + Art <% } %>
    diff --git a/src/websocket.ts b/src/websocket.ts index 99686e8..ce87fe8 100644 --- a/src/websocket.ts +++ b/src/websocket.ts @@ -1,5 +1,5 @@ import { logger } from "@helpers/logger"; -import type { ServerWebSocket } from "bun"; +import { type ServerWebSocket } from "bun"; class WebSocketHandler { public handleMessage(ws: ServerWebSocket, message: string): void { @@ -20,7 +20,11 @@ class WebSocketHandler { } } - public handleClose(ws: ServerWebSocket, code: number, reason: string): void { + public handleClose( + ws: ServerWebSocket, + code: number, + reason: string, + ): void { logger.warn(`WebSocket closed with code ${code}, reason: ${reason}`); } } diff --git a/tsconfig.json b/tsconfig.json index 68a5a97..ac5f2c7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,14 +2,28 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@/*": ["src/*"], - "@config/*": ["config/*"], - "@types/*": ["types/*"], - "@helpers/*": ["src/helpers/*"] + "@/*": [ + "src/*" + ], + "@config/*": [ + "config/*" + ], + "@types/*": [ + "types/*" + ], + "@helpers/*": [ + "src/helpers/*" + ] }, - "typeRoots": ["./src/types", "./node_modules/@types"], + "typeRoots": [ + "./src/types", + "./node_modules/@types" + ], // Enable latest features - "lib": ["ESNext", "DOM"], + "lib": [ + "ESNext", + "DOM" + ], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", @@ -27,7 +41,11 @@ // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false + "noPropertyAccessFromIndexSignature": false, }, - "include": ["src", "types", "config"] + "include": [ + "src", + "types", + "config" + ], }