您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Additional tools and information to Reddit's Modmail
当前为
// ==UserScript== // @name [Reddit] Modmail++ // @namespace HKR // @match https://mod.reddit.com/mail/* // @grant none // @version 2.0 // @author HKR // @description Additional tools and information to Reddit's Modmail // @icon https://www.redditstatic.com/modmail/favicon/favicon-32x32.png // @supportURL https://github.com/Hakorr/Userscripts/issues // ==/UserScript== console.log("[ModmailExtraInfo] %cScript started!", "color: green"); /* Do not touch */ const $ = document.querySelector.bind(document); const $$ = document.querySelectorAll.bind(document); var first = false; /* Do not touch */ function main() { console.log("[ModmailExtraInfo] %cMain function ran!", "color: grey"); /* SETTINGS */ /* NOTE (If you want to use the Custom Responses): Reddit's sync feature removes the script's added text. - If you block "https://oauth.reddit.com/api/mod/conversations/*****?markRead=false&redditWebClient=modmail", the added text will stay. */ //Variables for the responses const subTag = $(".ThreadTitle__community").href.slice(23); const userTag = "u/" + $(".InfoBar__username").innerText; const modmail = `[modmail](https://www.reddit.com/message/compose?to=/${subTag})`; const rules = `https://www.reddit.com/${subTag}/about/rules`; const randItem = itemArr => itemArr[Math.floor(Math.random() * itemArr.length)]; //Text color settings var textColor = null, lightModeTextColor = "#6e6e6e", darkModeTextColor = "#757575"; //Title color settings var titleColor = null, lightModeTitleColor = "#2c2c2c", darkModeTitleColor = "#a7a7a7"; //Listbox color settings var listBoxColor = null, lightModeListColor = "#fff", darkModeListColor = "#242424"; //Data (Such as numbers) color settings const dataColor = "#0079d3"; //No response list is created if false const enableCustomResponses = true; //No chat profile icons are added if false const chatProfileIcons = true; //Feel free to edit and add more responses suitable for you! Replace means if to replace all text or just to add the text. const responses = [ { "name":"Select a template", "replace":true, "content":`` }, { "name":"Default approved", "replace":true, "content":`Hey, approved the post!` }, { "name":"Default rule broken", "replace":true, "content":`Your post broke our [rules](${rules}).\n\nThe action will not be reverted.` }, { "name":"Add greetings", "replace":true, "content":`${randItem(["Greetings","Hello","Hi"])} ${userTag},\n\n` }, { "name":"Add thanks", "replace":false, "content":`\n\nThank you!` }, { "name":"Add subreddit mention", "replace":false, "content":`${subTag}` }, { "name":"Add user mention", "replace":false, "content":`${userTag}` }, { "name":"Add Modmail link", "replace":false, "content":`${modmail}` }, { "name":"Add Content Policy", "replace":false, "content":`[Content Policy](https://www.redditinc.com/policies/content-policy)` }, { "name":"Add User Agreement", "replace":false, "content":`[User Agreement](https://www.redditinc.com/policies/user-agreement)` }, { "name":"Add Rickroll", "replace":false, "content":`[link](https://www.youtube.com/watch?v=dQw4w9WgXcQ)` } ]; /* ---------- JS & HTML ---------- */ function time(UNIX_timestamp){ //Get UNIX time var d = new Date(UNIX_timestamp * 1000); const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; //Get year, month, date, hour, min & sec variables var year = d.getFullYear(), monthNum = d.getMonth() + 1, month = months[d.getMonth()], date = d.getDate(), hour = fixnumber(d.getHours()), min = fixnumber(d.getMinutes()), sec = fixnumber(d.getSeconds()); //Construct the time (DD/MM/YY HH/MM/SS) and return it var time = `${date}.${monthNum}.${year} ${hour}:${min}:${sec}`; return time; } //Adds a zero suffix if x < 10 const fixnumber = number => number < 10 ? "0" + number : number; //Removes the u/ prefix const removePrefix = username => username.includes("u/") ? username.slice(2) : username; //Adds the u/ prefix if nonexistant const keepPrefix = username => username.includes("u/") ? username : "u/" + username; //Function to avoid XSS function sanitize(evilstring) { const decoder = document.createElement('div') decoder.innerHTML = evilstring; return decoder.textContent; } //Appends the info (main, karma, links) to the page function addInfo(){ //Load and parse username var username = removePrefix($(".InfoBar__username").innerText); var about = "https://www.reddit.com/user/" + username + "/about.json"; const xhr = new XMLHttpRequest(); //Once the user info JSON has been fetched xhr.onload = () => { var user = JSON.parse(xhr.responseText); //Separator HTML element var seperator = document.createElement('div'); seperator.innerHTML = '<div class="InfoBar__modActions"></div>'; //HTML element that contains all the data var userDetails = document.createElement('div'); userDetails.classList.add("InfoBar__age"); userDetails.innerHTML = `<img class="profileIcon" src="${user.data.icon_img}" width="25"> <a class="InfoBar__username" href="https://www.reddit.com/user/${user.data.name}">${user.data.subreddit.display_name_prefixed}</a> <h1 style="color: ${textColor} ; font-size: 11px; margin-top: 17px; margin-bottom: 10px;">${sanitize(user.data.subreddit.public_description)}</h1> <h1 class="dataTitle">Main</h1> <div class="dataText"> <p>Created: <span class="value">${time(user.data.created)}</span></p> <p>UserID: <span class="value">${user.data.id}</span></p> <p>Verified: <span class="value">${user.data.verified}</span></p> <p>Employee: <span class="value">${user.data.is_employee}</span></p> <p>NSFW Profile: <span class="value">${user.data.subreddit.over_18}</span></p> </div> <h1 class="dataTitle">Karma</h1> <div class="dataText"> <p>Post: <span class="value">${user.data.link_karma}</span></p> <p>Comment: <span class="value">${user.data.comment_karma}</span></p> <p>Total: <span class="value">${user.data.total_karma}</span></p> <p>Awardee: <span class="value">${user.data.awardee_karma}</span></p> <p>Awarder: <span class="value">${user.data.awarder_karma}</span></p> </div> <h1 class="dataTitle">Links</h1> <div style="padding-left: 10px;"> <a class="InfoBar__recent" href="https://redditmetis.com/user/${user.data.name}" target="_blank">Redditmetis</a> <a class="InfoBar__recent" href="https://www.reddit.com/search?q=${user.data.name}" target="_blank">Reddit Search</a> <a class="InfoBar__recent" href="https://www.google.com/search?q=%22${user.data.name}%22" target="_blank">Google Search</a> </div>`; //Add profile pictures if(chatProfileIcons) { //Icon element var chatProfileIcon = document.createElement('div'); chatProfileIcon.innerHTML = `<img class="chatProfileIcon" src="${user.data.icon_img}" width="25">`; //Loop trough every username on chat for(var i = 0; i < $$(".ThreadPreview__author").length; i++) { //Get username (u/xxxxxx) let name = $$(".Author__text")[i].innerText; //Check if there is an icon appended already let exists = $$(".ThreadPreview__author")[i].childNodes.length == 1 ? false : true; //If the username is the user (non-mod) if(removePrefix(name) == username && !exists) { //Append the icon next to the username -> [icon] u/username $$(".ThreadPreview__author")[i].insertBefore(chatProfileIcon.cloneNode(true), $$(".ThreadPreview__author")[i].firstChild); } } } //Append the elements $(".ThreadViewer__infobar").appendChild(seperator); $(".ThreadViewer__infobar").appendChild(seperator); $(".ThreadViewer__infobar").appendChild(userDetails); $(".ThreadViewer__infobar").appendChild($(".ThreadViewer__infobar").firstChild); $(".InfoBar").appendChild($(".InfoBar__modActions")); $(".InfoBar").insertBefore($(".InfoBar__modActions"),$(".InfoBar").firstChild); if($(".InfoBar__banText")) $(".ThreadViewer__infobar").insertBefore($(".InfoBar__banText"),$(".ThreadViewer__infobar").firstChild); //Remove certain elements $$(".InfoBar__username")[1].outerHTML = ""; $$(".InfoBar__age")[1].outerHTML = ""; $$(".InfoBar__modActions")[1].outerHTML = ""; }; //Get user details xhr.open('GET', about); xhr.send(); } //Appends the response template listbox to the page function addResponseBox() { //Listbox element var responseBox = document.createElement('div'); responseBox.classList.add("select"); responseBox.innerHTML = `<h2 class="dataTitle">Response templates</h2> <select id="responseListbox" onchange="listBoxChanged(this.value);" onfocus="this.selectedIndex = -1;"/> <span class="focus"></span>`; //Script element to head var headJS = document.createElement('script'); headJS.innerHTML = `function listBoxChanged(message) { var messageBox = document.getElementsByClassName("Textarea ThreadViewerReplyForm__replyText")[0]; var responses = ${JSON.stringify(responses)}; var response = responses.find(x => x.content == message); response.replace ? messageBox.value = message : messageBox.value += message; console.log("[ModmailExtraInfo] New messageBox value: %c" + messageBox.value,"color: orange"); }`; function populate() { var select = $("#responseListbox"); for(var i = 0; i < responses.length; i++) { select.options[select.options.length] = new Option(responses[i].name, responses[i].content); } } $(".ThreadViewer__replyContainer").prepend(responseBox); var head = $("head"); head.appendChild(headJS); populate(); } //Detects the current theme (dark/light) and applies the correct color (for the added elements) function themeColors() { var darkTheme = $$(".theme-dark").length ? true : false; if(darkTheme) { console.log("[ModmailExtraInfo] Dark mode detected! Setting colors..."); textColor = darkModeTextColor; titleColor = darkModeTitleColor; listBoxColor = darkModeListColor; } else { console.log("[ModmailExtraInfo] Light mode detected! Setting colors..."); textColor = lightModeTextColor; titleColor = lightModeTitleColor; listBoxColor = lightModeListColor; } } themeColors(); //Took advice for the listbox CSS from moderncss.dev/custom-select-styles-with-pure-css, thanks! var css = `.profileIcon:hover { -ms-transform: scale(6); -webkit-transform: scale(6); transform: scale(6); } .profileIcon { position: relative; bottom: 4px; margin-bottom: 10px; float: left; border-radius: 50%; transition: transform .1s; } .InfoBar__recentsNone { color: #6e6e6e; } .InfoBar__metadata, .InfoBar__recents { margin: 6px 0; margin-left: 10px; } .value { color: ${dataColor}; } .InfoBar__banText { padding-bottom: 15px; } .InfoBar__username, .InfoBar__username:visited { padding-left: 10px; } .ThreadViewer__infobarContainer { display: table; } .dataText { color: ${textColor}; font-size: 13px; padding-left: 10px; } .dataTitle { color: ${titleColor}; font-size: 15px; margin-bottom: 3px; margin-top: 5px; } .responseListbox { width: 50%; cursor: pointer; } :root { --select-border: #0079d3; --select-focus: blue; --select-arrow: var(--select-border); } *, *::before, *::after { box-sizing: border-box; } select { appearance: none; background-color: ${listBoxColor}; color: ${textColor}; border: none; padding: 0 1em 0 0; margin: 0; width: 100%; cursor: pointer; font-family: inherit; font-size: inherit; line-height: inherit; outline: none; position: relative; } .select { width: 100%; min-width: 15ch; max-width: 30ch; border: 1px solid var(--select-border); border-radius: 0.25em; padding: 0.3em 0.4em; font-size: 0.9rem; line-height: 1.1; background-color: ${listBoxColor}; } select::-ms-expand { display: none; } option { white-space: normal; outline-color: var(--select-focus); } select:focus + .focus { position: absolute; top: -1px; left: -1px; right: -1px; bottom: -1px; border: 2px solid var(--select-focus); border-radius: inherit; } .Author__text { padding: 6px 0; } .chatProfileIcon { margin-right: 7px; transition: transform .1s; border-radius: 50%; }`; //Apply the custom css var styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = css; document.head.appendChild(styleSheet); addInfo(); if(enableCustomResponses && $("#responseListbox") == null) addResponseBox(); console.log("[ModmailExtraInfo] %cLoaded!", "color: lime"); } /* End of Main function */ /* Start Main function when visiting new modmail */ var pageURLCheckTimer = setInterval (function () { if (this.lastPathStr !== location.pathname) { this.lastPathStr = location.pathname; first = true; let startInterval = setInterval (function () { if($(".InfoBar__username")) { if(first) main(); first = false; clearInterval(startInterval); } }, 5); } }, 100);