Greasy Fork 支持简体中文。

Robin Enhancement Script

Highlight mentions, make link clickable, use channels & automatically remove spam

目前為 2016-04-04 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Robin Enhancement Script
  3. // @namespace https://www.reddit.com/
  4. // @version 2.0.0
  5. // @description Highlight mentions, make link clickable, use channels & automatically remove spam
  6. // @author mr_bag
  7. // @match https://www.reddit.com/robin*
  8. // @grant none
  9. // ==/UserScript==
  10. (function() {
  11.  
  12. // Grab users username + play nice with RES
  13. var robin_user = $("#header-bottom-right .user a").first().text();
  14. var ignored_users = {};
  15.  
  16. // for spam counter - very important i know :P
  17. var blocked_spam_el = null;
  18. var blocked_spam = 0;
  19.  
  20. var _robin_grow_detected = false;
  21.  
  22. // Supported channels
  23. var channels = {
  24. "%": "percent",
  25. "$" : "dollar",
  26. "#" : "hash",
  27. "~" : "tilde",
  28. "^" : "hat",
  29. "+" : "plus",
  30. "&" : "and",
  31. "*" : "star",
  32. "." : "dot",
  33. "!" : "exclaim",
  34. "?" : "question"
  35. };
  36.  
  37. var channel_unread_counts = {};
  38.  
  39. // Init unread count
  40. for(var i in channels){
  41. channel_unread_counts[i] = 0;
  42. }
  43.  
  44. /**
  45. * Check if a message is "spam"
  46. */
  47. var is_spam = function(line){
  48. return (
  49. // Hide auto vote messages
  50. (/^voted to (grow|stay|abandon)/.test(line)) ||
  51. // random unicode?
  52. (/[\u0080-\uFFFF]/.test(line)) ||
  53. // hide any auto voter messages
  54. (/\[.*autovoter.*\]/.test(line)) ||
  55. // Common bots
  56. (/^(\[binbot\]|\[robin-grow\])/.test(line)) ||
  57. // repeating chars in line (more than 5). e.g. aaaaaaa !!!!!!!!
  58. (/(.)\1{5,}/.test(line)) ||
  59. // Some common messages
  60. (/(voting will end in approximately|\[i spam the most used phrase\]|\[message from creator\]|\[.*bot.*\])/.test(line)) ||
  61. // no spaces = spam if its longer than 25 chars (dont filter links)
  62. (line.indexOf(" ") === -1 && line.length > 25 && line.indexOf("http") === -1) ||
  63. // repeating same word
  64. /(\b\S+\b)\s+\b\1\b/i.test(line)
  65. );
  66. };
  67.  
  68. /**
  69. * Check if a message is from an ignored user
  70. *
  71. */
  72. var is_ignored = function($usr, $ele){
  73. // no user name, go looking for when said it
  74. if($usr.length === 0){
  75. while($usr.length === 0){
  76. $ele = $ele.prev();
  77. $usr = $ele.find(".robin--username");
  78. }
  79. }
  80. // are they ignored?
  81. return (ignored_users[$usr.text()]);
  82. };
  83.  
  84. /**
  85. * Make links clickable
  86. *
  87. */
  88. var auto_link = function($msg){
  89. var text = $msg.html(); // read as html so stuff stays escaped
  90. // normal links
  91. text = text.replace(/\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim, '<a target="blank" href="$&">$&</a>');
  92. // reddit subreddit links
  93. text = text.replace(/ \/r\/(\w+)/gim, ' <a target="blank" href="https://reddit.com/r/$1">/r/$1</a>');
  94. // update text
  95. $msg.html(text);
  96. };
  97.  
  98. /**
  99. * Mute a user
  100. */
  101. var _mute_user = function(usr){
  102. // Add to ignore list
  103. ignored_users[usr] = true;
  104. _render_muted_list();
  105. };
  106.  
  107. /**
  108. * un-mute a user
  109. */
  110. var _unmute_user = function(usr){
  111. // Add to ignore list
  112. delete ignored_users[usr];
  113. _render_muted_list();
  114. };
  115.  
  116. // Render list of ignored users
  117. var _render_muted_list = function(){
  118. var html = "<strong>Ignored users</strong><br>";
  119. for(var u in ignored_users){
  120. html += "<div data-usr='"+ u + "'>" + u + " - [unmute]</div>";
  121. }
  122. $("#muted_users").html(html);
  123. };
  124.  
  125. // Scroll chat back to bottom
  126. var _scroll_to_bottom = function(){
  127. $("#robinChatWindow").scrollTop($("#robinChatMessageList").height());
  128. };
  129.  
  130. var update_spam_count = function(){
  131. blocked_spam++;
  132. blocked_spam_el.innerHTML = blocked_spam;
  133. }
  134.  
  135. /**
  136. * Parse a link and apply changes
  137. */
  138. var parse_line = function($ele){
  139. var $msg = $ele.find(".robin-message--message");
  140. var $usr = $ele.find(".robin--username");
  141.  
  142. var line = $msg.text().toLowerCase();
  143.  
  144. // If user is ignored or message looks like "Spam". hide it
  145. if (is_ignored($usr, $ele) || is_spam(line)) {
  146. $ele.addClass("spam-hidden");
  147. update_spam_count();
  148. }
  149.  
  150. // Highlight mentions
  151. if(line.indexOf(robin_user) !== -1){
  152. $ele.addClass("user-mention");
  153. }
  154.  
  155. // Make links clickable
  156. if(!_robin_grow_detected && line.indexOf("http") !== -1){
  157. auto_link($msg);
  158. }
  159.  
  160. // Add mute button to users
  161. if(!$ele.hasClass("robin--user-class--system") && $usr.text() != robin_user){
  162. $("<span style='font-size:.8em;cursor:pointer'> [mute] </span>").insertBefore($usr).click(function(){
  163. _mute_user($usr.text());
  164. });
  165. }
  166.  
  167. // Track channels
  168. for(var i in channels){
  169. if(line.indexOf(i) === 0){
  170. $ele.addClass("filter-" + channels[i] +" in-channel");
  171. channel_unread_counts[i]++;
  172. }
  173. }
  174. };
  175.  
  176. var _apply_updates = function(){
  177.  
  178. // Update unread counts
  179. $("#filter_tabs span").each(function(){
  180. var type = $(this).attr("data-filter");
  181. $(this).find("span").text(channel_unread_counts[type]);
  182. });
  183. };
  184.  
  185. // Detect changes, are parse the new message
  186. $("#robinChatWindow").on('DOMNodeInserted', function(e) {
  187. if ($(e.target).is('div.robin-message')) {
  188. // Apply changes to line
  189. parse_line($(e.target));
  190. }
  191. });
  192.  
  193. // When everything is ready
  194. $(document).ready(function(){
  195.  
  196. // Set default spam filter type
  197. $("#robinChatWindow").addClass("hide-spam");
  198.  
  199. // Add checkbox to toggle "hide" behaviors
  200. $("#robinDesktopNotifier").append("<label><input type='checkbox' checked='checked'>Hide spam completely (<span id='spamcount'>0</span> removed)</label>").click(function(){
  201. if($(this).find("input").is(':checked')){
  202. $("#robinChatWindow").removeClass("mute-spam").addClass("hide-spam");
  203. }else{
  204. $("#robinChatWindow").removeClass("hide-spam").addClass("mute-spam");
  205. }
  206. // correct scroll after spam filter change
  207. _scroll_to_bottom();
  208. });
  209.  
  210. blocked_spam_el = $("#spamcount")[0];
  211.  
  212. // Add Muted list & hook up unmute logic
  213. $('<div id="muted_users" class="robin-chat--sidebar-widget robin-chat--notification-widget"><strong>Ignored users</strong></div>').insertAfter($("#robinDesktopNotifier"));
  214. $('#muted_users').click(function(e){
  215. var user = $(e.target).data("usr");
  216. if(user) _unmute_user(user);
  217. });
  218.  
  219. // Hook up toggles for filters
  220. $('<div id="filter_tabs"><span data-action="unfilter" class="selected">Everything</span> <span data-filter="%">%</span> <span data-filter="~">~</span> <span data-filter="$">$</span> <span data-filter="#">#</span> <span data-filter=".">.</span> <span data-filter="?">?</span> <span data-filter="^">^</span> <span data-filter="&">&amp;</span> <span data-filter="+">+</span> <span data-filter="!">!</span> <span data-filter="*">*</span> <span data-action="channels">All channels</span> <span data-action="hide">No channels</span></div>').insertAfter("#robinChatWindow").click(function(e){
  221. var filter = $(e.target).data("filter");
  222. var action = $(e.target).data("action");
  223. // filter was toggled?
  224. if(!filter && !action) return;
  225.  
  226. var robin_window = $("#robinChatWindow");
  227. // Toggle selected "tab"
  228. $(e.target).parent().find("span").removeClass("selected");
  229. $(e.target).addClass("selected");
  230.  
  231. // remove filters
  232. robin_window.removeAttr("data-filter").removeClass("filter_on").removeClass("filter_all_channels").removeClass("filter_only_channels");
  233.  
  234. if(action === 'unfilter'){
  235. // Show all
  236. }else if(action === 'hide'){
  237. // Hide any channel conversations
  238. robin_window.addClass("filter_all_channels");
  239. }else if(action === 'channels'){
  240. // Hide any channel conversations
  241. robin_window.addClass("filter_only_channels");
  242. }else{
  243. // apply a filter
  244. robin_window.addClass("filter_on").attr("data-filter", filter);
  245.  
  246. // blank count on view
  247. //channel_unread_counts[filter] = 0;
  248. //$(e.target).find("span").text("0");
  249. }
  250.  
  251. // correct scroll after filter change
  252. _scroll_to_bottom();
  253. });
  254. // add counter
  255. $("#filter_tabs span[data-filter]").append(" (<span>0</span>)");
  256.  
  257.  
  258. // Auto append % when in filtered mode
  259. $("#robinSendMessage").submit(function(){
  260. if($("#robinChatWindow").hasClass("filter_on")){
  261. if($(".text-counter-input").val().indexOf("/") !== 0){ // /commands should work as normal
  262. $(".text-counter-input").val($("#robinChatWindow").attr("data-filter") + " " + $(".text-counter-input").val());
  263. }
  264. }
  265. });
  266.  
  267. // apply updates to counts every 5 seconds
  268. setInterval(_apply_updates, 1000);
  269. });
  270.  
  271.  
  272.  
  273.  
  274. // filter for channel
  275. document.styleSheets[0].insertRule("#robinChatWindow.filter_on div.robin-message { display:none; }", 0);
  276. document.styleSheets[0].insertRule("#robinChatWindow.filter_on[data-filter='$'] div.robin-message.filter-dollar,#robinChatWindow.filter_on[data-filter='*'] div.robin-message.filter-star,#robinChatWindow.filter_on[data-filter='.'] div.robin-message.filter-dot, #robinChatWindow.filter_on[data-filter='!'] div.robin-message.filter-exclaim,#robinChatWindow.filter_on[data-filter='?'] div.robin-message.filter-question,#robinChatWindow.filter_on[data-filter='&'] div.robin-message.filter-and, #robinChatWindow.filter_on[data-filter='%'] div.robin-message.filter-percent,#robinChatWindow.filter_on[data-filter='#'] div.robin-message.filter-hash,#robinChatWindow.filter_on[data-filter='~'] div.robin-message.filter-tilde,#robinChatWindow.filter_on[data-filter='^'] div.robin-message.filter-hat,#robinChatWindow.filter_on[data-filter='+'] div.robin-message.filter-plus,#robinChatWindow.filter_on div.robin-message.robin--user-class--system { display:block; }", 0);
  277.  
  278. // filter, hide all channels
  279. document.styleSheets[0].insertRule("#robinChatWindow.filter_all_channels div.robin-message.in-channel{ display:none; }", 0);
  280.  
  281. // filter show all channels
  282. document.styleSheets[0].insertRule("#robinChatWindow.filter_only_channels div.robin-message {display:none; }", 0);
  283. document.styleSheets[0].insertRule("#robinChatWindow.filter_only_channels div.robin-message.in-channel, #robinChatWindow.filter_only_channels div.robin-message.robin--user-class--system { display:block; }", 0);
  284.  
  285. // Styles for filter tabs
  286. document.styleSheets[0].insertRule("#filter_tabs { display: table; width:100%;table-layout: fixed; background:#d7d7d2; border-bottom:1px solid #efefed;'}", 0);
  287. document.styleSheets[0].insertRule("#filter_tabs > span { padding: 5px 2px;text-align: center; display: table-cell; cursor: pointer;width:2%; vertical-align: middle; font-size: 1.1em;}", 0);
  288. document.styleSheets[0].insertRule("#filter_tabs > span.selected, #filter_tabs span:hover { background: #fff;}", 0);
  289.  
  290. //mentions should show even in filter view
  291. document.styleSheets[0].insertRule("#robinChat #robinChatWindow div.robin-message.user-mention { display:block; font-weight:bold; }", 0);
  292.  
  293. // Add initial styles for "spam" messages
  294. document.styleSheets[0].insertRule("#robinChat #robinChatWindow.hide-spam div.robin-message.spam-hidden { display:none; }", 0);
  295. document.styleSheets[0].insertRule("#robinChat #robinChatWindow.mute-spam div.robin-message.spam-hidden { opacity:0.3; font-size:1.2em; }", 0);
  296.  
  297. // muted user box
  298. document.styleSheets[0].insertRule("#muted_users { font-size:1.2em; }", 0);
  299. document.styleSheets[0].insertRule("#muted_users div { padding: 2px 0; }", 0);
  300. document.styleSheets[0].insertRule("#muted_users strong { font-weight:bold; }", 0);
  301.  
  302. // FIX RES nightmode (ish)
  303. document.styleSheets[0].insertRule(".res-nightmode #robinChatWindow div.robin-message { color: #ccc; }", 0);
  304.  
  305.  
  306. $(document).ready(function(){
  307. setTimeout(function(){
  308. // Play nice with robin grow (makes room for tab bar we insert)
  309. if($(".usercount.robin-chat--vote").length !== 0){
  310.  
  311. _robin_grow_detected = true;
  312. document.styleSheets[0].insertRule("#robinChat.robin-chat .robin-chat--body { height: calc(100vh - 150px); }", 0);
  313. }
  314. },500);
  315. });
  316. })();