YARCO.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // ==UserScript==
  2. // @name Yet Another Reddit Comment Overwriter
  3. // @namespace https://github.com/adriantache/YARCO/
  4. // @description Local script to overwrite all your comments with random ASCII characters and delete them. This works because Reddit doesn't store editing history, so technically this is the only way to obfuscate the contents of the comments. Based on Reddit Overwrite script v.1.4.8.
  5. // @include https://*.reddit.com/user/*
  6. // @include http://*.reddit.com/user/*
  7. // @version 0.1
  8. // @run-at document-start
  9. // ==/UserScript==
  10. // TODO separate overwrite and delete
  11. // TODO only delete comments older than a day
  12. // TODO only delete comments from a certain subreddit
  13. // TODO check feedback for Reddit Overwrite for extra features
  14. // TODO consider caching comments array
  15. // TODO test Overview page
  16. // TODO add STOP button
  17. // TODO add optional confirmation dialog
  18. // reddit username
  19. unsafeWindow.user = '';
  20. // array of comments (more precisely author tags)
  21. unsafeWindow.comments = [];
  22. // number of detected user comments
  23. // TODO refactor this out
  24. unsafeWindow.num_user_comments = 0;
  25. // top section contents
  26. unsafeWindow.span = '';
  27. //TODO refactor these out
  28. unsafeWindow.to_delete = [];
  29. unsafeWindow.deleted = 0;
  30. //EXTRA OPTIONS
  31. let generate_individual_delete_buttons = false //disabled by default
  32. let time_between_actions = 2000 //reddit API limit is 60 actions per minute so don't exceed that
  33. // on page loaded, initialize the script
  34. window.addEventListener("DOMContentLoaded", init_script, false);
  35. function init_script(ev) {
  36. // get logged in username
  37. unsafeWindow.user = document.querySelector("span.user > a:not(.login-required)").innerHTML;
  38. // if not logged in exit
  39. if (!unsafeWindow.user) return;
  40. //retrieve all VISIBLE comments
  41. get_comments()
  42. // generate the top buttons
  43. generate_top_buttons();
  44. }
  45. function get_comments() {
  46. // find all author tags to eventually get comments
  47. let comments = document.querySelectorAll("a.author");
  48. // filter out other authors
  49. unsafeWindow.comments = [].filter.call(comments, filter_author)
  50. unsafeWindow.num_user_comments = unsafeWindow.comments.length;
  51. }
  52. // append buttons to page
  53. function generate_top_buttons() {
  54. if (unsafeWindow.num_user_comments) {
  55. unsafeWindow.span = document.createElement("div");
  56. unsafeWindow.span.setAttribute('class', 'nextprev secure_delete_all');
  57. unsafeWindow.span.innerHTML = "";
  58. unsafeWindow.span.style.marginBottom = "10px";
  59. // make Overwrite and Delete All link
  60. let odlink = document.createElement("a");
  61. odlink.setAttribute('class', 'bylink');
  62. odlink.setAttribute('onClick', 'javascript:return recursive_process(true, true)');
  63. odlink.setAttribute('href', 'javascript:void(0)');
  64. odlink.style.marginLeft = "10px";
  65. odlink.appendChild(document.createTextNode('OVERWRITE AND DELETE ' +
  66. unsafeWindow.comments.length +
  67. ' COMMENTS'));
  68. unsafeWindow.span.appendChild(odlink);
  69. let br = document.createElement("br");
  70. unsafeWindow.span.appendChild(br);
  71. // make Overwrite All link
  72. let olink = document.createElement("a");
  73. olink.setAttribute('class', 'bylink');
  74. olink.setAttribute('onClick', 'javascript:return recursive_process(true, false)');
  75. olink.setAttribute('href', 'javascript:void(0)');
  76. olink.style.marginLeft = "10px";
  77. olink.style.position = "relative";
  78. olink.style.top = "5px";
  79. olink.appendChild(document.createTextNode('OVERWRITE ' +
  80. unsafeWindow.comments.length +
  81. ' COMMENTS'));
  82. unsafeWindow.span.appendChild(olink);
  83. let br2 = document.createElement("br");
  84. unsafeWindow.span.appendChild(br2);
  85. // make Delete All link
  86. let dlink = document.createElement("a");
  87. dlink.setAttribute('class', 'bylink');
  88. dlink.setAttribute('onClick', 'javascript:return recursive_process(false, true)');
  89. dlink.setAttribute('href', 'javascript:void(0)');
  90. dlink.style.marginLeft = "10px";
  91. dlink.style.position = "relative";
  92. dlink.style.top = "10px";
  93. dlink.appendChild(document.createTextNode('DELETE ' +
  94. unsafeWindow.comments.length +
  95. ' COMMENTS'));
  96. unsafeWindow.span.appendChild(dlink);
  97. // TODO test status message
  98. // let status_message = document.createTextNode("p");
  99. // status_message.innerHTML = "STATUS";
  100. // status_message.style.marginLeft = "10px";
  101. // status_message.style.position = "relative";
  102. // status_message.style.top = "10px";
  103. // unsafeWindow.span.appendChild(status_message);
  104. document.querySelector("div.content").insertBefore(unsafeWindow.span, document.querySelector("div.content").firstChild);
  105. //add per comment buttons (inactive by default)
  106. if (generate_individual_delete_buttons) generate_delete_buttons()
  107. } else if (unsafeWindow.span != null) {
  108. unsafeWindow.span.style.display = 'none';
  109. }
  110. }
  111. unsafeWindow.recursive_process = function (overwrite_all, delete_all) {
  112. //get comments again in case the user has scrolled and revealed more comments
  113. get_comments()
  114. let commentsArray = [];
  115. for (let i = 0; i < unsafeWindow.comments.length; i++) {
  116. //for each author, get ID of the input field of the comment
  117. let thing_id = unsafeWindow.comments[i].parentNode.parentNode.querySelector("form.usertext > input[name='thing_id']").value;
  118. if (commentsArray.indexOf(thing_id) == -1) {
  119. commentsArray.push(thing_id);
  120. //TODO refactor this out
  121. unsafeWindow.num_user_comments++;
  122. }
  123. }
  124. if (overwrite_all && delete_all) {
  125. unsafeWindow.overwrite_all(commentsArray, true);
  126. } else if (overwrite_all) {
  127. unsafeWindow.overwrite_all(commentsArray, false);
  128. } else if (delete_all) {
  129. unsafeWindow.delete_all(commentsArray);
  130. }
  131. }
  132. unsafeWindow.overwrite_all = function (comments, also_delete) {
  133. //get next comment id
  134. let thing_id = comments.shift();
  135. //overwrite the next comment in the stack
  136. unsafeWindow.overwrite_comment(thing_id);
  137. //if also deleting, add a timeout and delete the comment
  138. if (also_delete) unsafeWindow.setTimeout(unsafeWindow.delete_comment(thing_id), time_between_actions);
  139. //if there are still comments left, get next comment
  140. //increase timeout if also deleting
  141. if (comments.length) unsafeWindow.setTimeout(unsafeWindow.overwrite_all, also_delete ? time_between_actions * 2 : time_between_actions, comments);
  142. }
  143. unsafeWindow.delete_all = function (comments) {
  144. delete_comment(comments.shift());
  145. //if there are still comments left, get next comment
  146. if (comments.length) unsafeWindow.setTimeout(delete_all, time_between_actions, comments);
  147. }
  148. unsafeWindow.overwrite_comment = function (thing_id) {
  149. try {
  150. //find edit form (hidden on page but active)
  151. let edit_form = document.querySelector("input[name='thing_id'][value='" + thing_id + "']").parentNode;
  152. //if comment is currently being edited, cancel out of that
  153. let edit_cancel_btn = edit_form.querySelector("div.usertext-edit > div.bottom-area > div.usertext-buttons > button.cancel");
  154. edit_cancel_btn.click();
  155. //find edit button and click it
  156. let edit_btn = edit_form.parentNode.querySelector("ul > li > a.edit-usertext");
  157. if (edit_btn) edit_btn.click();
  158. //find edit textbox and replace the string with random chars
  159. let edit_textbox = edit_form.querySelector("div.usertext-edit > div > textarea");
  160. let repl_str = '';
  161. let chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz><.-,+!#$%^&*();:[]~";
  162. for (let i = 0; i < edit_textbox.value.length; i++) {
  163. if (edit_textbox.value.substr(i, 1) == '\n') {
  164. repl_str += '\n';
  165. } else {
  166. let random_char = Math.floor(Math.random() * chars.length);
  167. repl_str += chars.charAt(random_char, 1);
  168. }
  169. }
  170. //set edited value to the random string
  171. edit_textbox.value = repl_str;
  172. //find save comment button and click it
  173. let edit_save_btn = edit_form.querySelector("div.usertext-edit > div.bottom-area > div.usertext-buttons > button.save");
  174. edit_save_btn.click();
  175. } catch (e) {
  176. alert("Error interacting with overwrite form: " + e);
  177. }
  178. }
  179. unsafeWindow.delete_comment = function (thing_id) {
  180. try {
  181. // get current status of comment editing box to prevent deleting comment before overwrite is complete
  182. let thing = document.querySelector("input[name='thing_id'][value='" + thing_id + "']");
  183. let status = thing.parentNode.querySelector("div.usertext-edit > div.bottom-area > div.usertext-buttons > span.status").innerHTML;
  184. if (status.indexOf("error") != -1) {
  185. alert("Failed to overwrite comment " + thing_id + " due to an unknown reddit error, skipping.");
  186. return;
  187. }
  188. // if status is submitting, there may be an internet connectivity error, so we retry
  189. if (status.indexOf("submitting") != -1) {
  190. unsafeWindow.setTimeout(delete_comment, time_between_actions * 5, thing_id);
  191. return;
  192. }
  193. // find delete button and click it and then yes confirmation button
  194. let del_form = thing.parentNode.parentNode.querySelector("ul.buttons > li > form.del-button");
  195. unsafeWindow.toggle(del_form.querySelector("span.main > a"));
  196. del_form.querySelector("span.error > a.yes").click();
  197. } catch (e) {
  198. alert("Error deleting comment: " + e);
  199. }
  200. }
  201. //(UTILITY FUNCTIONS)
  202. function filter_author(comment) {
  203. return comment.innerHTML == unsafeWindow.user
  204. }
  205. //[UNUSED FUNCTIONS]
  206. //Add a "SECURE DELETE" button near each comment delete button
  207. //TODO refactor this
  208. //TODO add overwrite button
  209. function generate_delete_buttons() {
  210. for (let i = 0; i < comments.length; i++) {
  211. // skip comments by anyone else
  212. if (comments[i].innerHTML != unsafeWindow.user) continue;
  213. try {
  214. // get the parent
  215. let main_parent = comments[i].parentNode.parentNode;
  216. let thing_id = main_parent.querySelector("form > input[name='thing_id']").value;
  217. let list = main_parent.querySelector("ul.flat-list");
  218. // if it already contains the tag, skip
  219. if (list.querySelector("li.secure_delete")) continue;
  220. unsafeWindow.num_user_comments++;
  221. let addedlink = document.createElement("li");
  222. addedlink.setAttribute('class', 'secure_delete');
  223. let dlink = document.createElement("a");
  224. dlink.setAttribute('class', 'bylink secure_delete');
  225. // TODO test let here
  226. dlink.setAttribute('onClick', 'javascript:var ret = overwrite_comment("' + thing_id + '", false);');
  227. dlink.setAttribute('href', 'javascript:void(0)');
  228. dlink.appendChild(document.createTextNode('SECURE DELETE'));
  229. addedlink.appendChild(dlink);
  230. main_parent.querySelector("ul.flat-list").appendChild(addedlink);
  231. } catch (e) {
  232. alert("Error adding Secure Delete links to comments.\nError: " + e + " Stack:" + e.stack);
  233. }
  234. }
  235. }