[Reddit] ModmailExtraInfo

Shows additional user information on the sidebar of modmail

当前为 2021-11-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name [Reddit] ModmailExtraInfo
  3. // @namespace HKR
  4. // @match https://mod.reddit.com/mail/*
  5. // @grant none
  6. // @version 1.6
  7. // @author HKR
  8. // @description Shows additional user information on the sidebar of modmail
  9. // @require https://greasyfork.org/scripts/21927-arrive-js/code/arrivejs.js
  10. // @icon https://www.redditstatic.com/modmail/favicon/favicon-32x32.png
  11. // @supportURL https://github.com/Hakorr/Userscripts/issues
  12. // ==/UserScript==
  13.  
  14. /* NOTE: (If you want to use the Custom Responses) Reddit's sync feature removes the script's added text. This is a bug in my script and can be fixed with time.
  15. If you block "https://oauth.reddit.com/api/mod/conversations/*****?markRead=false&redditWebClient=modmail", the added text will stay. Thanks for understanding.*/
  16.  
  17. /* VARIABLES FOR RESPONSES */
  18. var subTag = document.getElementsByClassName("ThreadTitle__community")[0].href.slice(23);
  19. var userTag = "u/" + document.getElementsByClassName("InfoBar__username")[0].innerText;
  20. var modmail = `[modmail](https://www.reddit.com/message/compose?to=/${subTag})`;
  21.  
  22. /* SETTINGS */
  23. var textColor = null;
  24. var lightModeTextColor = "#6e6e6e";
  25. var darkModeTextColor = "#757575";
  26.  
  27. var titleColor = null;
  28. var lightModeTitleColor = "#2c2c2c";
  29. var darkModeTitleColor = "#a7a7a7";
  30.  
  31. var listBoxColor = null;
  32. var lightModeListColor = "#fff";
  33. var darkModeListColor = "#242424";
  34.  
  35. var dataColor = "#0079d3";
  36.  
  37. var enableCustomResponses = true;
  38. //Feel free to edit and add more responses suitable for you! Replace means if to replace all text or just to add the text.
  39. var responses = [
  40. {
  41. "name":"Select a template",
  42. "replace":true,
  43. "content":``
  44. },
  45. {
  46. "name":"Default approved",
  47. "replace":true,
  48. "content":`Hey, approved the post!`
  49. },
  50. {
  51. "name":"Default rule broken",
  52. "replace":true,
  53. "content":`Your post broke our rules.\n\nThe action will not be reverted.`
  54. },
  55. {
  56. "name":"Add thanks",
  57. "replace":false,
  58. "content":`\n\nThank you!`
  59. },
  60. {
  61. "name":"Add subreddit mention",
  62. "replace":false,
  63. "content":`${subTag}`
  64. },
  65. {
  66. "name":"Add user mention",
  67. "replace":false,
  68. "content":`${userTag}`
  69. },
  70. {
  71. "name":"Add Modmail link",
  72. "replace":false,
  73. "content":`${modmail}`
  74. },
  75. {
  76. "name":"Add Content Policy",
  77. "replace":false,
  78. "content":`[Content Policy](https://www.redditinc.com/policies/content-policy)`
  79. },
  80. {
  81. "name":"Add User Agreement",
  82. "replace":false,
  83. "content":`[User Agreement](https://www.redditinc.com/policies/user-agreement)`
  84. },
  85. {
  86. "name":"Add Rickroll",
  87. "replace":false,
  88. "content":`[link](https://www.youtube.com/watch?v=dQw4w9WgXcQ)`
  89. }
  90. ];
  91.  
  92. /* ---------- JS & HTML ---------- */
  93.  
  94. function Get(url) {
  95. var xmlHttp = new XMLHttpRequest();
  96. xmlHttp.open( "GET", url, false );
  97. xmlHttp.send( null );
  98. return xmlHttp.responseText;
  99. }
  100.  
  101. function time(UNIX_timestamp){
  102. var a = new Date(UNIX_timestamp * 1000);
  103. var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  104. var year = a.getFullYear();
  105. var month = months[a.getMonth()];
  106. var date = a.getDate();
  107. var hour = fixnumber(a.getHours());
  108. var min = fixnumber(a.getMinutes());
  109. var sec = fixnumber(a.getSeconds());
  110. var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ;
  111. return time;
  112. }
  113.  
  114. function fixnumber(number) {
  115. if(number < 10) return "0" + number;
  116. else return number;
  117. }
  118.  
  119. function sanitize(evilstring) {
  120. const decoder = document.createElement('div')
  121. decoder.innerHTML = evilstring;
  122. return decoder.textContent;
  123. }
  124.  
  125. //Function that appends HTML into the Modmail page
  126. function addInfo(){
  127. //Load and parse username
  128. var username = document.getElementsByClassName("InfoBar__username")[0].innerText;
  129. var about = "https://www.reddit.com/user/" + username + "/about.json";
  130. var user = JSON.parse(Get(about));
  131.  
  132. //Separator HTML element
  133. var seperator = document.createElement('div');
  134. seperator.innerHTML = '<div class="InfoBar__modActions"></div>';
  135.  
  136. //HTML element that contains all the data
  137. var userDetails = document.createElement('div');
  138. userDetails.classList.add("InfoBar__age");
  139. userDetails.innerHTML = `
  140. <img class="profileIcon" src="${user.data.icon_img}" width="25">
  141. <a class="InfoBar__username" href="https://www.reddit.com/user/${user.data.name}">${user.data.subreddit.display_name_prefixed}</a>
  142. <h1 style="color: ${textColor} ; font-size: 11px; margin-top: 17px; margin-bottom: 10px;">${sanitize(user.data.subreddit.public_description)}</h1>
  143. <h1 class="dataTitle">Main</h1>
  144. <div class="dataText">
  145. <p>Created: <span class="value">${time(user.data.created)}</span></p>
  146. <p>UserID: <span class="value">${user.data.id}</span></p>
  147. <p>Verified: <span class="value">${user.data.verified}</span></p>
  148. <p>Employee: <span class="value">${user.data.is_employee}</span></p>
  149. <p>NSFW Profile: <span class="value">${user.data.subreddit.over_18}</span></p>
  150. </div>
  151. <h1 class="dataTitle">Karma</h1>
  152. <div class="dataText">
  153. <p>Post: <span class="value">${user.data.link_karma}</span></p>
  154. <p>Comment: <span class="value">${user.data.comment_karma}</span></p>
  155. <p>Total: <span class="value">${user.data.total_karma}</span></p>
  156. <p>Awardee: <span class="value">${user.data.awardee_karma}</span></p>
  157. <p>Awarder: <span class="value">${user.data.awarder_karma}</span></p>
  158. </div>
  159. <h1 class="dataTitle">Links</h1>
  160. <div style="padding-left: 10px;">
  161. <a class="InfoBar__recent" href="https://redditmetis.com/user/${user.data.name}" target="_blank">Redditmetis</a>
  162. <a class="InfoBar__recent" href="https://www.reddit.com/search?q=${user.data.name}" target="_blank">Reddit Search</a>
  163. <a class="InfoBar__recent" href="https://www.google.com/search?q=%22${user.data.name}%22" target="_blank">Google Search</a>
  164. </div>
  165. `;
  166. //Arrange items and append
  167. document.getElementsByClassName("ThreadViewer__infobar")[0].appendChild(seperator);
  168. document.getElementsByClassName("ThreadViewer__infobar")[0].appendChild(userDetails);
  169. document.getElementsByClassName("ThreadViewer__infobar")[0].appendChild(document.getElementsByClassName("ThreadViewer__infobar")[0].firstChild);
  170. document.getElementsByClassName("InfoBar")[0].appendChild(document.getElementsByClassName("InfoBar__modActions")[0]);
  171. document.getElementsByClassName("InfoBar")[0].insertBefore(document.getElementsByClassName("InfoBar__modActions")[0],document.getElementsByClassName("InfoBar")[0].firstChild);
  172. if(document.getElementsByClassName("InfoBar__banText")[0])
  173. document.getElementsByClassName("ThreadViewer__infobar")[0].insertBefore(document.getElementsByClassName("InfoBar__banText")[0],document.getElementsByClassName("ThreadViewer__infobar")[0].firstChild);
  174. document.getElementsByClassName("InfoBar__username")[1].outerHTML = "";
  175. document.getElementsByClassName("InfoBar__age")[1].outerHTML = "";
  176. document.getElementsByClassName("InfoBar__modActions")[1].outerHTML = "";
  177. }
  178.  
  179. function addResponseBox() {
  180. //Listbox element
  181. var responseBox = document.createElement('div');
  182. responseBox.classList.add("select");
  183. responseBox.innerHTML = `
  184. <h2 class="dataTitle">Response templates</h2>
  185. <select id="responseListbox" onchange="listBoxChanged(this.value);" onfocus="this.selectedIndex = -1;"/>
  186. <span class="focus"></span>
  187. `;
  188.  
  189. //Script element to head
  190. var headJS = document.createElement('script');
  191. headJS.innerHTML = `
  192. function listBoxChanged(message) {
  193. var messageBox = document.getElementsByClassName("Textarea ThreadViewerReplyForm__replyText")[0];
  194. var responses = ${JSON.stringify(responses)};
  195. var response = responses.find(x => x.content == message);
  196. response.replace ? messageBox.value = message : messageBox.value += message;
  197. console.log("Set message to: " + message);
  198. }
  199. `;
  200.  
  201. function populate() {
  202. var select = document.getElementById("responseListbox");
  203. for(var i = 0; i < responses.length; i++) {
  204. select.options[select.options.length] = new Option(responses[i].name, responses[i].content);
  205. }
  206. }
  207.  
  208. document.getElementsByClassName("ThreadViewer__replyContainer")[0].prepend(responseBox);
  209. var head = document.getElementsByTagName('head')[0];
  210. head.appendChild(headJS);
  211.  
  212. populate();
  213. }
  214.  
  215. function themeColors() {
  216. var darkTheme = document.getElementsByClassName("theme-dark").length ? true : false;
  217. if(darkTheme) {
  218. console.log("Dark mode detected!");
  219. textColor = darkModeTextColor;
  220. titleColor = darkModeTitleColor;
  221. listBoxColor = darkModeListColor;
  222. } else {
  223. console.log("Light mode detected!");
  224. textColor = lightModeTextColor;
  225. titleColor = lightModeTitleColor;
  226. listBoxColor = lightModeListColor;
  227. }
  228. }
  229.  
  230. //When Modmail conversation has been opened, load the HTML elements with the correct data
  231. const elementToWatch = 'a[class="InfoBar__username"]';
  232. document.arrive(elementToWatch, function () {
  233. themeColors();
  234. addInfo();
  235. if(enableCustomResponses) addResponseBox();
  236. });
  237.  
  238. if(document.getElementsByClassName("InfoBar__username")[0]) {
  239. themeColors();
  240. addInfo();
  241. if(enableCustomResponses) addResponseBox();
  242. }
  243.  
  244. //Took advice for the listbox CSS from moderncss.dev/custom-select-styles-with-pure-css, thanks!
  245. var css = `
  246. .profileIcon:hover {
  247. -ms-transform: scale(6);
  248. -webkit-transform: scale(6);
  249. transform: scale(6);
  250. }
  251. .profileIcon {
  252. position: relative;
  253. bottom: 4px;
  254. margin-bottom: 10px;
  255. float: left; border-radius: 50%;
  256. transition: transform .2s;
  257. }
  258. .InfoBar__recentsNone {
  259. color: #6e6e6e;
  260. }
  261. .InfoBar__metadata, .InfoBar__recents {
  262. margin: 6px 0;
  263. margin-left: 10px;
  264. }
  265. .value {
  266. color: ${dataColor};
  267. }
  268. .InfoBar__banText {
  269. padding-bottom: 15px;
  270. }
  271. .InfoBar__username, .InfoBar__username:visited {
  272. padding-left: 10px;
  273. }
  274. .ThreadViewer__infobarContainer {
  275. display: table;
  276. }
  277. .ThreadViewer__threadContainer.m-has-infobar {
  278. right: 340px;
  279. }
  280. .dataText {
  281. color: ${textColor};
  282. font-size: 13px;
  283. padding-left: 10px;
  284. }
  285. .dataTitle {
  286. color: ${titleColor};
  287. font-size: 15px;
  288. margin-bottom: 3px;
  289. margin-top: 5px;
  290. }
  291. .responseListbox {
  292. width: 50%;
  293. }
  294. :root {
  295. --select-border: #0079d3;
  296. --select-focus: blue;
  297. --select-arrow: var(--select-border);
  298. }
  299. *,
  300. *::before,
  301. *::after {
  302. box-sizing: border-box;
  303. }
  304. select {
  305. appearance: none;
  306. background-color: ${listBoxColor};
  307. color: ${textColor};
  308. border: none;
  309. padding: 0 1em 0 0;
  310. margin: 0;
  311. width: 100%;
  312. font-family: inherit;
  313. font-size: inherit;
  314. cursor: inherit;
  315. line-height: inherit;
  316. outline: none;
  317. position: relative;
  318. }
  319. .select {
  320. width: 100%;
  321. min-width: 15ch;
  322. max-width: 30ch;
  323. border: 1px solid var(--select-border);
  324. border-radius: 0.25em;
  325. padding: 0.25em 0.5em;
  326. font-size: 0.9rem;
  327. cursor: pointer;
  328. line-height: 1.1;
  329. background-color: ${listBoxColor};
  330. }
  331. select::-ms-expand {
  332. display: none;
  333. }
  334. option {
  335. white-space: normal;
  336. outline-color: var(--select-focus);
  337. }
  338. select:focus + .focus {
  339. position: absolute;
  340. top: -1px;
  341. left: -1px;
  342. right: -1px;
  343. bottom: -1px;
  344. border: 2px solid var(--select-focus);
  345. border-radius: inherit;
  346. }
  347. `;
  348.  
  349. //Apply the custom css
  350. var styleSheet = document.createElement("style");
  351. styleSheet.type = "text/css";
  352. styleSheet.innerText = css;
  353. document.head.appendChild(styleSheet);