| <!DOCTYPE html> |
| <html lang="de"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>NoahsChat β Member Count</title> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap'); |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| body { |
| background: #0a0e14; |
| color: #e2e8f0; |
| font-family: Orbitron, monospace; |
| min-height: 100vh; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| .container { |
| text-align: center; |
| padding: 40px; |
| } |
| .title { |
| font-size: .7rem; |
| color: #4a5568; |
| letter-spacing: .3em; |
| margin-bottom: 30px; |
| } |
| .logo { |
| font-size: 1.6rem; |
| font-weight: 900; |
| color: #00d4ff; |
| letter-spacing: .2em; |
| margin-bottom: 40px; |
| } |
| .stats { |
| display: flex; |
| gap: 30px; |
| justify-content: center; |
| flex-wrap: wrap; |
| } |
| .stat-box { |
| background: #0d1117; |
| border: 1px solid #1e2a3a; |
| border-radius: 8px; |
| padding: 24px 36px; |
| min-width: 140px; |
| } |
| .stat-label { |
| font-size: .55rem; |
| color: #4a5568; |
| letter-spacing: .15em; |
| margin-bottom: 10px; |
| } |
| .stat-value { |
| font-size: 2.4rem; |
| font-weight: 700; |
| } |
| .stat-value.members { color: #00d4ff; } |
| .stat-value.online { color: #4eff91; } |
| .stat-value.visitors { color: #a855f7; } |
| .updated { |
| margin-top: 30px; |
| font-size: .55rem; |
| color: #2d3748; |
| letter-spacing: .1em; |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="container"> |
| <div class="logo">NoahsChat</div> |
| <div class="title">SERVER STATISTIKEN</div> |
| <div class="stats"> |
| <div class="stat-box"> |
| <div class="stat-label">MITGLIEDER</div> |
| <div class="stat-value members" id="count-members">β</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">ONLINE</div> |
| <div class="stat-value online" id="count-online">β</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">UNIQUE BESUCHER</div> |
| <div class="stat-value visitors" id="count-visitors">β</div> |
| </div> |
| </div> |
| <div class="updated" id="last-updated">WIRD GELADEN...</div> |
| </div> |
|
|
| <script> |
| const JSONBIN_KEY = '$2a$10$Hurr28g4Cy7NWpA/Abd6YOzkBLuC8PdAOPQR34g4pEkA24LpXo7NK'; |
| const JSONBIN_BIN = '69976e43ae596e708f382b97'; |
| |
| |
| function getFingerprint() { |
| const s = navigator.userAgent + screen.width + screen.height + screen.colorDepth |
| + (Intl.DateTimeFormat().resolvedOptions().timeZone || '') + navigator.language; |
| let h = 0; |
| for (let i = 0; i < s.length; i++) { h = ((h << 5) - h) + s.charCodeAt(i); h = h & h; } |
| return Math.abs(h).toString(36); |
| } |
| const MY_FP = getFingerprint(); |
| |
| async function loadAndTrack() { |
| try { |
| const r = await fetch(`https://api.jsonbin.io/v3/b/${JSONBIN_BIN}/latest`, { |
| headers: { 'X-Master-Key': JSONBIN_KEY, 'X-Bin-Meta': 'false' } |
| }); |
| const DB = await r.json(); |
| |
| |
| if (!DB.visitors) DB.visitors = {}; |
| const isNew = !DB.visitors[MY_FP]; |
| if (isNew) { |
| DB.visitors[MY_FP] = { ts: Date.now() }; |
| await fetch(`https://api.jsonbin.io/v3/b/${JSONBIN_BIN}`, { |
| method: 'PUT', |
| headers: { 'Content-Type': 'application/json', 'X-Master-Key': JSONBIN_KEY, 'X-Bin-Versioning': 'false' }, |
| body: JSON.stringify(DB) |
| }); |
| } |
| |
| |
| document.getElementById('count-members').textContent = Object.keys(DB.users || {}).length; |
| document.getElementById('count-visitors').textContent = Object.keys(DB.visitors || {}).length; |
| |
| const now = Date.now(); |
| const online = Object.values(DB.online || {}).filter(ts => now - ts < 12000).length; |
| document.getElementById('count-online').textContent = online; |
| |
| const time = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); |
| document.getElementById('last-updated').textContent = 'ZULETZT AKTUALISIERT: ' + time; |
| |
| } catch(e) { |
| document.getElementById('last-updated').textContent = 'FEHLER BEIM LADEN'; |
| } |
| } |
| |
| loadAndTrack(); |
| setInterval(loadAndTrack, 10000); |
| </script> |
|
|
| </body> |
| </html> |
|
|