RedditRaffle

Choses a list of winners by certain criteria

// ==UserScript==
// @name        RedditRaffle
// @namespace   redditraffle
// @description Choses a list of winners by certain criteria
// @include     http://www.reddit.com/r/*/comments/*
// @include     https://www.reddit.com/r/*/comments/*
// @include     http://old.reddit.com/r/*/comments/*
// @include     https://old.reddit.com/r/*/comments/*
// @include     http://new.reddit.com/r/*/comments/*
// @include     https://new.reddit.com/r/*/comments/*
// @version     1.7
// @grant       none
// ==/UserScript==

var link,
    url,
    winner_count,
    author_exclude,
    keyword_included,
    keyword_excluded,
    karma_link,
    karma_comment,
    age = 0.,
    total_comments,
    comments = [],
    more = [],
    more_lock = false,
    parse_lock = false,
    names = [],
    chosen = [],
    userinfo,
    query_timeout,
    connection_count = 0,
    url_base = new URL(window.location.href).origin;

function render_results() {
    progress.innerHTML = "Rendering:";
    var html = [];
    html.push("<!doctype html><meta charset=\"utf-8\" /><title>Raffle results</title>" +
              "<style>table{overflow:hidden;border:1px solid #d3d3d3;background:#fefefe;" +
              "width:70%;margin:5% auto 0;-moz-border-radius:5px;-webkit-border-radius:5px;" +
              "border-radius:5px;-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);" +
              "-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);}th,td{text-align:center;}" +
              "th{padding:14px 22px;text-shadow: 1px 1px 1px #fff;background:#e8eaeb;}" +
              "td{border-top:1px solid #e0e0e0;border-right:1px solid #e0e0e0;}" +
              "tr:nth-child(odd) td{background:#f6f6f6;}td:last-child{border-right:none;" +
              "text-align:left;padding:8px 18px;}</style><table><tr><th>No.</th><th>User</th>" +
              "<th>Comment</th></tr>");
    var winner_length = Math.min(winner_count, chosen.length);
    for(var i = 0; i < winner_length; i++) {
        html.push("<tr><td>" + (i + 1) + ".</td><td><a href=\"" + url_base + "/user/" +
                  chosen[i].author + "\">" + chosen[i].author + "</a></td><td>");
        html.push(chosen[i].body_html.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&'));
        html.push("<br /><a href=\"" + url + chosen[i].id + "\">Show</a></td><tr>");
    }
    html.push("</table>");
    progress.innerHTML = "<a href=\"" + URL.createObjectURL(new Blob(html, {type : 'text/html'})) +
                         "\" target=\"_new\">Show results</a>";
}

function query_users() {
    if(more_lock || parse_lock) return;
    var query_interval = window.setInterval(function() {
        if(chosen.length >= winner_count || !comments.length) {
            window.clearInterval(query_interval);
            render_results();
            return;
        }
        if(connection_count > 10) return;
        var comment = comments.splice(Math.floor(Math.random() * comments.length), 1)[0];
        if(comment === undefined ||
           comment.body.toLowerCase().indexOf(keyword_included) < 0 ||
           (keyword_excluded && !(comment.body.toLowerCase().indexOf(keyword_excluded) < 0)) ||
           (author_exclude && comment.author === author_exclude) ||
           !(names.indexOf(comment.author) < 0))
            return;
        if(age || karma_link || karma_comment) {
            var request = new XMLHttpRequest();
            request.onreadystatechange = function() {
                if (this.readyState == 4) {
                    if(this.status == 200) {
                        connection_count--;
                        userinfo = JSON.parse(this.responseText).data;
                        if((!age || userinfo.created_utc <= age) &&
                           userinfo.link_karma >= karma_link &&
                           userinfo.comment_karma >= karma_comment &&
                           chosen.length < winner_count &&
                           names.indexOf(userinfo.name) < 0) {
                            names.push(userinfo.name);
                            chosen.push(comment);
                            progress.innerHTML = "Querying Users: " + chosen.length + "/" + winner_count;
                        }
                    } else {
                        connection_count--;
                				console.log("Server returned status " + this.status);
                    }
                }
            };
    				request.open('GET', url_base + '/user/' + comment.author + '/about.json', true);
    				request.send();
            connection_count++;
        } else {
            names.push(comment.author);
            chosen.push(comment);
            progress.innerHTML = "Querying Users: " + chosen.length + "/" + winner_count;
        }
    }, 100);
}

function add_comment(comment) {
    comments.push(comment);
    if(comment.replies && comment.replies.kind === "Listing")
        parse_listing(comment.replies.data.children);
}

function add_more(children) {
    more.push.apply(more, children);
    if(!more_lock) parse_more();
}

function parse_more() {
    if(!more.length) {
        more_lock = false;
        query_users();
        return;
    }
    more_lock = true;
    var children = more.splice(0, 20);
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (this.readyState == 4) {
            if(this.status == 200) {
                parse_listing(JSON.parse(this.responseText).json.data.things);
                progress.innerHTML = "Parsing Comments: " + comments.length + "/" + total_comments;
                parse_more();
            } else {
                more.push.apply(more, children);
                console.log("Server returned status " + this.status);
                parse_more();
            }
        }
    };
    request.open('GET', url_base + '/api/morechildren.json?link_id=' + link + '&api_type=json&children=' + children.join(), true);
    request.send();
}

function parse_listing(listing) {
    var listingLength = listing.length;
    for(var child = 0; child < listingLength; child++) {
        if (listing[child].kind === 't1')
            add_comment(listing[child].data);
        else if (listing[child].kind === 't3') {
            link = listing[child].data.name;
            author_exclude = form.elements.author_exclude.checked ? listing[child].data.author : "";
            total_comments = listing[child].data.num_comments;
            progress.innerHTML = "Parsing Comments: 0/" + total_comments;
        }
        else if (listing[child].kind === 'more')
            add_more(listing[child].data.children);
    }
}

function do_raffle() {
    progress.innerHTML = "Parsing Comments:";
    winner_count = parseInt(form.elements.winner_count.value) || 20;
    keyword_included = form.elements.keyword_included.value.toLowerCase();
    keyword_excluded = form.elements.keyword_excluded.value.toLowerCase();
    karma_link = parseInt(form.elements.karma_link.value) || 0;
    karma_comment = parseInt(form.elements.karma_comment.value) || 0;
    if(form.elements.age.value) {
        var date = new Date(),
            number = parseInt(form.elements.age.value) || 0;
        switch(form.elements.age_type.value) {
            case "y":
                date.setFullYear(date.getFullYear() - number);
                break;
            case "d":
                date.setDate(date.getDate() - number);
                break;
            default:
                date.setMonth(date.getMonth() - number);
        }
        age = date.getTime()/1000;
    }
    url = window.location.href;
    if(!(url.indexOf("?") < 0)) {
        url = url.substring(0, url.indexOf("?"));
    }
    parse_lock = true;
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (this.readyState == 4) {
            if(this.status == 200) {
                var things = JSON.parse(this.responseText),
                    thingsLength = things.length;
                for(var thing = 0; thing < thingsLength; thing++)
                    if (things[thing].kind === 'Listing')
                        parse_listing(things[thing].data.children);
                parse_lock = false;
                query_users();
            } else {
                console.log("Server returned status " + this.status);
            }
        }
    };
    request.open('GET', url + '.json?limit=500', true);
    request.send();
}


var style = document.createElement('style');
style.type = 'text/css';
document.head.appendChild(style);
style.sheet.insertRule("#raffle_form input[type=text] {\
    width:265px;\
    border:1px solid #DBDADA;\
    border-radius:2px;\
    font-size:14px;\
    font-style:italic;\
    padding:4px;\
}", 0);
style.sheet.insertRule("#raffle_form button {\
    border:1px solid #DBDADA;\
    border-radius:2px;\
    font-size:14px;\
    font-style:italic;\
    padding:4px;\
    margin:4px;\
    background:#EBEBEB;\
    color:#141414;\
}", 1);
style.sheet.insertRule("#raffle_form a {\
    color:blue;\
    text-decoration:underline;\
}", 2);


var form = document.createElement("form");
form.id = "raffle_form";
form.innerHTML = "<ul class=\"content\">\
<li><input type=\"text\" placeholder=\"Number of Winners\" name=\"winner_count\" /></li>\
<li><input type=\"text\" placeholder=\"Comments containing\" name=\"keyword_included\" /></li>\
<li><input type=\"text\" placeholder=\"Comments not containing\" name=\"keyword_excluded\" /></li>\
<li><input type=\"text\" placeholder=\"Required Link Karma\" name=\"karma_link\" /></li>\
<li><input type=\"text\" placeholder=\"Required Comment Karma\" name=\"karma_comment\" /></li>\
<li>\
<input type=\"text\" placeholder=\"Age\" name=\"age\" style=\"width: 190px;\"></input>\
<select name=\"age_type\">\
<option value=\"d\">days</option>\
<option value=\"m\" selected=\"selected\">month</option>\
<option value=\"y\">years</option>\
</select>\
</li>\
<li>\
<button class=\"save\" type=\"button\">Raffle</button>\
<input type=\"checkbox\" name=\"author_exclude\" id=\"author_exclude\" checked=\"checked\"/>\
<label for=\"author_exclude\">Exclude Author</label>\
</li>\
<li><span id=\"raffle_progress\"></span></li>\
</ul>";

var progress = form.querySelector("#raffle_progress");
form.querySelector("button").addEventListener("click", do_raffle, true);

// old design
if(document.querySelector("div.side")) {
    var spacer = document.createElement("div"),
        sidecontentbox = document.createElement("div");
    spacer.classList.add("spacer");
    sidecontentbox.classList.add("sidecontentbox");
    spacer.appendChild(sidecontentbox);
    sidecontentbox.innerHTML = "<div class=\"title\"><h1>Raffle</h1></div>";
    sidecontentbox.appendChild(form);
    document.querySelector("div.side").appendChild(spacer);
}

// new design
else {
    var template_container = document.evaluate("//div[text()=\"Community Details\"]/..", document, null, XPathResult.ANY_TYPE, null).iterateNext(),
        sidebar = template_container.parentNode,
        container = document.createElement("div"),
        title = document.createElement("div"),
        content = document.createElement("div");
    container.classList = template_container.classList;
    container.appendChild(title);
    container.appendChild(content);
    title.classList = template_container.children[0].classList;
    title.innerHTML = "Raffle";
    content.classList = template_container.children[1].classList;
    content.appendChild(form);
    setTimeout(() => {sidebar.insertBefore(container, sidebar.children[2]);}, 400);
}