[Reddit] Modmail++

Additional tools and information to Reddit's Modmail

当前为 2021-12-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name [Reddit] Modmail++
  3. // @namespace HKR
  4. // @match https://mod.reddit.com/mail/*
  5. // @grant none
  6. // @version 2.4
  7. // @author HKR
  8. // @description Additional tools and information to Reddit's Modmail
  9. // @icon https://www.redditstatic.com/modmail/favicon/favicon-32x32.png
  10. // @supportURL https://github.com/Hakorr/Userscripts/issues
  11. // @require https://cdn.jsdelivr.net/npm/party-js@2.1.2/bundle/party.js
  12. // ==/UserScript==
  13.  
  14. function main() { console.log("[Modmail++] %cMain function ran!", "color: grey");
  15. /* ---------- SETTINGS ---------- */
  16.  
  17. //Variables for the responses
  18. const subTag = $(".ThreadTitle__community").href.slice(23); //Format r/subreddit
  19. const userTag = "u/" + $(".InfoBar__username").innerText; //Format u/username
  20. const modmail = `[modmail](https://www.reddit.com/message/compose?to=/${keepPrefix(subTag,true)})`;
  21. const rules = `https://www.reddit.com/${keepPrefix(subTag,true)}/about/rules`;
  22.  
  23. //Text color settings
  24. var textColor = null, lightModeTextColor = "#6e6e6e", darkModeTextColor = "#757575";
  25.  
  26. //Title color settings
  27. var titleColor = null, lightModeTitleColor = "#2c2c2c", darkModeTitleColor = "#a7a7a7";
  28.  
  29. //Listbox color settings
  30. var listBoxColor = null, lightModeListColor = "#fff", darkModeListColor = "#242424";
  31.  
  32. //Data (Such as numbers) color settings
  33. const dataColor = "#0079d3";
  34.  
  35. //No response list is created if false
  36. const enableCustomResponses = true;
  37.  
  38. //No chat profile icons are added if false
  39. const chatProfileIcons = true;
  40.  
  41. const placeholderMessage = randItem([
  42. "Message...",
  43. "Look, a bird! Message...",
  44. "What have you been up to today? Message...",
  45. "Beautiful day, isn't it? Message...",
  46. "Was the weather nice? Message...",
  47. "You look good today! Message...",
  48. "What dreams did you see last night? Message...",
  49. "What did you do today? Message...",
  50. "What did you eat today? Message...",
  51. "Have you drank enough water? Message...",
  52. "Remember to stretch! Message...",
  53. "≖‿≖ I live inside of your walls. Message...",
  54. "(✿◠‿◠) Message...",
  55. ]);
  56.  
  57. /* Responses - Edit to your own liking, remove whatever you don't like!
  58. - name | The name of the response that will show on the listbox. (Example value: "Hello!")
  59. - replace | Replace all messagebox text if true, otherwise just add. (Example value: true)
  60. - subreddit | Visible only while on this subreddit's modmail. (Example value: "r/subreddit")
  61. - content | This text will be added to the messagebox once selected (Example value: "Hello world!")*/
  62. const responses = [
  63. {
  64. "name":"Select a template",
  65. "replace":true,
  66. "subreddit":"",
  67. "content":``
  68. },
  69. {
  70. "name":"Default Approved",
  71. "replace":true,
  72. "subreddit":"",
  73. "content":`Hey, approved the post!`
  74. },
  75. {
  76. "name":"Default Rule Broken",
  77. "replace":true,
  78. "subreddit":"",
  79. "content":`Your post broke our [rules](${rules}).\n\nThe action will not be reverted.`
  80. },
  81. {
  82. "name":"Add Rule Description",
  83. "replace":false,
  84. "subreddit":"",
  85. "content":`<open-rulelist-dialog>`
  86. },
  87. {
  88. "name":"Add Greetings",
  89. "replace":false,
  90. "subreddit":"",
  91. "content":`${randItem(["Greetings","Hello","Hi"])} ${userTag},\n\n`
  92. },
  93. {
  94. "name":"Add Thanks",
  95. "replace":false,
  96. "subreddit":"",
  97. "content":`\n\nThank you!`
  98. },
  99. {
  100. "name":"Add Subreddit Mention",
  101. "replace":false,
  102. "subreddit":"",
  103. "content":`${subTag}`
  104. },
  105. {
  106. "name":"Add User Mention",
  107. "replace":false,
  108. "subreddit":"",
  109. "content":`${userTag}`
  110. },
  111. {
  112. "name":"Add Modmail Link",
  113. "replace":false,
  114. "subreddit":"",
  115. "content":`${modmail}`
  116. },
  117. {
  118. "name":"Add Karma Link",
  119. "replace":false,
  120. "subreddit":"",
  121. "content":`[karma](https://reddit.zendesk.com/hc/en-us/articles/204511829-What-is-karma-)`
  122. },
  123. {
  124. "name":"Add Content Policy",
  125. "replace":false,
  126. "subreddit":"",
  127. "content":`[Content Policy](https://www.redditinc.com/policies/content-policy)`
  128. },
  129. {
  130. "name":"Add User Agreement",
  131. "replace":false,
  132. "subreddit":"",
  133. "content":`[User Agreement](https://www.redditinc.com/policies/user-agreement)`
  134. },
  135. {
  136. "name":"Add Rickroll",
  137. "replace":false,
  138. "subreddit":"",
  139. "content":`[link](https://www.youtube.com/watch?v=dQw4w9WgXcQ)`
  140. }
  141. ];
  142.  
  143. /* ---------- SETTINGS END ---------- */
  144.  
  145. /* ---------- JAVASCRIPT & HTML ---------- */
  146.  
  147. function time(UNIX_timestamp) {
  148. //Get UNIX time
  149. var d = new Date(UNIX_timestamp * 1000);
  150. const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  151.  
  152. //Get year, month, date, hour, min & sec variables
  153. var year = d.getFullYear(),
  154. monthNum = d.getMonth() + 1,
  155. month = months[d.getMonth()],
  156. date = d.getDate(),
  157. hour = fixnumber(d.getHours()),
  158. min = fixnumber(d.getMinutes()),
  159. sec = fixnumber(d.getSeconds());
  160.  
  161. //Construct the time (DD/MM/YY HH/MM/SS) and return it
  162. var time = `${date}.${monthNum}.${year} ${hour}:${min}:${sec}`;
  163. return time;
  164. }
  165. //Function to avoid XSS
  166. function sanitize(evilstring) {
  167. const decoder = document.createElement('div')
  168. decoder.innerHTML = evilstring;
  169. return decoder.textContent;
  170. }
  171.  
  172. //Appends the info (main, karma, links) to the page
  173. function addInfo() {
  174. //Load and parse username
  175. var username = removePrefix($(".InfoBar__username").innerText);
  176. var about = `https://www.reddit.com/user/${username}/about.json`;
  177. const xhr = new XMLHttpRequest();
  178. //Once the user info JSON has been fetched
  179. xhr.onload = () => {
  180. var user = JSON.parse(xhr.responseText);
  181. //Separator HTML element
  182. var seperator = document.createElement('div');
  183. seperator.innerHTML = '<div class="InfoBar__modActions"></div>';
  184. //HTML element that contains all the data
  185. var userDetails = document.createElement('div');
  186. userDetails.classList.add("InfoBar__age");
  187. userDetails.innerHTML = `<img class="profileIcon" src="${user.data.icon_img}" width="25">
  188. <a class="InfoBar__username" href="https://www.reddit.com/user/${user.data.name}">${removePrefix(user.data.subreddit.display_name_prefixed)}</a>
  189. <h1 style="color: ${textColor} ; font-size: 11px; margin-top: 17px; margin-bottom: 10px;">${sanitize(user.data.subreddit.public_description)}</h1>
  190. <h1 class="dataTitle">Main</h1>
  191. <div class="dataText">
  192. <p>Created: <span class="value">${time(user.data.created)}</span></p>
  193. <p>UserID: <span class="value">${user.data.id}</span></p>
  194. <p>Verified: <span class="value">${user.data.verified}</span></p>
  195. <p>Employee: <span class="value">${user.data.is_employee}</span></p>
  196. <p>NSFW Profile: <span class="value">${user.data.subreddit.over_18}</span></p>
  197. </div>
  198. <h1 class="dataTitle">Karma</h1>
  199. <div class="dataText">
  200. <p>Post: <span class="value">${user.data.link_karma}</span></p>
  201. <p>Comment: <span class="value">${user.data.comment_karma}</span></p>
  202. <p>Total: <span class="value">${user.data.total_karma}</span></p>
  203. <p>Awardee: <span class="value">${user.data.awardee_karma}</span></p>
  204. <p>Awarder: <span class="value">${user.data.awarder_karma}</span></p>
  205. </div>
  206. <h1 class="dataTitle">Links</h1>
  207. <div style="padding-left: 10px;">
  208. <a class="InfoBar__recent" href="https://redditmetis.com/user/${user.data.name}" target="_blank">Redditmetis</a>
  209. <a class="InfoBar__recent" href="https://www.reddit.com/search?q=${user.data.name}" target="_blank">Reddit Search</a>
  210. <a class="InfoBar__recent" href="https://www.google.com/search?q=%22${user.data.name}%22" target="_blank">Google Search</a>
  211. </div>`;
  212.  
  213. //Add profile pictures
  214. if(chatProfileIcons) {
  215. //Icon element
  216. var chatProfileIcon = document.createElement('div');
  217. chatProfileIcon.innerHTML = `<img class="chatProfileIcon" src="${user.data.icon_img}" width="25">`;
  218.  
  219. //Loop trough every username on chat
  220. for(var i = 0; i < $$(".ThreadPreview__author").length; i++) {
  221. //Get username (u/xxxxxx)
  222. let name = $$(".Author__text")[i].innerText;
  223. //Check if there is an icon appended already
  224. let exists = $$(".ThreadPreview__author")[i].childNodes.length == 1 ? false : true;
  225. //If the username is the user (non-mod)
  226. if(removePrefix(name) == username && !exists) {
  227. //Append the icon next to the username -> [icon] u/username
  228. $$(".ThreadPreview__author")[i].insertBefore(chatProfileIcon.cloneNode(true), $$(".ThreadPreview__author")[i].firstChild);
  229. }
  230. }
  231. }
  232. //Append the elements
  233. $(".ThreadViewer__infobar").appendChild(seperator);
  234. $(".ThreadViewer__infobar").appendChild(seperator);
  235. $(".ThreadViewer__infobar").appendChild(userDetails);
  236. $(".ThreadViewer__infobar").appendChild($(".ThreadViewer__infobar").firstChild);
  237. $(".InfoBar").appendChild($(".InfoBar__modActions"));
  238. $(".InfoBar").insertBefore($(".InfoBar__modActions"),$(".InfoBar").firstChild);
  239. if($(".InfoBar__banText"))
  240. $(".ThreadViewer__infobar").insertBefore($(".InfoBar__banText"),$(".ThreadViewer__infobar").firstChild);
  241. //Remove certain elements
  242. $$(".InfoBar__username")[1].outerHTML = "";
  243. $$(".InfoBar__age")[1].outerHTML = "";
  244. $$(".InfoBar__modActions")[1].outerHTML = "";
  245. };
  246. //Get user details
  247. xhr.open('GET', about);
  248. xhr.send();
  249. }
  250.  
  251. function makeListValue(name,description) {
  252. return `<div class="listValue" value='${description}'><input onclick="selected(this)" name="subredditRule" type="radio" id='${name}' value='${name}' ><label for='${name}'>${name}</label></div>`
  253. }
  254.  
  255. //Appends the response template listbox to the page
  256. function addResponseBox() {
  257. //Hide real textarea and append a new one (so the text won't get removed by the sync feature)
  258. $(".ThreadViewerReplyForm__replyText").style.cssText += 'display: none';
  259. const txtArea = document.createElement("textarea");
  260. txtArea.setAttribute('class', 'Textarea ThreadViewerReplyForm__replyText ');
  261. txtArea.setAttribute('id', 'realTextarea');
  262. txtArea.setAttribute('name', 'body');
  263. txtArea.setAttribute('placeholder', `${placeholderMessage}`);
  264. $(".ThreadViewerReplyForm").insertBefore(txtArea,$(".ThreadViewerReplyForm__replyFooter"));
  265. //Fix clear textarea - will not clear it if the moderator stays to send another message
  266. var replyButton = $(".ThreadViewerReplyForm__replyButton")
  267. const clearBoxJS = `setTimeout(function(){document.getElementById("realTextarea").value = ""; console.log("[Modmail++] Cleared the textarea!");},500)`;
  268. replyButton.setAttribute("onclick", clearBoxJS);
  269.  
  270. //Listbox element
  271. var responseBox = document.createElement('div');
  272. responseBox.classList.add("select");
  273. responseBox.innerHTML = `<h2 class="dataTitle">Response Templates</h2>
  274. <select id="responseListbox" onchange="listBoxChanged(this.value);" onfocus="this.selectedIndex = -1;"/>
  275. <span class="focus"></span>`;
  276.  
  277. const xhr = new XMLHttpRequest();
  278. const ruleJSON = rules + ".json";
  279.  
  280. //Once the user info JSON has been fetched
  281. xhr.onload = () => {
  282. try {
  283. $$(".subredditRuleList").forEach(elem => elem.remove());
  284. let json = JSON.parse(xhr.responseText);
  285. let listContent = "";
  286. for(let i = 0; i < json.rules.length; i++) {
  287. listContent += makeListValue(json.rules[i].short_name,json.rules[i].description);
  288. }
  289. var ruleList = document.createElement('div');
  290. ruleList.classList.add("subredditRuleList");
  291. ruleList.innerHTML = `<div class="ruleDiv" style="background-color: rgba(26, 26, 27, 0.6); visibility: hidden">
  292. <div aria-modal="true" class="dialogWindow" role="dialog" tabindex="-1">
  293. <div class="listWindow">
  294. <div class="ruleList">
  295. <div class="ruleHeader">Select a rule<svg onclick="closeIconClicked()" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" class="closeIconSVG"><polygon fill="inherit" points="11.649 9.882 18.262 3.267 16.495 1.5 9.881 8.114 3.267 1.5 1.5 3.267 8.114 9.883 1.5 16.497 3.267 18.264 9.881 11.65 16.495 18.264 18.262 16.497"></polygon></svg></div>
  296. <fieldset class="fieldSet">
  297. <div class="title"><span>Which community rule did the user violate?</span></div>
  298. <div class="listBox">
  299. ${listContent}
  300. </div>
  301. <div class="infoIcon"><svg class="infoIconSVG" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g><path d="M10,8.5 C10.553,8.5 11,8.948 11,9.5 L11,13.5 C11,14.052 10.553,14.5 10,14.5 C9.447,14.5 9,14.052 9,13.5 L9,9.5 C9,8.948 9.447,8.5 10,8.5 Z M10.7002,5.79 C10.8012,5.89 10.8702,6 10.9212,6.12 C10.9712,6.24 11.0002,6.37 11.0002,6.5 C11.0002,6.57 10.9902,6.63 10.9802,6.7 C10.9712,6.76 10.9502,6.82 10.9212,6.88 C10.9002,6.94 10.8702,7 10.8302,7.05 C10.7902,7.11 10.7502,7.16 10.7002,7.21 C10.6602,7.25 10.6102,7.29 10.5512,7.33 C10.5002,7.37 10.4402,7.4 10.3812,7.42 C10.3202,7.45 10.2612,7.47 10.1902,7.48 C10.1312,7.49 10.0602,7.5 10.0002,7.5 C9.7402,7.5 9.4802,7.39 9.2902,7.21 C9.1102,7.02 9.0002,6.77 9.0002,6.5 C9.0002,6.37 9.0302,6.24 9.0802,6.12 C9.1312,5.99 9.2002,5.89 9.2902,5.79 C9.5202,5.56 9.8702,5.46 10.1902,5.52 C10.2612,5.53 10.3202,5.55 10.3812,5.58 C10.4402,5.6 10.5002,5.63 10.5512,5.67 C10.6102,5.71 10.6602,5.75 10.7002,5.79 Z M10,16 C6.691,16 4,13.309 4,10 C4,6.691 6.691,4 10,4 C13.309,4 16,6.691 16,10 C16,13.309 13.309,16 10,16 M10,2 C5.589,2 2,5.589 2,10 C2,14.411 5.589,18 10,18 C14.411,18 18,14.411 18,10 C18,5.589 14.411,2 10,2"></path></g></svg>
  302. <div class="infoBox">
  303. <p><span>Not sure? </span><a href="https://www.reddit.com/${keepPrefix(subTag)}/about/rules" target="_blank" rel="noopener noreferrer">Read ${subTag}'s rules</a></p>
  304. </div>
  305. </div>
  306. <footer class="bottomFooter"><button type="button" disabled="" onclick="selectButtonClicked()" class="selectButton">Select</button></footer>
  307. </fieldset>
  308. </div>
  309. </div>
  310. </div>
  311. </div>`;
  312.  
  313. $("body").appendChild(ruleList);
  314. } catch(e) {
  315. console.log("[Modmail++] %cFailed to load subreddit rules, possibly a private subreddit?", "color: red");
  316. }
  317. };
  318.  
  319. //Get subreddit's rules
  320. xhr.open('GET', ruleJSON);
  321. xhr.send();
  322.  
  323. //Script element to head
  324. var headJS = document.createElement('script');
  325. headJS.innerHTML = `
  326. var responses = ${JSON.stringify(responses)};
  327. var ruleListActivator = "<open-rulelist-dialog>";
  328. function listBoxChanged(message) {
  329. if(message == ruleListActivator) {
  330. let ruleDiv = document.getElementsByClassName("ruleDiv")[0];
  331. ruleDiv.style.visibility = "visible";
  332. } else {
  333. var messageBox = document.getElementById("realTextarea");
  334. var response = responses.find(x => x.content == message);
  335. response.replace ? messageBox.value = message : messageBox.value += message;
  336. console.log("[Modmail++] New messageBox value: %c" + messageBox.value,"color: orange");
  337. }
  338. }
  339. //Implement listbox select highlight
  340. function selected(element){
  341. let selectColor = "#79797959";
  342. let selectedElem = document.getElementById("currentlySelected");
  343. //If an elem already selected, reset the id and set its background color to nothing
  344. if(selectedElem) { selectedElem.style.backgroundColor = ""; selectedElem.id = ""; }
  345. element.parentElement.style.backgroundColor = selectColor;
  346. element.parentElement.id = "currentlySelected";
  347. document.getElementsByClassName("selectButton")[0].disabled = false;
  348. }
  349. function selectButtonClicked() {
  350. let selectedElem = document.getElementById("currentlySelected");
  351. let messageBox = document.getElementById("realTextarea");
  352. if(selectedElem) {
  353. let response = responses.find(x => x.content == ruleListActivator);
  354. let message = \`Rule | Description\n---------|----------\n\${selectedElem.children[1].textContent} | \${selectedElem.getAttribute('value')}\`;
  355. response.replace ? messageBox.value = message : messageBox.value += message;
  356. console.log("[Modmail++] New messageBox value: %c" + messageBox.value,"color: orange");
  357. closeIconClicked();
  358. }
  359. }
  360. function closeIconClicked() {
  361. let ruleDiv = document.getElementsByClassName("ruleDiv")[0];
  362. ruleDiv.style.visibility = "hidden";
  363. }`;
  364. //Add all the responses to the listbox
  365. function populate() {
  366. var select = $("#responseListbox");
  367. for(var i = 0; i < responses.length; i++) {
  368. //Sorry if it looks a bit complicated
  369. if(keepPrefix(responses[i].subreddit.toLowerCase(), true) == keepPrefix(subTag.toLowerCase(), true) || responses[i].subreddit == "")
  370. select.options[select.options.length] = new Option(responses[i].name, responses[i].content);
  371. }
  372. }
  373. $(".ThreadViewer__replyContainer").prepend(responseBox);
  374. var head = $("head");
  375. head.appendChild(headJS);
  376.  
  377. $(".ThreadViewer__replyContainer").insertBefore($(".ThreadViewer__typingIndicator"),$(".select"));
  378. populate();
  379. }
  380.  
  381. //Detects the current theme (dark/light) and applies the correct color (for the added elements)
  382. function themeColors() {
  383. var darkTheme = $$(".theme-dark").length ? true : false;
  384. if(darkTheme) {
  385. console.log("[Modmail++] Dark mode detected! Setting colors...");
  386. textColor = darkModeTextColor;
  387. titleColor = darkModeTitleColor;
  388. listBoxColor = darkModeListColor;
  389. } else {
  390. console.log("[Modmail++] Light mode detected! Setting colors...");
  391. textColor = lightModeTextColor;
  392. titleColor = lightModeTitleColor;
  393. listBoxColor = lightModeListColor;
  394. }
  395. }
  396.  
  397. themeColors();
  398.  
  399. //Took advice for the listbox CSS from moderncss.dev/custom-select-styles-with-pure-css, thanks!
  400. var css = `.profileIcon:hover {
  401. -ms-transform: scale(6);
  402. -webkit-transform: scale(6);
  403. transform: scale(6);
  404. }
  405. .profileIcon {
  406. position: relative;
  407. bottom: 4px;
  408. margin-bottom: 10px;
  409. float: left; border-radius: 50%;
  410. transition: transform .1s;
  411. }
  412. .InfoBar__recentsNone {
  413. color: #6e6e6e;
  414. }
  415. .InfoBar__metadata, .InfoBar__recents {
  416. margin: 6px 0;
  417. margin-left: 10px;
  418. }
  419. .value {
  420. color: ${dataColor};
  421. }
  422. .InfoBar__banText {
  423. padding-bottom: 15px;
  424. }
  425. .InfoBar__username, .InfoBar__username:visited {
  426. padding-left: 10px;
  427. }
  428. .ThreadViewer__infobarContainer {
  429. display: table;
  430. }
  431. .dataText {
  432. color: ${textColor};
  433. font-size: 13px;
  434. padding-left: 10px;
  435. }
  436. .dataTitle {
  437. color: ${titleColor};
  438. font-size: 15px;
  439. margin-bottom: 3px;
  440. margin-top: 5px;
  441. }
  442. .responseListbox {
  443. width: 50%;
  444. cursor: pointer;
  445. }
  446. :root {
  447. --select-border: #0079d3;
  448. --select-focus: blue;
  449. --select-arrow: var(--select-border);
  450. }
  451. *,
  452. *::before,
  453. *::after {
  454. box-sizing: border-box;
  455. }
  456. select {
  457. appearance: none;
  458. background-color: ${listBoxColor};
  459. color: ${textColor};
  460. border: none;
  461. padding: 0 1em 0 0;
  462. margin: 0;
  463. width: 100%;
  464. cursor: pointer;
  465. font-family: inherit;
  466. font-size: inherit;
  467. line-height: inherit;
  468. outline: none;
  469. position: relative;
  470. }
  471. .select {
  472. width: 100%;
  473. min-width: 15ch;
  474. max-width: 30ch;
  475. border: 1px solid var(--select-border);
  476. border-radius: 0.25em;
  477. padding: 0.3em 0.4em;
  478. font-size: 0.9rem;
  479. line-height: 1.1;
  480. background-color: ${listBoxColor};
  481. margin-bottom: 15px;
  482. }
  483. select::-ms-expand {
  484. display: none;
  485. }
  486. option {
  487. white-space: normal;
  488. outline-color: var(--select-focus);
  489. }
  490. select:focus + .focus {
  491. position: absolute;
  492. top: -1px;
  493. left: -1px;
  494. right: -1px;
  495. bottom: -1px;
  496. border: 2px solid var(--select-focus);
  497. border-radius: inherit;
  498. }
  499. .Author__text {
  500. padding: 6px 0;
  501. }
  502. .chatProfileIcon {
  503. margin-right: 7px;
  504. transition: transform .1s;
  505. border-radius: 50%;
  506. }
  507. .App__page {
  508. background: var(--color-tone-8);
  509. }
  510. ::-webkit-scrollbar {
  511. width: 10px;
  512. }
  513. ::-webkit-scrollbar-track {
  514. background: ${listBoxColor};
  515. }
  516. ::-webkit-scrollbar-thumb {
  517. background: #888;
  518. }
  519. ::-webkit-scrollbar-thumb:hover {
  520. background: #555;
  521. }
  522. .subredditRuleList {
  523. --newRedditTheme-bodyText: ${titleColor};
  524. --newRedditTheme-metaText: ${textColor};
  525. --newRedditTheme-navIconFaded10: rgba(215,218,220,0.1);
  526. --newRedditTheme-actionIconTinted80: #9a9b9c;
  527. --newRedditTheme-activeShaded90: #006cbd;
  528. --newRedditTheme-actionIconAlpha20: rgba(129,131,132,0.2);
  529. --newCommunityTheme-actionIcon: #818384;
  530. --newRedditTheme-bodyTextAlpha03: ${listBoxColor};
  531. --newRedditTheme-navIcon: #D7DADC;
  532. --newCommunityTheme-line: #343536;
  533. --newCommunityTheme-body: #1A1A1B;
  534. }
  535. .ruleList {
  536. padding: 0 24px 0 20px;
  537. background: var(--newRedditTheme-bodyTextAlpha03);
  538. max-height: 100%;
  539. }
  540. html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, button, cite, code, del, dfn, em, img, input, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  541. margin: 0;
  542. padding: 0;
  543. border: 0;
  544. font-size: 100%;
  545. font: inherit;
  546. vertical-align: baseline;
  547. }
  548. .dialogWindow {
  549. pointer-events: auto;
  550. }
  551. .ruleDiv {
  552. -ms-flex-align: center;
  553. align-items: center;
  554. box-sizing: border-box;
  555. display: -ms-flexbox;
  556. display: flex;
  557. height: 100%;
  558. padding: 75px 30px 20px;
  559. pointer-events: none;
  560. position: fixed;
  561. top: 0;
  562. width: 100%;
  563. z-index: 55;
  564. }
  565. .dialogWindow {
  566. background-color: var(--newCommunityTheme-body);
  567. border: 1px solid var(--newCommunityTheme-line);
  568. border-radius: 4px;
  569. box-shadow: 0 2px 20px 0 rgb(0 0 0 / 30%);
  570. margin: auto;
  571. pointer-events: auto;
  572. z-index: 55;
  573. }
  574. .listWindow {
  575. width: 550px;
  576. position: relative;
  577. }
  578. .ruleHeader {
  579. height: 50px;
  580. border-bottom: 1px solid var(--newRedditTheme-bodyTextAlpha03);
  581. position: relative;
  582. display: -ms-flexbox;
  583. display: flex;
  584. -ms-flex-align: center;
  585. align-items: center;
  586. padding: 0 24px 0 20px;
  587. margin: 0 -24px 0 -20px;
  588. font-weight: 700;
  589. font-size: 14px;
  590. color: var(--newRedditTheme-metaText);
  591. }
  592. .infoIcon {
  593. background: #86848412;
  594. border-radius: 8px;
  595. padding: 10px 16px 10px 12px;
  596. display: -ms-flexbox;
  597. display: flex;
  598. box-sizing: border-box;
  599. margin-top: 16px;
  600. }
  601. .bottomFooter {
  602. box-shadow: 0 -1px 0 var(--newRedditTheme-bodyTextAlpha03);
  603. padding: 20px 0 16px;
  604. min-height: 80px;
  605. display: -ms-flexbox;
  606. display: flex;
  607. box-sizing: border-box;
  608. bottom: 0;
  609. left: 0;
  610. }
  611. .closeIconSVG {
  612. margin-left: auto;
  613. margin-right: -4px;
  614. cursor: pointer;
  615. height: 20px;
  616. padding: 4px;
  617. width: 20px;
  618. fill: var(--newCommunityTheme-actionIcon);
  619. }
  620. .infoIconSVG {
  621. -ms-flex: 0 0 20px;
  622. flex: 0 0 20px;
  623. width: 20px;
  624. margin-right: 12px;
  625. fill: #878a8c;
  626. }
  627. .selectButton:disabled {
  628. opacity: .5;
  629. }
  630. .selectButton {
  631. margin-top: 8px;
  632. -ms-flex: 0 0 150px;
  633. flex: 0 0 150px;
  634. background: var(--newRedditTheme-activeShaded90);
  635. height: 31px;
  636. border-radius: 100px;
  637. color: #fff;
  638. -ms-flex-item-align: end;
  639. align-self: flex-end;
  640. margin-left: auto;
  641. font-size: 12px;
  642. font-weight: 700;
  643. text-transform: uppercase;
  644. letter-spacing: .05em;
  645. outline: none!important;
  646. }
  647. .infoBox {
  648. -ms-flex: 0 1 auto;
  649. flex: 0 1 auto;
  650. font-size: 14px;
  651. line-height: 1.45;
  652. letter-spacing: -.01em;
  653. color: var(--newRedditTheme-metaText);
  654. }
  655. .infoBox a {
  656. color: #24a0ed;
  657. }
  658. button {
  659. background: transparent;
  660. border: none;
  661. color: inherit;
  662. cursor: pointer;
  663. padding: initial;
  664. }
  665. input {
  666. -webkit-writing-mode: horizontal-tb !important;
  667. text-rendering: auto;
  668. color: -internal-light-dark(black, white);
  669. letter-spacing: normal;
  670. word-spacing: normal;
  671. text-transform: none;
  672. text-indent: 0px;
  673. text-shadow: none;
  674. display: inline-block;
  675. text-align: start;
  676. appearance: auto;
  677. background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59));
  678. -webkit-rtl-ordering: logical;
  679. cursor: text;
  680. margin: 0em;
  681. font: 400 13.3333px Arial;
  682. padding: 1px 2px;
  683. border-width: 2px;
  684. border-style: inset;
  685. border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133));
  686. border-image: initial;
  687. }
  688. body {
  689. min-height: calc(100vh - 48px);
  690. line-height: 1;
  691. font-family: IBMPlexSans, Arial, sans-serif;
  692. -webkit-font-smoothing: antialiased;
  693. }
  694. ._3kEv5z1lDKGV8PQ5ijp4Uh {
  695. background-size: 24px 24px;
  696. background-position: 11px 6px;
  697. padding-left: 42px!important;
  698. background-repeat: no-repeat;
  699. }
  700. .title {
  701. margin-top: 16px;
  702. font-size: 16px;
  703. line-height: 1.2;
  704. font-weight: 700;
  705. color: var(--newRedditTheme-bodyText);
  706. }
  707. .fieldSet {
  708. display: -ms-flexbox;
  709. display: flex;
  710. -ms-flex-direction: column;
  711. flex-direction: column;
  712. box-sizing: border-box;
  713. }
  714. .listValue label {
  715. padding: 0 72px 0 20px;
  716. display: -ms-flexbox;
  717. display: flex;
  718. height: 100%;
  719. -ms-flex-align: center;
  720. align-items: center;
  721. cursor: pointer;
  722. font-size: 14px;
  723. font-weight: 700;
  724. color: var(--newRedditTheme-metaText);
  725. position: relative;
  726. }
  727. .listValue {
  728. box-sizing: border-box;
  729. height: 64px;
  730. border-top: 1px solid var(--newRedditTheme-navIconFaded10);
  731. }
  732. .listBox {
  733. margin: 16px -24px 0 -20px;
  734. max-height: 60vh;
  735. min-height: 100px;
  736. overflow: auto;
  737. }
  738. .listValue input {
  739. visibility: hidden;
  740. display: none;
  741. }
  742. .StyledHtml tr {
  743. color: ${titleColor};
  744. }
  745. .StyledHtml td {
  746. color: ${textColor};
  747. }`;
  748.  
  749. //Apply the custom css
  750. var styleSheet = document.createElement("style");
  751. styleSheet.type = "text/css";
  752. styleSheet.innerText = css;
  753. document.head.appendChild(styleSheet);
  754.  
  755. addInfo();
  756. if(enableCustomResponses && $("#responseListbox") == null) addResponseBox();
  757. console.log("[Modmail++] %cLoaded!", "color: lime");
  758.  
  759. } /* End of Main function */
  760.  
  761. console.log("[Modmail++] %cScript started!", "color: green");
  762.  
  763. const $ = document.querySelector.bind(document);
  764. const $$ = document.querySelectorAll.bind(document);
  765.  
  766. //Returns a random item from array
  767. const randItem = itemArr => itemArr[Math.floor(Math.random() * itemArr.length)];
  768.  
  769. //Adds a zero suffix if x < 10
  770. const fixnumber = number => number < 10 ? "0" + number : number;
  771.  
  772. //Removes the Reddit prefix
  773. const removePrefix = username => ["r/","u/"].some(tag => username.includes(tag)) ? username.slice(2) : username;
  774.  
  775. //Adds the Reddit prefix if nonexistant
  776. const keepPrefix = (username, subreddit) => ["r/","u/"].some(tag => username.includes(tag)) ? username : subreddit ? `r/${username}` : `u/${username}`;
  777.  
  778. $(".Sidebar__titleMessage").setAttribute("onclick","window.open('https://github.com/Hakorr/Userscripts/tree/main/Reddit.com/ModmailExtraInfo')");
  779. $(".Sidebar__titleMessage").setAttribute("style","cursor: pointer");
  780. $(".Sidebar__titleMessage").innerText = "Modmail++";
  781.  
  782. var run = true;
  783.  
  784. /* Start Main function when visiting new modmail */
  785. var pageURLCheckTimer = setInterval (function () {
  786. if (this.lastPathStr !== location.pathname)
  787. {
  788. this.lastPathStr = location.pathname;
  789.  
  790. console.log("[Modmail++] %cNew page detected!", "color: gold")
  791. run = true;
  792.  
  793. let interval = setInterval (function () {
  794. //Add confetti explosion if no mail
  795. if($(".NoThreadMessage__generic") && run) {
  796. clearInterval(interval);
  797. run = false;
  798. console.log("[Modmail++] %cNo modmail!", "color: lime");
  799. party.confetti($(".NoThreadMessage__generic"), {
  800. count: party.variation.range(20, 40),
  801. spread: 50
  802. });
  803. }
  804.  
  805. //User is on modmail "chat" page
  806. if($(".InfoBar__username")&& run) {
  807. clearInterval(interval);
  808. run = false;
  809. if($("body")) main();
  810. }
  811. }, 5);
  812. }
  813. }, 100);