Instantly share code, notes, and snippets.

Embed
What would you like to do?
Randomize Hacker News user script
// ==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