Add specific support for pronouns.page

Closes #7
This commit is contained in:
nachtjasmin 2023-07-10 22:34:57 +02:00
parent f032dcca3f
commit bee683036d
No known key found for this signature in database
4 changed files with 78 additions and 9 deletions

View file

@ -60,7 +60,7 @@ export async function fetchPronouns(dataID, accountName, type) {
status = await fetchStatus(dataID);
}
let pronouns = extractFromStatus(status);
let pronouns = await extractFromStatus(status);
if (!pronouns) {
pronouns = "null";
//TODO: if no field check bio

View file

@ -2,7 +2,7 @@ import sanitizeHtml from "sanitize-html";
const fieldMatchers = [/pro.*nouns?/i, "pronomen"];
const knownPronounUrls = [
/pronouns\.page\/([\w/]+)/,
/pronouns\.page\/:?([\w/@]+)/,
/pronouns\.within\.lgbt\/([\w/]+)/,
/pronouns\.cc\/pronouns\/([\w/]+)/,
];
@ -14,9 +14,9 @@ const knownPronounUrls = [
* If found, it sanitizes and returns the value of said field.
*
* @param {any} status
* @returns {string|null} Author pronouns if found. Otherwise returns null.
* @returns {Promise<string|null>} Author pronouns if found. Otherwise returns null.
*/
export function extractFromStatus(status) {
export async function extractFromStatus(status) {
// get account from status and pull out fields
const account = status.account;
const fields = account.fields;
@ -42,6 +42,62 @@ export function extractFromStatus(status) {
text = pronounsRaw.match(knownUrlRe)[1];
}
// Right now, only the pronoun.page regex matches the @usernames.
if (text.charAt(0) === "@") {
text = await queryPronounsFromPronounsPage(text.substring(1));
}
if (!text) return null;
return text;
}
/**
* Queries the pronouns from the pronouns.page API.
* @param {string} username The username of the person.
* @returns {Promise<string|null>} The pronouns that have set the "yes" opinion.
*/
async function queryPronounsFromPronounsPage(username) {
// 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`);
if (resp.status >= 400) {
return null;
}
const { profiles } = await resp.json();
if (!profiles) return null;
// Unfortunately, pronouns.page does not return a 404 if a profile does not exist, but an empty profiles object. :clown_face:
if (!Object.keys(profiles).length) return null;
let pronouns;
// Query the pronouns in the following language order:
// 1. The mastodon interface language
// 2. The spoken languages according to the user
// 3. The english language.
const languages = [document.documentElement.lang, ...window.navigator.languages, "en"];
for (const lang of languages) {
if (lang in profiles) {
pronouns = profiles[lang].pronouns;
break;
}
}
// If we don't have a value yet, just take the first profile.
if (!pronouns) pronouns = profiles[0].pronouns;
let val = pronouns.find((x) => x.opinion === "yes").value;
val = sanitizePronounPageValue(val);
return val;
}
/**
* @param {string} val
*/
function sanitizePronounPageValue(val) {
if (!val.startsWith("https://")) return val;
val = val.replace(/https?:\/\/.+\.pronouns\.page\/:?/, "");
if (val === "no-pronouns") val = "no pronouns";
return val;
}

View file

@ -10,7 +10,7 @@
"description": "puts pronouns next to usernames on mastodon",
"homepage_url": "https://github.com/ItsVipra/ProToots",
"permissions": ["storage"],
"permissions": ["storage", "https://en.pronouns.page/api/profile/get/*"],
"browser_action": {
"default_icon": "icons/icon small_size/icon small_size.png",

View file

@ -13,8 +13,8 @@ const validFields = [
];
for (const field of validFields) {
extract(`${field} is extracted`, () => {
const result = pronouns.extractFromStatus({
extract(`${field} is extracted`, async () => {
const result = await pronouns.extractFromStatus({
account: {
fields: [{ name: field, value: "pro/nouns" }],
},
@ -30,12 +30,19 @@ valueExtractionSuite.before(() => {
global.window = {
// @ts-ignore
navigator: {
language: "en",
languages: ["en"],
},
};
global.document = {
// @ts-ignore
documentElement: {
lang: "de",
},
};
});
valueExtractionSuite.after(() => {
global.window = undefined;
global.document = undefined;
});
const valueExtractionTests = [
["she/her", "she/her"], // exact match
@ -44,7 +51,13 @@ const valueExtractionTests = [
["https://en.pronouns.page/they/them", "they/them"], // plain-text "URLs"
["pronouns.page/they/them", "they/them"], // plain-text "URLs" without scheme
[`<a href="https://en.pronouns.page/they/them"></a>`, "they/them"], // HTML-formatted URLs
[`<a href="https://en.pronouns.page/@Vipra"></a>`, null], // pronoun pages with usernames
[`<a href="https://en.pronouns.page/@Vipra"></a>`, "she/her"], // pronoun pages with usernames
[
`<a href="https://en.pronouns.page/@definitely_not_existing_username_on_pronouns_page"></a>`,
null,
], // 404 errors
[`<a href="https://de.pronouns.page/:Katze"></a>`, "Katze"], // custom pronouns
[`<a href="https://de.pronouns.page/@benaryorg"></a>`, "Katze"], // custom pronouns in profile
];
for (const [input, expects] of valueExtractionTests) {
valueExtractionSuite(input, async () => {