Last active
March 4, 2020 20:05
-
-
Save mbrubeck/52126a1d9123694754d5 to your computer and use it in GitHub Desktop.
Randomize Hacker News user script
This file contains 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 Randomize Hacker News | |
// @namespace http://limpet.net/mbrubeck/ | |
// @description Promote random new or low-ranked stories to the Hacker News front page. | |
// @include http://news.ycombinator.com/ | |
// @include http://news.ycombinator.com/news | |
// @include https://news.ycombinator.com/ | |
// @include https://news.ycombinator.com/news | |
// ==/UserScript== | |
// Greasemonkey users: Click the "Raw" button above to install this user script from gist.github.com. | |
// Chrome users: Right-click the "Raw" button and choose "Save as...", | |
// then drag the saved file to Chrome's Extensions page to install it. | |
(function() { | |
"use strict"; | |
// Search for a random "top" story, and a random "new" story. | |
function load() { | |
get("topstories", function(result) { load_from(result.slice(30)); }); | |
get("updates", function(result) { load_from(result.items); }); | |
} | |
// Load and insert a random story from an array of IDs. | |
function load_from(ids) { | |
get("item/"+choose(ids), insert); | |
} | |
// Request a resource from the official Hacker News API. | |
function get(query, callback) { | |
var api = "https://hacker-news.firebaseio.com/v0/"; | |
var req = new XMLHttpRequest(); | |
req.open("get", api + query + ".json", true); | |
req.onload = function() { | |
callback(JSON.parse(this.responseText)); | |
}; | |
req.send(); | |
} | |
// Add a new story to the list of stories on the front page. | |
// NOTE: This depends on the exact structure of the HTML used on Hacker News. | |
// It's likely to break whenever the site design changes. | |
function insert(item) { | |
// If we ended up with a comment, ignore it. | |
// (The "updates" API doesn't have a way to filter by type.) | |
if (item.type != "story") | |
return; | |
// Skip items that are high-scoring but old. | |
if (item.score > 100) | |
return; | |
// FIXME: Filter out links that are already on the front page. | |
console.log("Inserting random story "+item.title); | |
// Clone the three TR elements from an existing story. | |
var frag = document.createDocumentFragment(); | |
var row = document.querySelector(".title").parentNode; | |
for (var i = 0; i < 3; i++) { | |
frag.appendChild(row.cloneNode(true)); | |
row = row.nextSibling; | |
} | |
// Row 0. | |
var comment_url = "https://news.ycombinator.com/item?id=" + item.id; | |
var vote = frag.children[0].children[1].querySelector("a"); | |
// FIXME: Can't generate a valid upvote link. | |
vote.href = comment_url; | |
vote.onclick = ""; | |
var title = frag.children[0].children[2]; | |
title.innerHTML = ""; | |
var link = document.createElement("a"); | |
link.href = item.url || comment_url; | |
link.textContent = item.title; | |
title.appendChild(link); | |
if (item.url) { | |
var domain = RegExp("//([^/]*)").exec(item.url)[1]; | |
var dspan = document.createElement("span"); | |
dspan.className = "comHead"; | |
dspan.textContent = " (" + domain + ")"; | |
title.appendChild(dspan); | |
} | |
// Row 1. | |
var subtext = frag.querySelector(".subtext"); | |
// FIXME: Use better time formatting. | |
var hours = Math.floor((Date.now()/1000 - item.time) / 3600); | |
// FIXME: Link to author's profile page. | |
subtext.innerHTML = item.score + " points by " + item.by + " " + hours + " hours ago | "; | |
var comments = document.createElement("a"); | |
comments.href = comment_url; | |
// FIXME: Comment count is wrong (includes only top-level comments). | |
comments.textContent = item.kids ? item.kids.length + " comments" : "comments"; | |
subtext.appendChild(comments); | |
// Insert the story into the story list. | |
var random_row = choose(document.querySelectorAll(".title")).parentNode; | |
random_row.parentNode.insertBefore(frag, random_row); | |
renumber(); | |
} | |
// Recalculate all the row numbers. | |
function renumber() { | |
var numbers = document.querySelectorAll(".title:first-child"); | |
for (var i = 0; i < numbers.length; i++) { | |
numbers[i].textContent = (i + 1) + "."; | |
} | |
} | |
// Return a randomly-chosen element from an array. | |
function choose(v) { | |
return v[Math.floor(Math.random() * v.length)]; | |
} | |
load(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment