Twitch Chat User Highlight

Highlights messages by user, on username or @username click

  1. // ==UserScript==
  2. // @name Twitch Chat User Highlight
  3. // @namespace 1N07
  4. // @version 0.5
  5. // @description Highlights messages by user, on username or @username click
  6. // @author 1N07
  7. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
  8. // @include https://www.twitch.tv*
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_unregisterMenuCommand
  13. // @noframes
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. var ChatList, MutUser, ChatGetInterval, AutoHighlightStopTimeout;
  20. const MutConf = {childList: true};
  21. const Observer = new MutationObserver(OnNewComment);
  22.  
  23. var HighlightStopTimer = GM_getValue("HighlightStopTimer", 5);
  24. var MenuOptionRegister;
  25. MenuOptionRegister = GM_registerMenuCommand("Set HighlightStopTimer ("+HighlightStopTimer+")", MenuOptionRegisterFunc);
  26. function MenuOptionRegisterFunc()
  27. {
  28. HighlightStopTimer = prompt("Set HighlightStopTimer\nThe time it takes for the user highlighting to automatically stop after the mouse leaves the chat area.\nRe-entering the chat area resets and stops the timer.", HighlightStopTimer);
  29. GM_setValue("HighlightStopTimer", HighlightStopTimer);
  30. GM_unregisterMenuCommand(MenuOptionRegister);
  31. MenuOptionRegister = GM_registerMenuCommand("Set HighlightStopTimer ("+HighlightStopTimer+")", MenuOptionRegisterFunc);
  32. }
  33.  
  34. addGlobalStyle(`
  35. .tcuh-highlighted {
  36. border: rgba(255,255,255,0.5) 2px solid;
  37. background: rgba(255,255,255,0.15);
  38. }
  39. .chat-line__message-mention, .mention-fragment, .mentioning
  40. {
  41. cursor: pointer;
  42. font-weight: 700;
  43. }
  44. `);
  45.  
  46.  
  47. var lastCheckedUrl = window.location.href;
  48. OnPageLoad();
  49.  
  50. setInterval(() => {
  51. if(lastCheckedUrl != window.location.href)
  52. {
  53. lastCheckedUrl = window.location.href;
  54. OnPageLoad();
  55. }
  56. }, 200);
  57.  
  58.  
  59. function OnPageLoad()
  60. {
  61. Initialize();
  62. AfterChatIsAvailable(() => {
  63. StartClickListener();
  64. });
  65. }
  66.  
  67.  
  68.  
  69.  
  70.  
  71. function Initialize()
  72. {
  73. if(ChatGetInterval)
  74. clearInterval(ChatGetInterval);
  75.  
  76. if(ChatList && ChatList.length > 0)
  77. ChatList.off();
  78.  
  79. MutUser = ChatList = ChatGetInterval = null;
  80. StopHighlightingUsers();
  81. }
  82.  
  83. function AfterChatIsAvailable(callback)
  84. {
  85. ChatGetInterval = setInterval(() => {
  86. ChatList = $(".chat-scrollable-area__message-container"); //Live vanilla & FFZ
  87. if(ChatList.length <= 0) ChatList = $("div.video-chat__message-list-wrapper > div > ul"); //VODs vanilla & FFZ
  88.  
  89. if(ChatList.length > 0)
  90. {
  91. callback();
  92. clearInterval(ChatGetInterval);
  93. }
  94. }, 50);
  95. }
  96.  
  97. function StartClickListener()
  98. {
  99. //post author
  100. ChatList.on("click", ".video-chat__message-author, .chat-line__username", function(e){ //VODs vanilla & FFZ, Live vanilla & FFZ
  101. e.preventDefault();
  102. e.stopPropagation();
  103.  
  104. let user = $(this).find(".chat-author__display-name[data-a-user]"); //Live & VODs vanilla
  105. if(user.length == 0) user = $(this).closest(".chat-line__message[data-user]"); //Live FFZ
  106. if(user.length == 0) user = $(this).find(".chat-author__display-name"); //VODs FFZ
  107.  
  108. user = user.data("aUser") || user.data("user") || user.parent().parent().data("user"); //Live & VODs vanilla || Live FFZ || VODs FFZ
  109.  
  110. StartHighlightingUser(user);
  111. });
  112. //@
  113. ChatList.on("click", ".chat-line__message-mention, .mention-fragment, .mentioning", function(e){ //Live & VODs FFZ, Live & VODs vanilla, ?
  114. e.preventDefault();
  115. e.stopPropagation();
  116. let user = $(this).data("login") || $(this).text().replace("@", "").toLowerCase(); //Live & VODs FFZ || Live & VODs vanilla
  117. StartHighlightingUser(user);
  118. });
  119. }
  120.  
  121. function StartHighlightingUser(user)
  122. {
  123. if(user && user.length > 0)
  124. {
  125. StopHighlightingUsers(false);
  126. if(user == MutUser)
  127. MutUser = null;
  128. else
  129. {
  130. MutUser = user;
  131.  
  132. let targets = ChatList.find(".chat-line__message .chat-author__display-name[data-a-user='"+MutUser+"']"); //Live vanilla
  133. if(targets.length > 0)
  134. {
  135. targets.each(function(e){
  136. $(this).closest(".chat-line__message").addClass("tcuh-highlighted");
  137. });
  138. }
  139. else
  140. {
  141. targets = ChatList.find(".chat-line__message[data-user='"+MutUser+"']"); //Live FFZ
  142. if(targets.length > 0)
  143. {
  144. targets.each(function(e){
  145. $(this).addClass("tcuh-highlighted");
  146. });
  147. }
  148. else
  149. {
  150. targets = ChatList.find(".vod-message .chat-author__display-name[data-a-user='"+MutUser+"'], .vod-message [data-user='"+MutUser+"']"); //VODs vanilla & FFZ
  151. if(targets.length > 0)
  152. {
  153. targets.each(function(e){
  154. let target = $(this).closest(".vod-message").parent().addClass("tcuh-highlighted");
  155. });
  156. }
  157. }
  158. }
  159.  
  160. Observer.observe(ChatList[0], MutConf);
  161.  
  162. if(HighlightStopTimer != 0)
  163. {
  164. $(".chat-shell").off();
  165. $(".chat-shell").mouseenter(function(){
  166. clearTimeout(AutoHighlightStopTimeout);
  167. });
  168. $(".chat-shell").mouseleave(function(){
  169. StartHighlightAutoStopTimer();
  170. });
  171. }
  172. }
  173. }
  174. }
  175.  
  176. function StartHighlightAutoStopTimer()
  177. {
  178. clearTimeout(AutoHighlightStopTimeout);
  179. AutoHighlightStopTimeout = setTimeout(StopHighlightingUsers, HighlightStopTimer * 1000);
  180. }
  181.  
  182. function StopHighlightingUsers(alsoClearMutUser = true)
  183. {
  184. if(alsoClearMutUser)
  185. MutUser = null;
  186. $(".tcuh-highlighted").removeClass("tcuh-highlighted");
  187. Observer.disconnect();
  188. }
  189.  
  190. function OnNewComment(mutationsList, observer)
  191. {
  192. for(let i = 0; i < mutationsList.length; i++)
  193. {
  194. if(mutationsList[i].type == 'childList' && mutationsList[i].addedNodes.length > 0)
  195. {
  196. for(let j = 0; j < mutationsList[i].addedNodes.length; j++)
  197. {
  198. if($(mutationsList[i].addedNodes[j]).find(".chat-author__display-name[data-a-user='"+MutUser+"']").length > 0 || //Live vanilla
  199. $(mutationsList[i].addedNodes[j]).find(".vod-message .chat-author__display-name[data-a-user='"+MutUser+"']").length > 0 || //VODs vanilla
  200. $(mutationsList[i].addedNodes[j]).find(".vod-message [data-user='"+MutUser+"']").length > 0 || //VODs FFZ
  201. $(mutationsList[i].addedNodes[j]).data("user") == MutUser) //Live FFZ
  202. {
  203. $(mutationsList[i].addedNodes[j]).addClass("tcuh-highlighted");
  204. }
  205. }
  206. }
  207. }
  208. }
  209.  
  210. function addGlobalStyle(css)
  211. {
  212. var head, style;
  213. head = document.getElementsByTagName('head')[0];
  214. if (!head) { return; }
  215. style = document.createElement('style');
  216. style.type = 'text/css';
  217. style.innerHTML = css;
  218. head.appendChild(style);
  219. }
  220.  
  221. })();