Add Kemono Party buttons on ppixiv user dropdown
目前為
// ==UserScript==
// @name kemono.party links for ppixiv
// @namespace https://www.pixiv.net/
// @version 1.2.0
// @description Add Kemono Party buttons on ppixiv user dropdown
// @author EnergoStalin
// @match https://*.pixiv.net/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=pixiv.net
// @license GPL3
// @grant GM_xmlhttpRequest
// @connect www.patreon.com
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const cachedPatreonUsers = {};
const patreonIdRegex = /"id":\s*"(\d+)",[\n\s]*"type":\s*"user"/sm;
const linkRegex = /(https?:\/\/(?:www)*\.?.+?\..+)/g;
function ripPatreonId(link) {
return new Promise((res,rej) => {
GM_xmlhttpRequest({
method: "GET",
url: link,
onload: function(response) {
res(response.responseText.match(patreonIdRegex)[1]);
},
onerror: rej,
onabort: rej,
});
});
}
function fanbox({extraLinks, userInfo}) {
extraLinks.push({
url: new URL(`https://kemono.party/fanbox/user/${userInfo.userId}`),
icon: "mat:money_off",
type: "kemono_fanbox",
label: "Kemono fanbox"
});
}
function normalizePatreonLink({ link }) {
if(typeof(link.url) === "string") link.url = new URL(link.url);
link.url.protocol = "https";
if(!link.url.host.startsWith("www.")) link.url.host = `www.${link.url.host}`;
}
function patreon({link, extraLinks, userInfo}) {
normalizePatreonLink({ link });
const url = link.url.toString();
const cachedId = cachedPatreonUsers[url];
if(!cachedId) {
ripPatreonId(url).then(id => {
cachedPatreonUsers[url] = id;
unsafeWindow.ppixiv.userCache.callUserModifiedCallbacks(userInfo.userId);
}).catch(console.log)
} else {
extraLinks.push({
url: new URL(`https://kemono.party/patreon/user/${cachedId}`),
icon: "mat:money_off",
type: "kemono_patreon",
label: "Kemono patreon"
});
}
}
function getLinksFromDescription(extraLinks) {
return removeDuplicates(
preprocessMatches(
Array.from(document.body.querySelector(".description").innerText.matchAll(linkRegex)).map(e => e.slice(1))
),
extraLinks
);
}
function removeDuplicates(links, extraLinks) {
const labels = extraLinks.map(e => e.label);
return links.filter(e => !labels.includes(e.label));
}
function preprocessMatches(matches) {
const map = {
"patreon": "patreon.com",
"fanbox": "Fanbox"
};
return matches.map(e => {
const url = new URL(e);
return {
label: map[Object.keys(map).filter(e => url.host.includes(e))[0]],
url
};
});
}
function addUserLinks({ extraLinks, userInfo }) {
for(const link of [...extraLinks, ...getLinksFromDescription(extraLinks)]) {
switch(link.label) {
case "Fanbox": fanbox({extraLinks, userInfo}); break;
case "patreon.com": patreon({link, extraLinks, userInfo}); break;
}
}
}
unsafeWindow.vviewHooks = {
addUserLinks: addUserLinks
};
})();