-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(PVQ Panel): add activity #11030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||
| { | ||||||
| "$schema": "https://schemas.premid.app/metadata/1.10", | ||||||
| "apiVersion": 1, | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
Unexpected property "apiVersion"
|
||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| "author": { | ||||||
| "name": "Pavl21", | ||||||
| "id": "289810872396808193" | ||||||
| }, | ||||||
| "service": "PVQ Panel", | ||||||
| "description": { | ||||||
| "de": "Zeigt deine aktuelle Aktivität im PVQ Panel auf Discord.", | ||||||
| "en": "Shows what you are doing in PVQ Panel on Discord." | ||||||
|
Comment on lines
+10
to
+11
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be the description of the site, not the activity |
||||||
| }, | ||||||
| "url": "pv-q.de", | ||||||
| "regExp": "^https://(?:[a-z0-9-]+\\.)?pv-q\\.de(/.*)?$", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "version": "1.0.1", | ||||||
Check failureCode scanning / PMD Makes sure the version is bumped Error
Expected initial version of activity PVQ Panel to be 1.0.0
|
||||||
|
|
||||||
| "logo": "https://pv-q.de/favicons/pvq-icon-maskable-512x512.png", | ||||||
| "thumbnail": "https://pv-q.de/favicons/pvq-icon-maskable-512x512.png", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thumbnail should be a landscape promotional image or screenshot, not the logo |
||||||
| "color": "#3b82f6", | ||||||
| "category": "other", | ||||||
| "tags": ["pvq", "panel", "gameserver", "pterodactyl"], | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the Website name from the tags |
||||||
| "settings": [ | ||||||
| { | ||||||
| "id": "showServerName", | ||||||
| "icon": "fas fa-server", | ||||||
| "value": true, | ||||||
| "multiLanguage": true, | ||||||
| "title": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
"settings[0].title" must be string.
|
||||||
|
|
||||||
| "en": "Show Server Name", | ||||||
| "de": "Servername anzeigen" | ||||||
| }, | ||||||
| "description": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
Unexpected property "settings[0].description"
|
||||||
|
|
||||||
| "en": "Show the current server name on Discord.", | ||||||
| "de": "Zeigt den aktuellen Servernamen auf Discord an." | ||||||
| } | ||||||
|
Comment on lines
+27
to
+34
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are currently no multi-language title/descriptions for the settings. |
||||||
| }, | ||||||
| { | ||||||
| "id": "showStatus", | ||||||
| "icon": "fas fa-signal", | ||||||
| "value": true, | ||||||
| "multiLanguage": true, | ||||||
| "title": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
"settings[1].title" must be string.
|
||||||
|
|
||||||
| "en": "Show Server Status", | ||||||
| "de": "Server-Status anzeigen" | ||||||
| }, | ||||||
| "description": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
Unexpected property "settings[1].description"
|
||||||
|
|
||||||
| "en": "Show the server status (Online/Offline) on Discord.", | ||||||
| "de": "Zeigt den Server-Status (Online/Offline) auf Discord an." | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "id": "showElapsedTime", | ||||||
| "icon": "fas fa-clock", | ||||||
| "value": true, | ||||||
| "multiLanguage": true, | ||||||
| "title": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
"settings[2].title" must be string.
|
||||||
|
|
||||||
| "en": "Show Elapsed Time", | ||||||
| "de": "Vergangene Zeit anzeigen" | ||||||
| }, | ||||||
| "description": { | ||||||
Check failureCode scanning / ESLint validate object with JSON Schema. Error
Unexpected property "settings[2].description"
|
||||||
|
|
||||||
| "en": "Show how long you've been on the page.", | ||||||
| "de": "Zeigt, wie lange du auf der Seite bist." | ||||||
| } | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| const presence = new Presence({ clientId: '1509262800776728726' }) | ||
| const startTimestamp = Math.floor(Date.now() / 1000) | ||
|
|
||
| enum ActivityAssets { | ||
| Logo = 'https://pv-q.de/favicons/pvq-icon-maskable-512x512.png', | ||
| } | ||
|
|
||
| // Liest Server-Namen aus PVQ Panel Sidebar DOM. | ||
| // Regulärer Server: <span class="font-semibold text-lg text-gray-50 truncate" title="ServerName"> | ||
| // Node-Server: zwei Spans — "Node 555" + "Germany 1" → "Node 555 - Germany 1" | ||
| function getServerName(): string | null { | ||
| const regularSpan = document.querySelector<HTMLSpanElement>( | ||
| 'span.font-semibold.text-lg.text-gray-50.truncate[title]', | ||
| ) | ||
| if (regularSpan) { | ||
| const name = regularSpan.getAttribute('title')?.trim() | ||
| if (name) | ||
| return name | ||
| } | ||
|
|
||
| const nodeLabel = document.querySelector<HTMLSpanElement>( | ||
| 'span.text-2xl.font-extrabold.text-gray-50', | ||
| )?.textContent?.trim() | ||
| const nodeDesc = document.querySelector<HTMLSpanElement>( | ||
| 'span.text-md.text-gray-300', | ||
| )?.textContent?.trim() | ||
|
|
||
| if (nodeLabel && nodeDesc) | ||
| return `${nodeLabel} - ${nodeDesc}` | ||
| if (nodeLabel) | ||
| return nodeLabel | ||
|
|
||
| return null | ||
| } | ||
|
|
||
| function isNodeServer(name: string): boolean { | ||
| return /^Node \d+ - .+$/.test(name) | ||
| } | ||
|
|
||
| // Kürzt lange Server-/Node-Namen für die kompakte Discord-Anzeige. | ||
| function truncate(text: string, max: number): string { | ||
| return text.length > max ? `${text.slice(0, max - 1).trimEnd()}…` : text | ||
| } | ||
|
|
||
| function isBotPath(subPath: string): boolean { | ||
| return ['/knowledge', '/blacklist', '/channels', '/profile', '/access', '/memory'].some( | ||
| p => subPath.startsWith(p), | ||
| ) | ||
| } | ||
|
|
||
| function isNodeManagerPath(subPath: string): boolean { | ||
| return ['/firewall', '/system-logs', '/node-users', '/api-control'].some( | ||
| p => subPath.startsWith(p), | ||
| ) | ||
| } | ||
|
|
||
| // Liest Server-Status aus aria-label des ServerStatusBadge DOM-Elements. | ||
| // ServerStatusBadge rendert: <div role="img" aria-label="Online|Offline|..."> | ||
| // Panel-Texte sind z.B. "Server läuft" — "Server "-Präfix entfernt, da details | ||
| // bereits mit "Server: <Name>" beginnt (sonst "Server: X · Server läuft"). | ||
| function getServerStatus(): string | null { | ||
| const label = document.querySelector<HTMLElement>('div[role="img"][aria-label]') | ||
| ?.getAttribute('aria-label') | ||
| if (!label) | ||
| return null | ||
| return label.replace(/^Server\s+/i, '') | ||
| } | ||
|
|
||
| // Zählt Server-Rows im Node-Manager-Dashboard. | ||
| // ServerSplitterContainer: <div class="server-list-container"> → Kinder sind motion.div je Server | ||
| function getNodeServerCount(): number { | ||
| const container = document.querySelector('.server-list-container') | ||
| if (!container) | ||
| return 0 | ||
| return container.children.length | ||
| } | ||
|
|
||
| function getServerAction(subPath: string): string { | ||
| if (subPath === '/' || subPath === '') | ||
| return 'Verwaltet Konsole' | ||
| if (subPath.startsWith('/files/edit') || subPath.startsWith('/files/new')) | ||
| return 'Bearbeitet Dateien' | ||
| if (subPath.startsWith('/files')) | ||
| return 'Verwaltet Dateien' | ||
| if (subPath.startsWith('/databases')) | ||
| return 'Verwaltet Datenbanken' | ||
| if (subPath.startsWith('/backups')) | ||
| return 'Verwaltet Backups' | ||
| if (subPath.startsWith('/minecraft-plugins')) | ||
| return 'Installiert Plugins' | ||
| if (subPath.startsWith('/modpacks')) | ||
| return 'Durchsucht Modpacks' | ||
| if (subPath.startsWith('/players')) | ||
| return 'Verwaltet Spieler' | ||
| if (subPath.startsWith('/minecraft/bedrock-addons')) | ||
| return 'Verwaltet Bedrock-Addons' | ||
| if (subPath.startsWith('/minecraft/properties')) | ||
| return 'Bearbeitet Server-Einstellungen' | ||
| if (subPath.startsWith('/minecraft/versions')) | ||
| return 'Wechselt Server-Version' | ||
| if (subPath.startsWith('/bedrock-support')) | ||
| return 'Konfiguriert Bedrock' | ||
| if (subPath.startsWith('/network')) | ||
| return 'Verwaltet Port-Freigaben' | ||
| if (subPath.startsWith('/proxy')) | ||
| return 'Konfiguriert Proxy' | ||
| if (subPath.startsWith('/subdomain')) | ||
| return 'Konfiguriert Subdomains' | ||
| if (subPath.startsWith('/schedules/') && subPath.length > '/schedules/'.length) | ||
| return 'Bearbeitet Automatisierung' | ||
| if (subPath.startsWith('/schedules')) | ||
| return 'Verwaltet Automatisierungen' | ||
| if (subPath.startsWith('/users')) | ||
| return 'Verwaltet Zugänge' | ||
| if (subPath.startsWith('/startup')) | ||
| return 'Konfiguriert Startup' | ||
| if (subPath.startsWith('/settings')) | ||
| return 'Verwaltet Einstellungen' | ||
| if (subPath.startsWith('/Statistics')) | ||
| return 'Analysiert Statistiken' | ||
| if (subPath.startsWith('/activity')) | ||
| return 'Prüft Aktivitätslogs' | ||
| if (subPath.startsWith('/picoclaw')) | ||
| return 'Verwaltet PicoClaw' | ||
| return 'Im Panel' | ||
| } | ||
|
|
||
| function getNodeAction(subPath: string): string { | ||
| if (subPath === '/' || subPath === '') { | ||
| const count = getNodeServerCount() | ||
| return count > 0 ? `Verwaltet ${count} Server` : 'Verwaltet Node' | ||
| } | ||
| if (subPath.startsWith('/firewall')) | ||
| return 'Konfiguriert Firewall' | ||
| if (subPath.startsWith('/system-logs')) | ||
| return 'Liest System-Logs' | ||
| if (subPath.startsWith('/node-users')) | ||
| return 'Verwaltet Node-Nutzer' | ||
| if (subPath.startsWith('/api-control')) | ||
| return 'Verwaltet API-Keys' | ||
| return 'Verwaltet Node' | ||
| } | ||
|
|
||
| function getBotAction(subPath: string): string { | ||
| if (subPath === '/' || subPath === '') | ||
| return 'Bot-Übersicht' | ||
| if (subPath.startsWith('/knowledge')) | ||
| return 'Trainiert Wissensdatenbank' | ||
| if (subPath.startsWith('/blacklist')) | ||
| return 'Verwaltet Blacklist' | ||
| if (subPath.startsWith('/channels')) | ||
| return 'Konfiguriert Kanäle' | ||
| if (subPath.startsWith('/profile')) | ||
| return 'Bearbeitet Bot-Profil' | ||
| if (subPath.startsWith('/logs')) | ||
| return 'Liest Bot-Logs' | ||
| if (subPath.startsWith('/access')) | ||
| return 'Verwaltet Bot-Zugang' | ||
| if (subPath.startsWith('/activity')) | ||
| return 'Prüft Bot-Aktivität' | ||
| if (subPath.startsWith('/memory')) | ||
| return 'Verwaltet Bot-Memory' | ||
| if (subPath.startsWith('/settings')) | ||
| return 'Bot-Einstellungen' | ||
| return 'Verwaltet Bot' | ||
| } | ||
|
|
||
| function getAccountAction(pathname: string): string { | ||
| if (pathname.includes('/activity')) | ||
| return 'Account-Aktivität' | ||
| if (pathname.includes('/snippets')) | ||
| return 'Command Snippets' | ||
| if (pathname.includes('/data-export')) | ||
| return 'DSGVO-Datenauskunft' | ||
| return 'Account-Einstellungen' | ||
| } | ||
|
|
||
| presence.on('UpdateData', async () => { | ||
| const presenceData: PresenceData = { | ||
| largeImageKey: ActivityAssets.Logo, | ||
| buttons: [{ label: 'PVQ Panel öffnen', url: 'https://pv-q.de/auth/login' }], | ||
| } | ||
|
|
||
| const showServerName = await presence.getSetting<boolean>('showServerName') | ||
| const showStatus = await presence.getSetting<boolean>('showStatus') | ||
| const showElapsedTime = await presence.getSetting<boolean>('showElapsedTime') | ||
Check failureCode scanning / ESLint disallow unused capturing group Error
Capturing group number 1 is defined but never used.
|
||
|
Comment on lines
+184
to
+186
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Combine using Promise.all |
||
|
|
||
| if (showElapsedTime) { | ||
| presenceData.startTimestamp = startTimestamp | ||
| } | ||
|
|
||
| const { pathname } = document.location | ||
|
|
||
| const serverMatch = pathname.match(/^\/server\/([a-f0-9-]+)(\/.*)?$/i) | ||
|
|
||
|
|
||
| if (serverMatch) { | ||
| const subPath = serverMatch[2] || '/' | ||
| const serverName = getServerName() | ||
|
|
||
| if (isBotPath(subPath)) { | ||
| presenceData.details = showServerName && serverName | ||
| ? `Discord Bot: ${truncate(serverName, 40)}` | ||
| : 'Discord Bot' | ||
| presenceData.state = getBotAction(subPath) | ||
| } | ||
| else if (isNodeManagerPath(subPath) || (serverName !== null && isNodeServer(serverName))) { | ||
| presenceData.details = showServerName && serverName | ||
| ? `Node Manager: ${truncate(serverName, 40)}` | ||
| : 'Node Manager' | ||
| presenceData.state = getNodeAction(subPath) | ||
| } | ||
| else { | ||
| let details = showServerName && serverName | ||
| ? `Server: ${truncate(serverName, 40)}` | ||
| : 'Server' | ||
|
|
||
| if (showStatus && showServerName) { | ||
| const status = getServerStatus() | ||
| if (status) | ||
| details += ` · ${status}` | ||
| } | ||
|
|
||
| presenceData.details = details | ||
| presenceData.state = getServerAction(subPath) | ||
| } | ||
| } | ||
| else if (pathname.startsWith('/account')) { | ||
| presenceData.details = 'PVQ Panel' | ||
| presenceData.state = getAccountAction(pathname) | ||
| } | ||
| else { | ||
| presenceData.details = 'PVQ Panel' | ||
| presenceData.state = 'Dashboard' | ||
| } | ||
|
|
||
| presence.setActivity(presenceData) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.