HN 표시: HN 사용자의 나이/카르마를 표시하는 사용자 스크립트
hackernews
|
|
💼 비즈니스
#hn
#나이
#유저스크립트
#카르마
#팁
#hackernews
#ui
#사용자스크립트
원문 출처: hackernews · Genesis Park에서 요약 및 분석
요약
해커 뉴스 사용자들의 계정 생성일과 카르마 점수를 별도의 프로필 방문 없이 사용자명 옆에 바로 표시해주는 유저스크립트가 공개되었습니다. Tampermonkey를 통해 사용할 수 있는 이 작은 도구는 작성자가 엉망이라고 평가할 정도로 코드가 깔끔하지 않더라도 기능적으로는 정상적으로 작동합니다.
본문
- - Save m4chinations/f6d58711a94077d96cf4157665b0bab3 to your computer and use it in GitHub Desktop. hackernews account age and karma userscript This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters | // ==UserScript== | | | // @name HN User Info (Age & Karma) | | | // @namespace https://news.ycombinator.com/ | | | // @version 1.0 | | | // @description Shows account age (in days) and karma next to every username on Hacker News | | | // @match https://news.ycombinator.com/* | | | // @grant GM_xmlhttpRequest | | | // @grant GM.xmlHttpRequest | | | // @connect hacker-news.firebaseio.com | | | // ==/UserScript== | | | (function () { | | | 'use strict'; | | | // ── CONFIG ───────────────────────────────────────────────────────── | | | // Toggle which fields to show | | | const SHOW_AGE = true; | | | const SHOW_KARMA = true; | | | // Suffix strings (e.g. "d" → "5,055d", " days" → "5,055 days") | | | const AGE_SUFFIX = 'd'; | | | const KARMA_SUFFIX = 'k'; | | | // ────────────────────────────────────────────────────────────────── | | | // Cache fetched users so we don't re-fetch duplicates on the same page | | | const userCache = {}; | | | // Calculate age in days from a Unix timestamp | | | function ageDays(createdUnix) { | | | const now = Date.now() / 1000; | | | return Math.floor((now - createdUnix) / 86400); | | | } | | | // Create the badge span that will be inserted after the username | | | function makeBadge(username) { | | | const span = document.createElement('span'); | | | span.className = 'hn-userinfo-badge'; | | | span.style.cssText = 'color:#828282; font-size:inherit; opacity:0.5;'; | | | span.textContent = ' …'; | | | span.dataset.user = username; | | | return span; | | | } | | | // Format a number with commas (e.g. 13394 → "13,394") | | | function fmt(n) { | | | return n.toLocaleString('en-US'); | | | } | | | // Update every badge for a given username with the fetched data | | | function fillBadges(username, data) { | | | const days = ageDays(data.created); | | | const karma = data.karma; | | | const createdDate = new Date(data.created * 1000); | | | const dateStr = createdDate.toLocaleDateString('en-US', { | | | year: 'numeric', month: 'long', day: 'numeric' | | | }); | | | document.querySelectorAll(`.hn-userinfo-badge[data-user="${CSS.escape(username)}"]`).forEach(badge => { | | | badge.textContent = ''; | | | badge.style.opacity = '1'; | | | badge.style.transition = 'opacity 0.3s'; | | | if (SHOW_AGE) { | | | const ageLabel = `${fmt(days)}${AGE_SUFFIX}`; | | | const ageSpan = document.createElement('span'); | | | ageSpan.textContent = ` | ${ageLabel}`; | | | ageSpan.title = dateStr; | | | ageSpan.style.cssText = 'cursor:help; border-bottom:1px dotted #828282;'; | | | badge.appendChild(ageSpan); | | | } | | | if (SHOW_KARMA) { | | | const karmaLabel = `${fmt(karma)}${KARMA_SUFFIX}`; | | | badge.appendChild(document.createTextNode(` | ${karmaLabel}`)); | | | } | | | if (SHOW_AGE || SHOW_KARMA) { | | | badge.appendChild(document.createTextNode(' |')); | | | } | | | }); | | | } | | | // Fetch user info from the public HN Firebase API | | | function fetchUser(username) { | | | if (userCache[username]) return; // already fetched or in-flight | | | userCache[username] = true; // mark in-flight | | | const url = `https://hacker-news.firebaseio.com/v0/user/${encodeURIComponent(username)}.json`; | | | // Use GM_xmlhttpRequest if available (Greasemonkey / Tampermonkey), | | | // otherwise fall back to plain fetch (Violentmonkey, etc.) | | | const gmXHR = typeof GM_xmlhttpRequest === 'function' | | | ? GM_xmlhttpRequest | | | : (typeof GM !== 'undefined' && GM.xmlHttpRequest) | | | ? GM.xmlHttpRequest | | | : null; | | | if (gmXHR) { | | | gmXHR({ | | | method: 'GET', | | | url: url, | | | onload: function (resp) { | | | try { | | | const data = JSON.parse(resp.responseText); | | | if (data && data.created != null) { | | | userCache[username] = data; | | | fillBadges(username, data); | | | } | | | } catch (_) { /* silently ignore parse errors */ } | | | } | | | }); | | | } else { | | | fetch(url) | | | .then(r => r.json()) | | | .then(data => { | | | if (data && data.created != null) { | | | userCache[username] = data; | | | fillBadges(username, data); | | | } | | | }) | | | .catch(() => {}); | | | } | | | } | | | // Main: find all .hnuser links, inject badges, kick off fetches | | | function run() { | | | const userLinks = document.querySelectorAll('a.hnuser'); | | | const seen = new Set(); | | | userLinks.forEach(link => { | | | // Skip if we already processed this exact element | | | if (link.dataset.hnInfoDone) return; | | | link.dataset.hnInfoDone = '1'; | | | // Extract username from href like "user?id=fulafel" | | | const match = link.getAttribute('href')?.match(/user\?id=([^&]+)/); | | | if (!match)
Genesis Park 편집팀이 AI를 활용하여 작성한 분석입니다. 원문은 출처 링크를 통해 확인할 수 있습니다.
공유