RedditRaffle

Choses a list of winners by certain criteria

当前为 2018-10-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name RedditRaffle
  3. // @namespace redditraffle
  4. // @description Choses a list of winners by certain criteria
  5. // @include http://www.reddit.com/r/*/comments/*
  6. // @include https://www.reddit.com/r/*/comments/*
  7. // @include http://old.reddit.com/r/*/comments/*
  8. // @include https://old.reddit.com/r/*/comments/*
  9. // @include http://new.reddit.com/r/*/comments/*
  10. // @include https://new.reddit.com/r/*/comments/*
  11. // @version 1.7
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. var link,
  16. url,
  17. winner_count,
  18. author_exclude,
  19. keyword_included,
  20. keyword_excluded,
  21. karma_link,
  22. karma_comment,
  23. age = 0.,
  24. total_comments,
  25. comments = [],
  26. more = [],
  27. more_lock = false,
  28. parse_lock = false,
  29. names = [],
  30. chosen = [],
  31. userinfo,
  32. query_timeout,
  33. connection_count = 0,
  34. url_base = new URL(window.location.href).origin;
  35.  
  36. function render_results() {
  37. progress.innerHTML = "Rendering:";
  38. var html = [];
  39. html.push("<!doctype html><meta charset=\"utf-8\" /><title>Raffle results</title>" +
  40. "<style>table{overflow:hidden;border:1px solid #d3d3d3;background:#fefefe;" +
  41. "width:70%;margin:5% auto 0;-moz-border-radius:5px;-webkit-border-radius:5px;" +
  42. "border-radius:5px;-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);" +
  43. "-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);}th,td{text-align:center;}" +
  44. "th{padding:14px 22px;text-shadow: 1px 1px 1px #fff;background:#e8eaeb;}" +
  45. "td{border-top:1px solid #e0e0e0;border-right:1px solid #e0e0e0;}" +
  46. "tr:nth-child(odd) td{background:#f6f6f6;}td:last-child{border-right:none;" +
  47. "text-align:left;padding:8px 18px;}</style><table><tr><th>No.</th><th>User</th>" +
  48. "<th>Comment</th></tr>");
  49. var winner_length = Math.min(winner_count, chosen.length);
  50. for(var i = 0; i < winner_length; i++) {
  51. html.push("<tr><td>" + (i + 1) + ".</td><td><a href=\"" + url_base + "/user/" +
  52. chosen[i].author + "\">" + chosen[i].author + "</a></td><td>");
  53. html.push(chosen[i].body_html.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&'));
  54. html.push("<br /><a href=\"" + url + chosen[i].id + "\">Show</a></td><tr>");
  55. }
  56. html.push("</table>");
  57. progress.innerHTML = "<a href=\"" + URL.createObjectURL(new Blob(html, {type : 'text/html'})) +
  58. "\" target=\"_new\">Show results</a>";
  59. }
  60.  
  61. function query_users() {
  62. if(more_lock || parse_lock) return;
  63. var query_interval = window.setInterval(function() {
  64. if(chosen.length >= winner_count || !comments.length) {
  65. window.clearInterval(query_interval);
  66. render_results();
  67. return;
  68. }
  69. if(connection_count > 10) return;
  70. var comment = comments.splice(Math.floor(Math.random() * comments.length), 1)[0];
  71. if(comment === undefined ||
  72. comment.body.toLowerCase().indexOf(keyword_included) < 0 ||
  73. (keyword_excluded && !(comment.body.toLowerCase().indexOf(keyword_excluded) < 0)) ||
  74. (author_exclude && comment.author === author_exclude) ||
  75. !(names.indexOf(comment.author) < 0))
  76. return;
  77. if(age || karma_link || karma_comment) {
  78. var request = new XMLHttpRequest();
  79. request.onreadystatechange = function() {
  80. if (this.readyState == 4) {
  81. if(this.status == 200) {
  82. connection_count--;
  83. userinfo = JSON.parse(this.responseText).data;
  84. if((!age || userinfo.created_utc <= age) &&
  85. userinfo.link_karma >= karma_link &&
  86. userinfo.comment_karma >= karma_comment &&
  87. chosen.length < winner_count &&
  88. names.indexOf(userinfo.name) < 0) {
  89. names.push(userinfo.name);
  90. chosen.push(comment);
  91. progress.innerHTML = "Querying Users: " + chosen.length + "/" + winner_count;
  92. }
  93. } else {
  94. connection_count--;
  95. console.log("Server returned status " + this.status);
  96. }
  97. }
  98. };
  99. request.open('GET', url_base + '/user/' + comment.author + '/about.json', true);
  100. request.send();
  101. connection_count++;
  102. } else {
  103. names.push(comment.author);
  104. chosen.push(comment);
  105. progress.innerHTML = "Querying Users: " + chosen.length + "/" + winner_count;
  106. }
  107. }, 100);
  108. }
  109.  
  110. function add_comment(comment) {
  111. comments.push(comment);
  112. if(comment.replies && comment.replies.kind === "Listing")
  113. parse_listing(comment.replies.data.children);
  114. }
  115.  
  116. function add_more(children) {
  117. more.push.apply(more, children);
  118. if(!more_lock) parse_more();
  119. }
  120.  
  121. function parse_more() {
  122. if(!more.length) {
  123. more_lock = false;
  124. query_users();
  125. return;
  126. }
  127. more_lock = true;
  128. var children = more.splice(0, 20);
  129. var request = new XMLHttpRequest();
  130. request.onreadystatechange = function() {
  131. if (this.readyState == 4) {
  132. if(this.status == 200) {
  133. parse_listing(JSON.parse(this.responseText).json.data.things);
  134. progress.innerHTML = "Parsing Comments: " + comments.length + "/" + total_comments;
  135. parse_more();
  136. } else {
  137. more.push.apply(more, children);
  138. console.log("Server returned status " + this.status);
  139. parse_more();
  140. }
  141. }
  142. };
  143. request.open('GET', url_base + '/api/morechildren.json?link_id=' + link + '&api_type=json&children=' + children.join(), true);
  144. request.send();
  145. }
  146.  
  147. function parse_listing(listing) {
  148. var listingLength = listing.length;
  149. for(var child = 0; child < listingLength; child++) {
  150. if (listing[child].kind === 't1')
  151. add_comment(listing[child].data);
  152. else if (listing[child].kind === 't3') {
  153. link = listing[child].data.name;
  154. author_exclude = form.elements.author_exclude.checked ? listing[child].data.author : "";
  155. total_comments = listing[child].data.num_comments;
  156. progress.innerHTML = "Parsing Comments: 0/" + total_comments;
  157. }
  158. else if (listing[child].kind === 'more')
  159. add_more(listing[child].data.children);
  160. }
  161. }
  162.  
  163. function do_raffle() {
  164. progress.innerHTML = "Parsing Comments:";
  165. winner_count = parseInt(form.elements.winner_count.value) || 20;
  166. keyword_included = form.elements.keyword_included.value.toLowerCase();
  167. keyword_excluded = form.elements.keyword_excluded.value.toLowerCase();
  168. karma_link = parseInt(form.elements.karma_link.value) || 0;
  169. karma_comment = parseInt(form.elements.karma_comment.value) || 0;
  170. if(form.elements.age.value) {
  171. var date = new Date(),
  172. number = parseInt(form.elements.age.value) || 0;
  173. switch(form.elements.age_type.value) {
  174. case "y":
  175. date.setFullYear(date.getFullYear() - number);
  176. break;
  177. case "d":
  178. date.setDate(date.getDate() - number);
  179. break;
  180. default:
  181. date.setMonth(date.getMonth() - number);
  182. }
  183. age = date.getTime()/1000;
  184. }
  185. url = window.location.href;
  186. if(!(url.indexOf("?") < 0)) {
  187. url = url.substring(0, url.indexOf("?"));
  188. }
  189. parse_lock = true;
  190. var request = new XMLHttpRequest();
  191. request.onreadystatechange = function() {
  192. if (this.readyState == 4) {
  193. if(this.status == 200) {
  194. var things = JSON.parse(this.responseText),
  195. thingsLength = things.length;
  196. for(var thing = 0; thing < thingsLength; thing++)
  197. if (things[thing].kind === 'Listing')
  198. parse_listing(things[thing].data.children);
  199. parse_lock = false;
  200. query_users();
  201. } else {
  202. console.log("Server returned status " + this.status);
  203. }
  204. }
  205. };
  206. request.open('GET', url + '.json?limit=500', true);
  207. request.send();
  208. }
  209.  
  210.  
  211. var style = document.createElement('style');
  212. style.type = 'text/css';
  213. document.head.appendChild(style);
  214. style.sheet.insertRule("#raffle_form input[type=text] {\
  215. width:265px;\
  216. border:1px solid #DBDADA;\
  217. border-radius:2px;\
  218. font-size:14px;\
  219. font-style:italic;\
  220. padding:4px;\
  221. }", 0);
  222. style.sheet.insertRule("#raffle_form button {\
  223. border:1px solid #DBDADA;\
  224. border-radius:2px;\
  225. font-size:14px;\
  226. font-style:italic;\
  227. padding:4px;\
  228. margin:4px;\
  229. background:#EBEBEB;\
  230. color:#141414;\
  231. }", 1);
  232. style.sheet.insertRule("#raffle_form a {\
  233. color:blue;\
  234. text-decoration:underline;\
  235. }", 2);
  236.  
  237.  
  238. var form = document.createElement("form");
  239. form.id = "raffle_form";
  240. form.innerHTML = "<ul class=\"content\">\
  241. <li><input type=\"text\" placeholder=\"Number of Winners\" name=\"winner_count\" /></li>\
  242. <li><input type=\"text\" placeholder=\"Comments containing\" name=\"keyword_included\" /></li>\
  243. <li><input type=\"text\" placeholder=\"Comments not containing\" name=\"keyword_excluded\" /></li>\
  244. <li><input type=\"text\" placeholder=\"Required Link Karma\" name=\"karma_link\" /></li>\
  245. <li><input type=\"text\" placeholder=\"Required Comment Karma\" name=\"karma_comment\" /></li>\
  246. <li>\
  247. <input type=\"text\" placeholder=\"Age\" name=\"age\" style=\"width: 190px;\"></input>\
  248. <select name=\"age_type\">\
  249. <option value=\"d\">days</option>\
  250. <option value=\"m\" selected=\"selected\">month</option>\
  251. <option value=\"y\">years</option>\
  252. </select>\
  253. </li>\
  254. <li>\
  255. <button class=\"save\" type=\"button\">Raffle</button>\
  256. <input type=\"checkbox\" name=\"author_exclude\" id=\"author_exclude\" checked=\"checked\"/>\
  257. <label for=\"author_exclude\">Exclude Author</label>\
  258. </li>\
  259. <li><span id=\"raffle_progress\"></span></li>\
  260. </ul>";
  261.  
  262. var progress = form.querySelector("#raffle_progress");
  263. form.querySelector("button").addEventListener("click", do_raffle, true);
  264.  
  265. // old design
  266. if(document.querySelector("div.side")) {
  267. var spacer = document.createElement("div"),
  268. sidecontentbox = document.createElement("div");
  269. spacer.classList.add("spacer");
  270. sidecontentbox.classList.add("sidecontentbox");
  271. spacer.appendChild(sidecontentbox);
  272. sidecontentbox.innerHTML = "<div class=\"title\"><h1>Raffle</h1></div>";
  273. sidecontentbox.appendChild(form);
  274. document.querySelector("div.side").appendChild(spacer);
  275. }
  276.  
  277. // new design
  278. else {
  279. var template_container = document.evaluate("//div[text()=\"Community Details\"]/..", document, null, XPathResult.ANY_TYPE, null).iterateNext(),
  280. sidebar = template_container.parentNode,
  281. container = document.createElement("div"),
  282. title = document.createElement("div"),
  283. content = document.createElement("div");
  284. container.classList = template_container.classList;
  285. container.appendChild(title);
  286. container.appendChild(content);
  287. title.classList = template_container.children[0].classList;
  288. title.innerHTML = "Raffle";
  289. content.classList = template_container.children[1].classList;
  290. content.appendChild(form);
  291. setTimeout(() => {sidebar.insertBefore(container, sidebar.children[2]);}, 400);
  292. }