mirror of
https://github.com/ItsVipra/ProToots
synced 2024-11-25 04:50:18 +00:00
Merge pull request #47 from ItsVipra/normalize-pronoun-page-requests
Normalize pronoun.page URLs
This commit is contained in:
commit
de65c96e8d
3 changed files with 51 additions and 21 deletions
|
@ -2,9 +2,9 @@ import sanitizeHtml from "sanitize-html";
|
||||||
|
|
||||||
const fieldMatchers = [/\bpro.*nouns?\b/i, "pronomen"];
|
const fieldMatchers = [/\bpro.*nouns?\b/i, "pronomen"];
|
||||||
const knownPronounUrls = [
|
const knownPronounUrls = [
|
||||||
/pronouns\.page\/:?([\w/@]+)/,
|
/pronouns\.page\/(@(?<username>\w+))?:?(?<pronouns>[\w/:]+)?/,
|
||||||
/pronouns\.within\.lgbt\/([\w/]+)/,
|
/pronouns\.within\.lgbt\/(?<pronouns>[\w/]+)/,
|
||||||
/pronouns\.cc\/pronouns\/([\w/]+)/,
|
/pronouns\.cc\/pronouns\/(?<pronouns>[\w/]+)/,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,12 +58,21 @@ async function extractFromField(field) {
|
||||||
// If one of pronoun URLs matches, overwrite the current known value.
|
// If one of pronoun URLs matches, overwrite the current known value.
|
||||||
for (const knownUrlRe of knownPronounUrls) {
|
for (const knownUrlRe of knownPronounUrls) {
|
||||||
if (!knownUrlRe.test(pronounsRaw)) continue;
|
if (!knownUrlRe.test(pronounsRaw)) continue;
|
||||||
text = pronounsRaw.match(knownUrlRe)[1];
|
const { pronouns, username } = pronounsRaw.match(knownUrlRe).groups;
|
||||||
}
|
|
||||||
|
|
||||||
// Right now, only the pronoun.page regex matches the @usernames.
|
// For now, only the pronouns.page regexp has a username value, so we can be sure
|
||||||
if (text.charAt(0) === "@") {
|
// that we don't query the wrong API.
|
||||||
text = await queryPronounsFromPronounsPage(text.substring(1));
|
if (username) {
|
||||||
|
return await queryUserFromPronounsPage(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case that we have single-word pronoun.page values, like "https://en.pronouns.page/it",
|
||||||
|
// we want to normalize that to include the possessive pronoun as well.
|
||||||
|
if (pronounsRaw.includes("pronouns.page") && !pronouns.includes("/")) {
|
||||||
|
return await normalizePronounPagePronouns(pronouns);
|
||||||
|
}
|
||||||
|
|
||||||
|
text = pronouns;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!text) return null;
|
if (!text) return null;
|
||||||
|
@ -71,11 +80,11 @@ async function extractFromField(field) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries the pronouns from the pronouns.page API.
|
* Queries the pronouns for a given user from the pronouns.page API.
|
||||||
* @param {string} username The username of the person.
|
* @param {string} username The username of the person, without the leading "@".
|
||||||
* @returns {Promise<string|null>} The pronouns that have set the "yes" opinion.
|
* @returns {Promise<string|null>} The pronouns that have set the "yes" or "meh" opinion.
|
||||||
*/
|
*/
|
||||||
async function queryPronounsFromPronounsPage(username) {
|
async function queryUserFromPronounsPage(username) {
|
||||||
// Example page: https://en.pronouns.page/api/profile/get/andrea?version=2
|
// Example page: https://en.pronouns.page/api/profile/get/andrea?version=2
|
||||||
const resp = await fetch(`https://en.pronouns.page/api/profile/get/${username}?version=2`);
|
const resp = await fetch(`https://en.pronouns.page/api/profile/get/${username}?version=2`);
|
||||||
if (resp.status >= 400) {
|
if (resp.status >= 400) {
|
||||||
|
@ -105,20 +114,40 @@ async function queryPronounsFromPronounsPage(username) {
|
||||||
if (!pronouns) pronouns = profiles[0].pronouns;
|
if (!pronouns) pronouns = profiles[0].pronouns;
|
||||||
|
|
||||||
let val = pronouns.find((x) => x.opinion === "yes" || x.opinion === "meh").value;
|
let val = pronouns.find((x) => x.opinion === "yes" || x.opinion === "meh").value;
|
||||||
val = sanitizePronounPageValue(val);
|
val = await normalizePronounPagePronouns(val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} val
|
* @param {string} val
|
||||||
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
function sanitizePronounPageValue(val) {
|
async function normalizePronounPagePronouns(val) {
|
||||||
if (!val.startsWith("https://")) return val;
|
const match = val.match(/pronouns\.page\/(.+)/);
|
||||||
|
if (match) val = match[1];
|
||||||
|
|
||||||
val = val.replace(/https?:\/\/.+\.pronouns\.page\/:?/, "");
|
if (val.includes("/")) return val;
|
||||||
|
|
||||||
if (val === "no-pronouns") val = "no pronouns";
|
if (val === "no-pronouns") return "no pronouns";
|
||||||
return val;
|
|
||||||
|
const pronounNameResp = await fetch("https://en.pronouns.page/api/pronouns/" + val);
|
||||||
|
if (!pronounNameResp.ok) {
|
||||||
|
// In case the request fails, better show the likely pronouns than nothing at all.
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we query the pronouns.page API with invalid values, an empty body is returned, still with status code 200.
|
||||||
|
// Therefore, we just try to parse the JSON and if it does not work, we return the "val" from earlier and don't
|
||||||
|
// do further processing.
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
morphemes: { pronoun_subject, possessive_pronoun },
|
||||||
|
} = await pronounNameResp.json();
|
||||||
|
|
||||||
|
return [pronoun_subject, possessive_pronoun].join("/");
|
||||||
|
} catch {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -151,7 +180,7 @@ function sanitizePronouns(str) {
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
// Remove trailing characters that are used as separators.
|
// Remove trailing characters that are used as separators.
|
||||||
str = str.replace(/[-| /]+$/, "");
|
str = str.replace(/[-| :/]+$/, "");
|
||||||
|
|
||||||
// Finally, remove leading and trailing whitespace.
|
// Finally, remove leading and trailing whitespace.
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
"description": "puts pronouns next to usernames on mastodon",
|
"description": "puts pronouns next to usernames on mastodon",
|
||||||
"homepage_url": "https://github.com/ItsVipra/ProToots",
|
"homepage_url": "https://github.com/ItsVipra/ProToots",
|
||||||
"permissions": ["storage", "https://en.pronouns.page/api/profile/get/*"],
|
"permissions": ["storage", "https://en.pronouns.page/api/*"],
|
||||||
|
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"default_icon": "icons/icon small_size/icon small_size.png",
|
"default_icon": "icons/icon small_size/icon small_size.png",
|
||||||
|
|
|
@ -69,13 +69,14 @@ const valueExtractionTests = [
|
||||||
null,
|
null,
|
||||||
], // 404 errors
|
], // 404 errors
|
||||||
[`<a href="https://de.pronouns.page/:Katze"></a>`, "Katze"], // custom pronouns
|
[`<a href="https://de.pronouns.page/:Katze"></a>`, "Katze"], // custom pronouns
|
||||||
[`<a href="https://de.pronouns.page/@benaryorg"></a>`, "Katze"], // custom pronouns in profile
|
[`<a href="https://de.pronouns.page/@benaryorg"></a>`, "Katze/Katze's"], // custom pronouns in profile
|
||||||
[`:theythem:`, null], // emojis shortcodes used for pronouns
|
[`:theythem:`, null], // emojis shortcodes used for pronouns
|
||||||
[
|
[
|
||||||
// This is an actual example from a Mastodon field, with example.com redirecting to pronouns.page.
|
// This is an actual example from a Mastodon field, with example.com redirecting to pronouns.page.
|
||||||
`dey/denen, es/ihm - <a href="https://example.com" rel="nofollow noopener noreferrer" target="_blank"><span class="invisible">https://</span><span class="">example.com</span><span class="invisible"></span></a>`,
|
`dey/denen, es/ihm - <a href="https://example.com" rel="nofollow noopener noreferrer" target="_blank"><span class="invisible">https://</span><span class="">example.com</span><span class="invisible"></span></a>`,
|
||||||
"dey/denen, es/ihm",
|
"dey/denen, es/ihm",
|
||||||
],
|
],
|
||||||
|
["https://en.pronouns.page/it", "it/its"], // single-word pronoun pages
|
||||||
];
|
];
|
||||||
for (const [input, expects] of valueExtractionTests) {
|
for (const [input, expects] of valueExtractionTests) {
|
||||||
valueExtractionSuite(input, async () => {
|
valueExtractionSuite(input, async () => {
|
||||||
|
|
Loading…
Reference in a new issue