diff --git a/pronoun-assistant/pronoun-assistant.user.js b/pronoun-assistant/pronoun-assistant.user.js index a47ed0b..d12e103 100644 --- a/pronoun-assistant/pronoun-assistant.user.js +++ b/pronoun-assistant/pronoun-assistant.user.js @@ -8,10 +8,24 @@ // @updateURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js // @downloadURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js // @supportURL https://stackapps.com/questions/8440/pronoun-assistant -// @version 1.4 +// @version 2.1 // @match *://chat.stackexchange.com/rooms/* // @match *://chat.stackoverflow.com/rooms/* // @match *://chat.meta.stackexchange.com/rooms/* +// @match *://*.stackexchange.com/questions/* +// @match *://*.stackoverflow.com/questions/* +// @match *://*.superuser.com/questions/* +// @match *://*.serverfault.com/questions/* +// @match *://*.askubuntu.com/questions/* +// @match *://*.stackapps.com/questions/* +// @match *://*.mathoverflow.net/questions/* +// @exclude *://*.stackexchange.com/questions/ask +// @exclude *://*.stackoverflow.com/questions/ask +// @exclude *://*.superuser.com/questions/ask +// @exclude *://*.serverfault.com/questions/ask +// @exclude *://*.askubuntu.com/questions/ask +// @exclude *://*.stackapps.com/questions/ask +// @exclude *://*.mathoverflow.net/questions/ask // @grant GM_addStyle // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @require https://gist.github.com/raw/2625891/waitForKeyElements.js @@ -20,9 +34,6 @@ /* global $, waitForKeyElements */ -// NICETOHAVE: on main/meta sites as well; right now, there are very few users -// who have indicated their pronouns in their main/meta profile. - GM_addStyle(` .tiny-signature { display: inline-flex; @@ -37,6 +48,7 @@ GM_addStyle(` .pronouns { color: #777; + padding-left: 5px; } `) @@ -50,7 +62,7 @@ let allPronouns = [ ].join("|"); let pronounListRegex = new RegExp('\\W*((' + allPronouns + ')(\\s*/\\s*(' + allPronouns + '))+)\\W*', 'i'); let myPronounIsRegex = /(https?:\/\/)?(my\.)?pronoun\.is\/([\w/]+)/i; -let explicitPronounsRegex = /pronouns:\s*([^.\n]*)(\.|\n|$)/im; +let explicitPronounsRegex = /pronouns:\s*([^.\n)]*)(\.|\n|\)|$)/im; // Keys: user IDs // Values: either a list of DOM elements (specifically, the anchors to chat profiles) @@ -59,17 +71,25 @@ var users = {}; // TODO: cache in local storage // Adds pronoun information to a user's 'signature' in chat. -function showPronouns(element, pronouns) { +function showPronounsForChat(element, pronouns) { if (pronouns == "") { return; } // the element might contain both a tiny and a full signature element.find("div.username").each(function (index, usernameElement) { usernameElement.innerHTML = '' + usernameElement.innerHTML + '
' - + '' + pronouns + ''; + + '' + pronouns.replace(/(<([^>]+)>)/ig, "") + ''; }); } +// +function showPronouns(element, pronouns) { + if (pronouns == "") { + return; + } + element.after($('' + pronouns.replace(/(<([^>]+)>)/ig, "") + '')); +} + // Check the user's 'about' in their chat profile for pronoun indicators function getPronouns(aboutMe) { // Link to Pronoun Island, e.g. @@ -100,6 +120,19 @@ function getPronouns(aboutMe) { return ""; } +// Converts the hostname (e.g. "meta.stackexchange.com") into the site parameter for the API ("meta") +function getSite(hostname) { + if (hostname.contains(".stackexchange.com")) { + return hostname.split(".stackexchange.com")[0]; + } else if (hostname.contains(".com")) { + return hostname.split(".com")[0]; + } else { + // MathOverflow, MathOverflow Meta + return hostname; + } +} + +// Chat signatures waitForKeyElements("a.signature", function(jNode) { let userID = jNode.attr("href").split("/users/")[1]; if (!users.hasOwnProperty(userID)) { @@ -109,13 +142,78 @@ waitForKeyElements("a.signature", function(jNode) { $.get("https://" + location.host + "/users/thumbs/" + userID + "?showUsage=true", function(data) { let pronouns = data.user_message == null ? "" : getPronouns(data.user_message); users[userID].forEach(function (element) { - showPronouns(element, pronouns); + showPronounsForChat(element, pronouns); }); users[userID] = pronouns; }); } else if (typeof users[userID] == 'string') { - showPronouns(jNode, users[userID]); + // We already have the pronouns, we can just use them. + showPronounsForChat(jNode, users[userID]); } else { + // User appearing multiple times, their profile is already being fetched. users[userID].push(jNode); } }); + +// Q&A site user cards & comment usernames +(async () => { + const sleep = async (ms) => { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); + }; + + const userIds = []; + const profiles = {}; + + const $userElements = $("div.user-details > a, a.comment-user"); + + // Grab all the user IDs out of a page first. We'll go back over them later to add pronouns in. + $userElements.each(function() { + const link = $(this).attr("href"); + if (!link.startsWith("/users/")) { + // not a user card, but a community wiki post + return; + } + const userId = parseInt(link.split("/users/")[1]); + if (userIds.indexOf(userId) === -1) { + userIds.push(userId); + } + }); + + // Split the list into 100-item pages and grab profiles for each page. + // This works because splice modifies the userIds array in-place, removing elements from the front and + // returning them into the `page` variable. When we've used them all, the array will be empty. + while (userIds.length > 0) { + const page = userIds.splice(0, 100); + + const resp = await fetch("https://api.stackexchange.com/2.2/users/" + page.join(';') + "?site=" + getSite(location.hostname) + + "&key=L8n*CvRX3ZsedRQicxnjIA((&filter=!23IboywNfWUzv_nydJbn*&pagesize=100"); + const data = await resp.json(); + if (typeof data.items != 'undefined') { + data.items.forEach(i => { + profiles[i.user_id] = i.about_me; + }); + } + + if (data.backoff) { + // Respect backoffs, not just pronouns. + await sleep(data.backoff * 1000); + } + } + + $userElements.each(function() { + const link = $(this).attr("href"); + const userId = parseInt(link.split("/users/")[1]); + if (!users.hasOwnProperty(userId)) { + // No pronouns calculated yet, we need to calculate and store them. + users[userId] = getPronouns(profiles[userId]); + showPronouns($(this), users[userId]); + } + else { + // We already have the pronouns, we can just use them. + showPronouns($(this), users[userId]); + } + }); +})(); +