Lemmy Universal Link Switcher

Ensures that all URLs to Lemmy instances always point to your main/home instance.

当前为 2023-06-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Lemmy Universal Link Switcher
// @namespace    http://azzurite.tv/
// @license      GPLv3
// @version      1.1.2
// @description  Ensures that all URLs to Lemmy instances always point to your main/home instance.
// @source       https://gitlab.com/azzurite/lemmy-rewrite-links
// @author       Azzurite
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=join-lemmy.org
// @grant        GM.setValue
// @grant        GM.getValue
// @require      https://unpkg.com/@popperjs/core@2
// @require      https://unpkg.com/tippy.js@6
// ==/UserScript==
"use strict";

const DEBUG = false;

const INSTANCES_LEMMY = lemmyInstances();
const INSTANCES_KBIN = kbinInstances();

const tippy = window.tippy;

const C = createConstants();

const OUR_CHANGES = { addedNodes: {} };

let HOME;
(async () => {
    HOME = await GM.getValue(`home`) || askForHomeIfOnInstance();
    updateHomePeriodically();
    startRewriting();
})();

//=========== FUNCTION DEFINITIONS ===========

function createConstants() {
    return {
        ICON_CLASS: withNS(`icon`),
        ICON_STYLES_ID: withNS(`icon-styles`),
        ICON_GRAPHIC_ID: withNS(`icon-graphic`),
        ORIGINAL_LINK_CLASS: withNS(`original-link`),
        SHOW_AT_HOME_BUTTON_ID: withNS(`show-at-home`),
        MAKE_HOME_BUTTON_ID: withNS(`make-home`),
        ICON_SVG_TEMPLATE_ID: withNS(`icon-template`)
    };
}

function triggerRewrite() {
    const triggerElem = document.createElement(`span`);
    triggerElem.style.display = `none`;
    document.body.append(triggerElem);
    setTimeout(() => {
        triggerElem.remove();
    }, 0);
}

function updateHomePeriodically() {
    debug(`Current HOME`, HOME);
    setInterval(async () => {
        const prev = HOME;

        HOME = await GM.getValue(`home`);

        if (prev !== HOME) {
            debug(`HOME changed from`, prev, `to`, HOME);
            triggerRewrite();
        }
    }, 1337)
}

function isOrHasOurAddedNode(node) {
    return Object.values(OUR_CHANGES.addedNodes).some(selector => node.matches?.(selector) || node.querySelector?.(selector));
}

function isIrrelevantChange(change) {
    if (change.type === `childList`) {
        if (Array.from(change.removedNodes).some(isOrHasOurAddedNode)) {
            trace(`Change`, change, `is relevant because a removed node is/has ours`);
            return false;
        }
        if (!Array.from(change.addedNodes).every(isOrHasOurAddedNode)) {
            trace(`Change`, change, `is relevant because not every added node is/has ours`);
            return false;
        }
    } else if (change.type === `attributes` && isRemoteUrl(new URL(change.target.href))) {
        trace(`Change`, change, `is relevant because href changed to a remote URL`);
        return false;
    }

    trace(`Change`, change, `is irrelevant`);
    return true;
}

async function startRewriting() {
    new MutationObserver((changes, observer) => {
        debug(`MutationObserver triggered`, changes);
        if (changes.every(isIrrelevantChange)) {
            debug(`All observed changes are irrelevant`);
            return;
        }
        doAllDomChanges(changes);
    }).observe(document.body, {
        subtree: true,
        childList: true,
        attributeFilter: [ `href` ]
    });

    await doAllDomChanges();
}

async function doAllDomChanges(changes) {
    debug(`doAllDomChanges start`);

    const addedMakeHomeButton = addMakeHomeButton();
    if (addedMakeHomeButton) debug(`Added Make Home Button`);

    const addedShowAtHomeButton = HOME ? addShowAtHomeButton() : false;
    if (addedShowAtHomeButton) debug(`Added Show At Home Button`);

    const rewrittenLinks = HOME ? await rewriteLinksToLocal(changes) : false;
    if (rewrittenLinks) debug(`Rewritten some links`);

    debug(`doAllDomChanges end`);
}

async function askForHomeIfOnInstance() {
    if (INSTANCES_LEMMY.has(location.origin) &&
        confirm(`Set this instance to be your home instance to which all URLs get rewritten to?`)) {

        GM.setValue(`home`, location.origin);
        return location.origin;
    } else {
        return null;
    }
}

function withNS(identifier) {
    return `lemmy-rewrite-urls-` + identifier;
}

function createSVG() { return document.createElementNS(`http://www.w3.org/2000/svg`, `svg`) }

function createIconTooltip(icon, link, originalHref) {
    const content = document.createElement(`span`);
    content.innerHTML = `Original link: <a class="${C.ORIGINAL_LINK_CLASS}" href="${originalHref}">${originalHref}</a>`;
    tippy(icon, {
        content,
        interactive: true,
        appendTo: () => link.parentNode,
        hideOnClick: false
    });
}

function ensureTemplateAvailable() {
    if (document.querySelector(`#` + C.ICON_SVG_TEMPLATE_ID)) return;

    const template = createSVG();
    template.id = C.ICON_SVG_TEMPLATE_ID
    template.innerHTML = `<defs><g id=${C.ICON_GRAPHIC_ID}><path fill=currentColor d="M52.8 34.6c.8.8 1.8 1.2 2.8 1.2s2-.4 2.8-1.2c1.5-1.5 1.5-4 0-5.6l-5.2-5.2h26v30.6c0 2.2 1.8 3.9 3.9 3.9 2.2 0 3.9-1.8 3.9-3.9V19.8c0-2.2-1.8-3.9-3.9-3.9h-30l5.2-5.2c1.5-1.5 1.5-4 0-5.6s-4-1.5-5.6 0l-11.8 12c-1.5 1.5-1.5 4 0 5.6l11.9 11.9zM31.1 28.7V11c0-3-2.5-5.5-5.5-5.5H8C5 5.5 2.5 8 2.5 11v17.7c0 3 2.5 5.5 5.5 5.5h17.7c3 0 5.4-2.5 5.4-5.5zM47.2 65.4c-1.5-1.5-4-1.5-5.6 0s-1.5 4 0 5.6l5.2 5.2h-26V45.6c0-2.2-1.8-3.9-3.9-3.9S13 43.5 13 45.6v34.5c0 2.2 1.8 3.9 3.9 3.9h30l-5.2 5.2c-1.5 1.5-1.5 4 0 5.6.8.8 1.8 1.2 2.8 1.2s2-.4 2.8-1.2l11.9-11.9c1.5-1.5 1.5-4 0-5.6l-12-11.9zM92 65.8H74.4c-3 0-5.5 2.5-5.5 5.5V89c0 3 2.5 5.5 5.5 5.5H92c3 0 5.5-2.5 5.5-5.5V71.3c0-3-2.5-5.5-5.5-5.5z"/></g>`;

    OUR_CHANGES.addedNodes[C.ICON_SVG_TEMPLATE_ID] = `#` + C.ICON_SVG_TEMPLATE_ID;
    document.head.append(template);
}

function ensureIconStylesAdded() {
    if (document.querySelector(`#` + C.ICON_STYLES_ID)) return;

    const style = document.createElement(`style`);
    style.id = C.ICON_STYLES_ID;
    style.innerText = `
    .${C.ICON_CLASS} {
        display: inline-block;
        vertical-align: sub;
        height: 1em; width: 1em;
        margin-left: 0.5em;
    }`;

    OUR_CHANGES.addedNodes[C.ICON_STYLES_ID] = `#` + C.ICON_STYLES_ID;
    document.head.append(style);
}

function createIcon(link, originalHref) {
    ensureTemplateAvailable();

    const icon = createSVG();
    icon.setAttribute(`class`, C.ICON_CLASS);
    ensureIconStylesAdded();
    icon.setAttribute(`viewBox`, `0 0 100 100`);
    icon.innerHTML = `<use href=#${C.ICON_GRAPHIC_ID} />`
    createIconTooltip(icon, link, originalHref);

    const wrapper = document.createElement(`span`);
    wrapper.addEventListener(`click`, (event) => {
        // for mobile so the tooltip can be opened
        event.preventDefault();
        event.stopPropagation();
    });

    OUR_CHANGES.addedNodes[C.ICON_CLASS] = `.` + C.ICON_CLASS;
    wrapper.append(icon);

    return wrapper;
}


function isRemoteLemmyUrl(url) {
    return url.origin !== HOME && INSTANCES_LEMMY.has(url.origin);
}

function isRemoteKbinUrl(url) {
    return url.origin !== HOME && INSTANCES_KBIN.has(url.origin);
}

function isRemoteUrl(url) {
    return isRemoteLemmyUrl(url) || isRemoteKbinUrl(url);
}

function isStandardAtLemmyFormat(url) {
    const paths = url.pathname.split(`/`);
    return paths[1] === `c` || paths[1] === `u`;
}

/**
 * @param url remote url to find local url for
 * @param rootPath optional, otherwise uses root path of {@link url}
 * @returns {string}
 */
function findLocalUrlForStandardAtFormat(url, rootPath) {
    const paths = url.pathname.split(`/`);

    const name = paths[2].includes(`@`) ? paths[2] : paths[2] + `@` + url.host;
    return `${HOME}/${rootPath || paths[1]}/${name}`;
}

function findLocalUrlForLemmyUrl(url) {
    if (isStandardAtLemmyFormat(url)) {
        return findLocalUrlForStandardAtFormat(url);
    } else {
        return null;
    }
}

function isStandardAtFormatKbin(url) {
    const paths = url.pathname.split(`/`);
    return paths.length === 3 && paths[1] === `m`;
}

function mappedKbinRootPath(url) {
    const paths = url.pathname.split(`/`);
    if (paths[1] === `m`) {
        return `c`
    } else {
        return null;
    }
}

function isKbinUserUrl(url) {
    const paths = url.pathname.split(`/`);
    return paths.length === 3 && paths[1] === `u`;
}

function findLocalUrlForKbinUserUrl(url) {
    const paths = url.pathname.split(`/`);

    const user = paths[2].startsWith(`@`) ? paths[2].substring(1) : paths[2];
    const name = user.includes(`@`) ? user : user + `@` + url.host;
    return `${HOME}/u/${name}`;
}

function findLocalUrlForKbinUrl(url) {
    if (isStandardAtFormatKbin(url)) {
        return findLocalUrlForStandardAtFormat(url, mappedKbinRootPath(url));
    } else if (isKbinUserUrl(url)) {
        return findLocalUrlForKbinUserUrl(url);
    }
}

function findLocalUrl(url) {
    if (isRemoteLemmyUrl(url)) return findLocalUrlForLemmyUrl(url);
    if (isRemoteKbinUrl(url)) return findLocalUrlForKbinUrl(url);
    return null;
}

function debug() {
    if (DEBUG) console.debug(`Rewriter | `, ...arguments);
}
function trace() {
    if (DEBUG === `trace`) console.debug(`Rewriter Trace | `, ...arguments);
}

function isHashLink(link) {
    return link.hash && link.origin + link.pathname + link.search === location.origin + location.pathname + location.search;
}

function rewriteToLocal(link) {
    if (!link.parentNode) return false; // DOM got changed under us
    if (link.classList.contains(C.ORIGINAL_LINK_CLASS)) return false;
    if (link.querySelector(`.` + C.ICON_CLASS)) return false;
    if (isHashLink(link)) return false;

    const localUrl = findLocalUrl(link);
    if (!localUrl) return false;

    if (localUrl === location.href) return false; // Probably a federation source link

    const oldHref = link.href;
    const treeWalker = document.createTreeWalker(link, NodeFilter.SHOW_TEXT, (node) => {
        if (node.textContent.toLowerCase().trim() === oldHref.toLowerCase().trim()) {
            return NodeFilter.FILTER_ACCEPT;
        } else {
            return NodeFilter.FILTER_SKIP;
        }
    });
    let textNode;
    while ((textNode = treeWalker.nextNode()) !== null) {
        textNode.textContent = localUrl;
    }

    link.href = localUrl;
    link.addEventListener(`click`, (event) => {
        // Fix for SPAs
        if (event.button === 0 && !event.ctrlKey && link.target !== `_blank`) {
            location.href = localUrl;
        }
    })
    link.append(createIcon(link, oldHref));

    trace(`Rewrite link`, link, ` from`, oldHref, `to`, localUrl);
    return true;
}

function findLinksInChange(change) {
    if (change.type === `childList`) {
        const links =  Array.from(change.addedNodes)
            .flatMap((addedNode) => {
                if (addedNode.tagName?.toLowerCase() === `a`) {
                    return addedNode
                } else if (addedNode.querySelectorAll) {
                    return Array.from(addedNode.querySelectorAll(`a`));
                } else {
                    return [];
                }
            });
        if (links.length > 0) trace(`Change`, change, `contained the links`, links);
        return links;
    } else if (change.type === `attributes`) {
        return change.target.matches?.(`a`) ? change.target : [];
    } else {
        return [];
    }
}

function findLinksToRewrite(changes) {
    if (!changes) {
        return document.querySelectorAll(`a`);
    }

    return changes.flatMap(findLinksInChange);
}

async function rewriteLinksToLocal(changes) {
    const links = findLinksToRewrite(changes);
    const chunkSize = 50;
    return await (async function processChunk(currentChunk) {
        const startIdx = currentChunk * chunkSize;
        const endChunkIdx = (currentChunk + 1) * chunkSize;
        const endIdx = Math.min(links.length, endChunkIdx);

        debug(`Processing ${links.length} links, current chunk `, currentChunk,
            `processing links ${startIdx} to ${endIdx}`)
        let anyRewritten = false;
        for (let i = startIdx; i < endIdx; ++i) {
            const rewritten = rewriteToLocal(links[i]);
            anyRewritten = anyRewritten || rewritten;
        }
        debug(`Processed links ${startIdx} to ${endIdx}`);

        if (endChunkIdx >= links.length) {
            return anyRewritten;
        }

        const chunkResult = await (new Promise(resolve => setTimeout(async () => {
            resolve(await processChunk(currentChunk + 1));
        }, 0)));
        return anyRewritten || chunkResult;
    })(0);
}

function showAtHomeButtonText() {
    const host = new URL(HOME).hostname;
    return `Show on ${host}`;
}

function createShowAtHomeAnchor(localUrl) {
    const showAtHome = document.createElement(`a`);
    showAtHome.dataset.creationHref = location.href;
    showAtHome.id = C.SHOW_AT_HOME_BUTTON_ID;
    showAtHome.innerText = showAtHomeButtonText();
    showAtHome.href = localUrl;

    OUR_CHANGES.addedNodes[C.SHOW_AT_HOME_BUTTON_ID] = `#` + C.SHOW_AT_HOME_BUTTON_ID;

    return showAtHome;
}

function isV17() {
    return isoData?.site_res?.version.startsWith(`0.17`);
}

function addLemmyShowAtHomeButton(localUrl) {
    const logo = document.querySelector(`a.navbar-brand`);
    const navbarIcons = isV17() ? document.querySelector(`[title="Search"]`)?.closest(`.navbar-nav`) : document.querySelector(`#navbarIcons`);
    if (!logo || !navbarIcons) {
        debug(`Could not find position to insert ShowAtHomeButton at`);
        return false;
    }
    const mobile = createShowAtHomeAnchor(localUrl);
    mobile.classList.add(`d-md-none`);
    mobile.style[`margin-right`] = `8px`;
    mobile.style[`margin-left`] = `auto`;
    if (isV17()) {
        document.querySelector(`.navbar-nav.ml-auto`)?.classList.remove(`ml-auto`);
    }

    logo.insertAdjacentElement('afterend', mobile);

    const desktop = createShowAtHomeAnchor(localUrl);
    desktop.classList.add(`d-md-inline`);
    desktop.style[`margin-right`] = `8px`;
    navbarIcons.insertAdjacentElement('beforebegin', desktop);
    return true;
}

function addKbinShowAtHomeButton(localUrl) {
    const prependTo = document.querySelector(`#header menu:not(.head-nav__menu)`)
    if (!prependTo) {
        debug(`Could not find position to insert ShowAtHomeButton at`);
        return false;
    }

    const item = document.createElement(`li`);
    item.append(createShowAtHomeAnchor(localUrl));
    prependTo.prepend(item);
    return true;
}

function addShowAtHomeButton() {
    const oldButton = document.querySelector(`#` + C.SHOW_AT_HOME_BUTTON_ID);
    if (oldButton && oldButton.dataset.creationHref !== location.href) {
        debug(`Removing old show at home button`);
        oldButton.remove(); // for SPA (like Lemmy) we need to watch when the location changes and update
    } else if (oldButton) {
        debug(`Old show at home button still exists`);
        return false;
    }

    const localUrl = findLocalUrl(location);
    if (!localUrl) {
        debug(`No local URL for show at home button found`, isRemoteKbinUrl(location));
        return false;
    }

    if (isRemoteLemmyUrl(location)) {
        return addLemmyShowAtHomeButton(localUrl);
    } else if (isRemoteKbinUrl(location)) {
        return addKbinShowAtHomeButton(localUrl);
    } else {
        return false;
    }
}

function addMakeHomeButton() {
    if (document.querySelector(`#` + C.MAKE_HOME_BUTTON_ID)) return false;
    if (!isRemoteLemmyUrl(location) || location.pathname !== `/settings`) return false;

    const insertAfter = document.querySelector(`#user-password`)?.closest(`.card`);
    if (!insertAfter) return;

    const button = document.createElement(`button`);
    button.id = C.MAKE_HOME_BUTTON_ID;
    button.setAttribute(`class`, `btn btn-block btn-primary mr-4 w-100`)
    button.innerText = `Make this my home instance for URL rewriting`;
    button.addEventListener(`click`, () => {
        GM.setValue(`home`, location.origin);
        HOME = location.origin;
        button.remove();
    });

    OUR_CHANGES.addedNodes[C.MAKE_HOME_BUTTON_ID] = `#` + C.MAKE_HOME_BUTTON_ID;
    insertAfter.insertAdjacentElement('afterend', button);

    return true;
}

function lemmyInstances() {
    /* retrieved with:
    (async function() {
        const req = await fetch("https://lemmymap.feddit.de/instances.json", {
            "headers": {"Cache-Control": "no-cache"}
        });
        const resp = await req.json();
        console.log(resp.map(site => new URL(site.site_view.site.actor_id).origin).sort());
    })(); 
    */
    return new Set(["http://changeme_gnv6cavn4bbpwvrpmjs5", "http://changeme_qiivckbjhtzuinufzvr2", "http://changeme_qiivckbjhtzuinufzvr2", "http://kek.henlo.fi", "https://0v0.social", "https://0xdd.org.ru", "https://1337lemmy.com", "https://158436977.xyz", "https://acqrs.co.uk", "https://actuallyruben.nl", "https://aggregation.cafe", "https://agora.nop.chat", "https://aiparadise.moe", "https://algebro.xyz", "https://anarch.is", "https://androiddev.network", "https://anime-pub.moe", "https://animoe.xyz", "https://apollo.town", "https://areality.social", "https://ascy.mooo.com", "https://aulem.org", "https://aussie.zone", "https://badblocks.rocks", "https://baomi.tv", "https://baraza.africa", "https://bbs.9tail.net", "https://bbs.darkwitch.net", "https://beehaw.org", "https://beer.andma.la", "https://beevibes.net", "https://bethe.kingofdog.de", "https://bigfoot.ninja", "https://biglemmowski.win", "https://bluuit.org", "https://board.minimally.online", "https://bolha.social", "https://bookwormstory.social", "https://booty.world", "https://botnet.club", "https://bubblesthebunny.com", "https://bulletintree.com", "https://butts.international", "https://c.calciumlabs.com", "https://caint.org", "https://cavy.rocks", "https://centennialstate.social", "https://chat.maiion.com", "https://cigar.cx", "https://civilloquy.com", "https://clatter.eu", "https://cnvrs.net", "https://code4lib.net", "https://coeus.sbs", "https://communick.news", "https://community.adiquaints.moe", "https://community.nicfab.it", "https://compuverse.uk", "https://crystals.rest", "https://cubing.social", "https://culture0.cc", "https://darmok.xyz", "https://databend.run", "https://dataterm.digital", "https://dendarii.alaeron.com", "https://derp.foo", "https://derpzilla.net", "https://dgngrnder.com", "https://diggit.xyz", "https://digipres.cafe", "https://digitalgoblin.uk", "https://discuss.icewind.me", "https://discuss.jacen.moe", "https://discuss.ntfy.sh", "https://discuss.online", "https://discuss.tchncs.de", "https://distress.digital", "https://dmv.social", "https://donky.social", "https://dormi.zone", "https://dot.surf", "https://drachennetz.com", "https://drak.gg", "https://drlemmy.net", "https://dsilo061298.ddns.net", "https://dubvee.org", "https://dubvee.org", "https://einweckglas.com", "https://endlesstalk.org", "https://endofti.me", "https://eslemmy.es", "https://eventfrontier.com", "https://eviltoast.org", "https://exploding-heads.com", "https://f.jbradaric.me", "https://fadoverso.pt", "https://fanaticus.social", "https://fanexus.com", "https://fed.rosssi.co.uk", "https://feddi.no", "https://feddit.at", "https://feddit.ch", "https://feddit.cl", "https://feddit.de", "https://feddit.dk", "https://feddit.eu", "https://feddit.fun", "https://feddit.it", "https://feddit.jp", "https://feddit.nl", "https://feddit.nu", "https://feddit.nz", "https://feddit.pl", "https://feddit.ro", "https://feddit.site", "https://feddit.strike23.de", "https://feddit.tech", "https://feddit.win", "https://feddiverse.org", "https://federated.community", "https://federated.ninja", "https://fedibb.ml", "https://fedit.io", "https://feditown.com", "https://fediverse.love", "https://fediverse.ro", "https://feedly.j-cloud.uk", "https://femboys.bar", "https://fenbushi.site", "https://fig.systems", "https://fjdk.uk", "https://fl0w.cc", "https://forkk.me", "https://foros.fediverso.gal", "https://forum.basedcount.com", "https://forum.stellarcastle.net", "https://freewilltiger.page", "https://frig.social", "https://geddit.social", "https://geddit.social", "https://glasstower.nl", "https://goddess.name", "https://gonelemmy.xyz", "https://granitestate.social", "https://greg.city", "https://group.lt", "https://grumpy.schuppentier.club", "https://hakbox.social", "https://halubilo.social", "https://hammerdown.0fucks.nl", "https://hc.frorayz.tech", "https://heckoverflow.net", "https://hoodlem.me", "https://hoodratshit.org", "https://hqueue.dev", "https://hyperfair.link", "https://iceorchid.net", "https://info.prou.be", "https://infosec.pub", "https://insane.dev", "https://invariant-marxism.red", "https://jalim.xyz", "https://jamie.moe", "https://jemmy.jeena.net", "https://ka.tet42.org", "https://kale.social", "https://keeb.lol", "https://keylog.zip", "https://kleptonix.com", "https://kyu.de", "https://l.1in1.net", "https://l.biendeo.com", "https://l.bxy.sh", "https://l.jugregator.org", "https://l.mathers.fr", "https://l.mchome.net", "https://l.nulltext.org", "https://l.plabs.social", "https://l.roofo.cc", "https://l.twos.dev", "https://labdegato.com", "https://laguna.chat", "https://latte.isnot.coffee", "https://le-me.xyz", "https://le.fduck.net", "https://le.mnau.xyz", "https://leaf.dance", "https://leby.dev", "https://leddit.minnal.icu", "https://leddit.social", "https://lef.li", "https://lem.agoomem.xyz", "https://lem.amd.im", "https://lem.cochrun.xyz", "https://lem.elbullazul.com", "https://lem.free.as", "https://lem.lyk.pw", "https://lem.monster", "https://lem.ph3j.com", "https://lem.serkozh.me", "https://lem.simple-gear.com", "https://lem.southcape.social", "https://lemdit.com", "https://lemido.freakspot.net", "https://lemitar.meta1203.com", "https://lemiverse.xyz", "https://lemm.ee", "https://lemmerz.org", "https://lemmings.basic-domain.com", "https://lemmings.online", "https://lemmit.online", "https://lemmit.online", "https://lemmit.xyz", "https://lemmony.click", "https://lemmus.org", "https://lemmy", "https://lemmy", "https://lemmy", "https://lemmy", "https://lemmy-xqddz-u4892.vm.elestio.app", "https://lemmy.0x3d.uk", "https://lemmy.1204.org", "https://lemmy.2kd.de", "https://lemmy.4d2.org", "https://lemmy.6px.eu", "https://lemmy.86thumbs.net", "https://lemmy.aguiarvieira.pt", "https://lemmy.albertoluna.es", "https://lemmy.alexland.ca", "https://lemmy.amxl.com", "https://lemmy.amyjnobody.com", "https://lemmy.ananace.dev", "https://lemmy.andiru.de", "https://lemmy.anji.nl", "https://lemmy.anonion.social", "https://lemmy.antemeridiem.xyz", "https://lemmy.antisocial.ly", "https://lemmy.appeine.com", "https://lemmy.arclight.pro", "https://lemmy.astheriver.art", "https://lemmy.aucubin.de", "https://lemmy.austinite.online", "https://lemmy.austinvaness.com", "https://lemmy.austinwadeheller.com", "https://lemmy.avata.social", "https://lemmy.azamserver.com", "https://lemmy.barnacles.one", "https://lemmy.baswag.de", "https://lemmy.batlord.org", "https://lemmy.beebl.es", "https://lemmy.beru.co", "https://lemmy.best", "https://lemmy.blad.is", "https://lemmy.blahaj.zone", "https://lemmy.bleh.au", "https://lemmy.bloodmoon-network.de", "https://lemmy.blue", "https://lemmy.blugatch.tube", "https://lemmy.borlax.com", "https://lemmy.brad.ee", "https://lemmy.bradis.me", "https://lemmy.brief.guru", "https://lemmy.bringdaruck.us", "https://lemmy.browntown.dev", "https://lemmy.brynstuff.co.uk", "https://lemmy.bulwarkob.com", "https://lemmy.bunbi.net", "https://lemmy.burger.rodeo", "https://lemmy.burger.rodeo", "https://lemmy.burningboard.net", "https://lemmy.ca", "https://lemmy.cablepick.net", "https://lemmy.cafe", "https://lemmy.caffeinated.social", "https://lemmy.calebmharper.com", "https://lemmy.calvss.com", "https://lemmy.cat", "https://lemmy.chiisana.net", "https://lemmy.chromozone.dev", "https://lemmy.ciechom.eu", "https://lemmy.click", "https://lemmy.cloudhub.social", "https://lemmy.clueware.org", "https://lemmy.cmstactical.net", "https://lemmy.cnschn.com", "https://lemmy.cock.social", "https://lemmy.coeus.icu", "https://lemmy.comfysnug.space", "https://lemmy.commodore.social", "https://lemmy.cook.gg", "https://lemmy.coolmathgam.es", "https://lemmy.cornspace.space", "https://lemmy.corrigan.xyz", "https://lemmy.coupou.fr", "https://lemmy.croc.pw", "https://lemmy.cultimean.group", "https://lemmy.davidbuckley.ca", "https://lemmy.davidfreina.at", "https://lemmy.dbzer0.com", "https://lemmy.dcrich.net", "https://lemmy.deadca.de", "https://lemmy.death916.xyz", "https://lemmy.decronym.xyz", "https://lemmy.deev.io", "https://lemmy.dekay.se", "https://lemmy.demonoftheday.eu", "https://lemmy.devils.house", "https://lemmy.direktoratet.se", "https://lemmy.discothe.quest", "https://lemmy.dlgreen.com", "https://lemmy.dnet.social", "https://lemmy.donmcgin.com", "https://lemmy.doomeer.com", "https://lemmy.dork.lol", "https://lemmy.dormedas.com", "https://lemmy.douwes.co.uk", "https://lemmy.dudeami.win", "https://lemmy.dupper.net", "https://lemmy.dustybeer.com", "https://lemmy.dyslexicjedi.com", "https://lemmy.easfrq.live", "https://lemmy.eatsleepcode.ca", "https://lemmy.eco.br", "https://lemmy.edgarchirivella.com", "https://lemmy.efradaphi.de", "https://lemmy.eic.lu", "https://lemmy.einval.net", "https://lemmy.elest.io", "https://lemmy.elest.io", "https://lemmy.elmusfire.xyz", "https://lemmy.emopolarbear.com", "https://lemmy.enchanted.social", "https://lemmy.escapebigtech.info", "https://lemmy.eus", "https://lemmy.fakecake.org", "https://lemmy.fanboys.xyz", "https://lemmy.fauxreigner.net", "https://lemmy.fbxl.net", "https://lemmy.fdvrs.xyz", "https://lemmy.fedi.bub.org", "https://lemmy.fedihub.social", "https://lemmy.fediverse.jp", "https://lemmy.fediversum.de", "https://lemmy.film", "https://lemmy.finance", "https://lemmy.flauschbereich.de", "https://lemmy.flugratte.dev", "https://lemmy.fmhy.ml", "https://lemmy.foxden.party", "https://lemmy.foxden.social", "https://lemmy.freddeliv.me", "https://lemmy.fredhs.net", "https://lemmy.fribyte.no", "https://lemmy.friheter.com", "https://lemmy.fromshado.ws", "https://lemmy.frozeninferno.xyz", "https://lemmy.fucs.io", "https://lemmy.fun", "https://lemmy.funkyfish.cool", "https://lemmy.funkylab.xyz", "https://lemmy.fwgx.uk", "https://lemmy.fyi", "https://lemmy.g97.top", "https://lemmy.game-files.net", "https://lemmy.gareth.computer", "https://lemmy.ghostplanet.org", "https://lemmy.gjz010.com", "https://lemmy.glasgow.social", "https://lemmy.gmprojects.pro", "https://lemmy.graphics", "https://lemmy.graz.social", "https://lemmy.gross.hosting", "https://lemmy.grouchysysadmin.com", "https://lemmy.grygon.com", "https://lemmy.gsp8181.co.uk", "https://lemmy.gtfo.social", "https://lemmy.haigner.me", "https://lemmy.hamrick.xyz", "https://lemmy.helheim.net", "https://lemmy.helios42.de", "https://lemmy.hellwhore.com", "https://lemmy.help", "https://lemmy.helvetet.eu", "https://lemmy.holmosapien.com", "https://lemmy.hopskipjump.cloud", "https://lemmy.hosted.frl", "https://lemmy.hpost.no", "https://lemmy.hutch.chat", "https://lemmy.intai.tech", "https://lemmy.irrealis.net", "https://lemmy.isamp.com", "https://lemmy.iswhereits.at", "https://lemmy.itermori.de", "https://lemmy.iys.io", "https://lemmy.jacaranda.club", "https://lemmy.jacaranda.club", "https://lemmy.jamesj999.co.uk", "https://lemmy.jamestrey.com", "https://lemmy.jasondove.me", "https://lemmy.jasonsanta.xyz", "https://lemmy.javant.xyz", "https://lemmy.jigoku.us.to", "https://lemmy.jlh.name", "https://lemmy.jmtr.org", "https://lemmy.johnpanos.com", "https://lemmy.jpaulus.io", "https://lemmy.jpiolho.com", "https://lemmy.jstsmthrgk.eu", "https://lemmy.jtmn.dev", "https://lemmy.juggler.jp", "https://lemmy.k6qw.com", "https://lemmy.kagura.eu", "https://lemmy.karaolidis.com", "https://lemmy.katia.sh", "https://lemmy.kemomimi.fans", "https://lemmy.keychat.org", "https://lemmy.kghorvath.com", "https://lemmy.kizaing.ca", "https://lemmy.knocknet.net", "https://lemmy.ko4abp.com", "https://lemmy.kodeklang.social", "https://lemmy.korgen.xyz", "https://lemmy.koski.co", "https://lemmy.krobier.com", "https://lemmy.kutara.io", "https://lemmy.kya.moe", "https://lemmy.l00p.org", "https://lemmy.l0nax.org", "https://lemmy.legally-berlin.de", "https://lemmy.lemist.de", "https://lemmy.lif.ovh", "https://lemmy.link", "https://lemmy.linuxuserspace.show", "https://lemmy.littlejth.com", "https://lemmy.livesound.world", "https://lemmy.loomy.li", "https://lemmy.loungerat.io", "https://lemmy.loutsenhizer.com", "https://lemmy.lpcha.im", "https://lemmy.lucaslower.com", "https://lemmy.lukeog.com", "https://lemmy.lylapol.com", "https://lemmy.m1k.cloud", "https://lemmy.maatwo.com", "https://lemmy.macaddict89.me", "https://lemmy.mambastretch.com", "https://lemmy.maples.dev", "https://lemmy.mariusdavid.fr", "https://lemmy.marud.fr", "https://lemmy.mats.ooo", "https://lemmy.matthe815.dev", "https://lemmy.mazurka.xyz", "https://lemmy.mb-server.com", "https://lemmy.mbirth.uk", "https://lemmy.media", "https://lemmy.meissners.me", "https://lemmy.meli.dev", "https://lemmy.menos.gotdns.org", "https://lemmy.mentalarts.info", "https://lemmy.meowchat.xyz", "https://lemmy.meridiangrp.co.uk", "https://lemmy.mildgrim.com", "https://lemmy.mira.pm", "https://lemmy.ml", "https://lemmy.mlaga97.space", "https://lemmy.mlsn.fr", "https://lemmy.modshiftx.com", "https://lemmy.monster", "https://lemmy.moonling.nl", "https://lemmy.morrisherd.com", "https://lemmy.mpcjanssen.nl", "https://lemmy.mrm.one", "https://lemmy.munsell.io", "https://lemmy.mxh.nu", "https://lemmy.my.id", "https://lemmy.mypinghertz.com", "https://lemmy.mywire.xyz", "https://lemmy.n1ks.net", "https://lemmy.naga.sh", "https://lemmy.name", "https://lemmy.nanoklinika.tk", "https://lemmy.nathaneaston.com", "https://lemmy.nauk.io", "https://lemmy.neeley.cloud", "https://lemmy.nerdcave.us", "https://lemmy.nerdcore.social", "https://lemmy.nexus", "https://lemmy.nikore.net", "https://lemmy.nine-hells.net", "https://lemmy.ninja", "https://lemmy.nope.ly", "https://lemmy.nopro.be", "https://lemmy.norbz.org", "https://lemmy.notdead.net", "https://lemmy.nrd.li", "https://lemmy.nz", "https://lemmy.obrell.se", "https://lemmy.one", "https://lemmy.onitato.com", "https://lemmy.onlylans.io", "https://lemmy.otakufarms.com", "https://lemmy.parasrah.com", "https://lemmy.pastwind.top", "https://lemmy.pathoris.de", "https://lemmy.pcft.eu", "https://lemmy.pe1uca.dev", "https://lemmy.pec0ra.ch", "https://lemmy.perigrine.ca", "https://lemmy.philipcristiano.com", "https://lemmy.picote.ch", "https://lemmy.pineapplemachine.com", "https://lemmy.pineapplenest.com", "https://lemmy.pipe01.net", "https://lemmy.piperservers.net", "https://lemmy.pipipopo.pl", "https://lemmy.pit.ninja", "https://lemmy.place", "https://lemmy.plasmatrap.com", "https://lemmy.podycust.co.uk", "https://lemmy.podycust.co.uk", "https://lemmy.ppl.town", "https://lemmy.primboard.de", "https://lemmy.pro", "https://lemmy.productionservers.net", "https://lemmy.proxmox-lab.com", "https://lemmy.pryst.de", "https://lemmy.pt", "https://lemmy.pub", "https://lemmy.pubsub.fun", "https://lemmy.pussthecat.org", "https://lemmy.pwzle.com", "https://lemmy.pxm.nl", "https://lemmy.qpixel.me", "https://lemmy.quad442.com", "https://lemmy.radio", "https://lemmy.rat.academy", "https://lemmy.ravc.tech", "https://lemmy.recursed.net", "https://lemmy.remotelab.uk", "https://lemmy.rhymelikedi.me", "https://lemmy.riffel.family", "https://lemmy.rimkus.it", "https://lemmy.rollenspiel.monster", "https://lemmy.room409.xyz", "https://lemmy.roombob.cat", "https://lemmy.root6.de", "https://lemmy.rubenernst.dev", "https://lemmy.run", "https://lemmy.run", "https://lemmy.s9m.xyz", "https://lemmy.saik0.com", "https://lemmy.samad.one", "https://lemmy.sascamooch.com", "https://lemmy.sbs", "https://lemmy.scam-mail.me", "https://lemmy.scav1.com", "https://lemmy.schlunker.com", "https://lemmy.schmeisserweb.at", "https://lemmy.schuerz.at", "https://lemmy.scottlabs.io", "https://lemmy.sdf.org", "https://lemmy.sebbem.se", "https://lemmy.secnd.me", "https://lemmy.sedimentarymountains.com", "https://lemmy.seifert.id", "https://lemmy.selfhost.quest", "https://lemmy.selfip.org", "https://lemmy.server.fifthdread.com", "https://lemmy.services.coupou.fr", "https://lemmy.sh", "https://lemmy.shattervideoisland.com", "https://lemmy.sidh.bzh", "https://lemmy.sietch.online", "https://lemmy.skillissue.dk", "https://lemmy.smeargle.fans", "https://lemmy.snoot.tube", "https://lemmy.socdojo.com", "https://lemmy.soontm.de", "https://lemmy.spacestation14.com", "https://lemmy.sprawl.club", "https://lemmy.srv.eco", "https://lemmy.srv0.lol", "https://lemmy.staphup.nl", "https://lemmy.stark-enterprise.net", "https://lemmy.starlightkel.xyz", "https://lemmy.starmade.de", "https://lemmy.steken.xyz", "https://lemmy.stormlight.space", "https://lemmy.strandundmeer.net", "https://lemmy.stuart.fun", "https://lemmy.studio", "https://lemmy.suchmeme.nl", "https://lemmy.sumuun.net", "https://lemmy.sumuun.net", "https://lemmy.svc.vesey.tech", "https://lemmy.sweevo.net", "https://lemmy.syrasu.com", "https://lemmy.sysctl.io", "https://lemmy.tancomps.net", "https://lemmy.tanktrace.de", "https://lemmy.tario.org", "https://lemmy.tarsis.org", "https://lemmy.taubin.cc", "https://lemmy.teaisatfour.com", "https://lemmy.technosorcery.net", "https://lemmy.techstache.com", "https://lemmy.tedomum.net", "https://lemmy.telaax.com", "https://lemmy.tf", "https://lemmy.tgxn.net", "https://lemmy.thanatos.at", "https://lemmy.the-burrow.com", "https://lemmy.the-goblin.net", "https://lemmy.theia.cafe", "https://lemmy.themainframe.org", "https://lemmy.theonecurly.page", "https://lemmy.thepixelproject.com", "https://lemmy.therhys.co.uk", "https://lemmy.thesmokinglounge.club", "https://lemmy.thias.xyz", "https://lemmy.tillicumnet.com", "https://lemmy.timdn.com", "https://lemmy.timon.sh", "https://lemmy.timwaterhouse.com", "https://lemmy.tobyvin.dev", "https://lemmy.today", "https://lemmy.toot.pt", "https://lemmy.towards.vision", "https://lemmy.tr00st.co.uk", "https://lemmy.trippy.pizza", "https://lemmy.ubergeek77.chat", "https://lemmy.umainfo.live", "https://lemmy.uncomfortable.business", "https://lemmy.unfiltered.social", "https://lemmy.uninsane.org", "https://lemmy.utopify.org", "https://lemmy.utveckla.re", "https://lemmy.va-11-hall-a.cafe", "https://lemmy.vanoverloop.xyz", "https://lemmy.vepta.org", "https://lemmy.villa-straylight.social", "https://lemmy.vinodjam.com", "https://lemmy.vip", "https://lemmy.virtim.dev", "https://lemmy.vodkatonic.org", "https://lemmy.vrchat-dev.tech", "https://lemmy.vskr.net", "https://lemmy.vyizis.tech", "https://lemmy.w9r.de", "https://lemmy.webgirand.eu", "https://lemmy.website", "https://lemmy.weckhorst.no", "https://lemmy.weiser.social", "https://lemmy.whalesinspace.de", "https://lemmy.whynotdrs.org", "https://lemmy.wiuf.net", "https://lemmy.wizjenkins.com", "https://lemmy.world", "https://lemmy.wraithsquadrongaming.com", "https://lemmy.wtf", "https://lemmy.wxbu.de", "https://lemmy.wyattsmith.org", "https://lemmy.x01.ninja", "https://lemmy.xce.pt", "https://lemmy.xcoolgroup.com", "https://lemmy.xoynq.com", "https://lemmy.zelinsky.dev", "https://lemmy.zell-mbc.com", "https://lemmy.zip", "https://lemmy.zone", "https://lemmy.zroot.org", "https://lemmy2.addictmud.org", "https://lemmybedan.com", "https://lemmydeals.com", "https://lemmyfi.com", "https://lemmyfly.org", "https://lemmygrad.ml", "https://lemmygrid.com", "https://lemmyis.fun", "https://lemmyngs.social", "https://lemmynsfw.com", "https://lemmyonline.com", "https://lemmypets.xyz", "https://lemmyrs.org", "https://lemmyunchained.net", "https://lemmywinks.com", "https://lemmywinks.xyz", "https://lemnt.telaax.com", "https://lemthony.com", "https://lib.lgbt", "https://libreauto.app", "https://liminal.southfox.me", "https://link.fossdle.org", "https://linkage.ds8.zone", "https://linkopath.com", "https://links.decafbad.com", "https://links.hackliberty.org", "https://links.lowsec.club", "https://links.rocks", "https://links.roobre.es", "https://links.wageoffsite.com", "https://livy.one", "https://lm.bittervets.org", "https://lm.byteme.social", "https://lm.curlefry.net", "https://lm.electrospek.com", "https://lm.gsk.moe", "https://lm.halfassmart.net", "https://lm.inu.is", "https://lm.kalinowski.dev", "https://lm.korako.me", "https://lm.m0e.space", "https://lm.madiator.cloud", "https://lm.melonbread.dev", "https://lm.paradisus.day", "https://lm.put.tf", "https://lm.qtt.no", "https://lm.runnerd.net", "https://lm.sethp.cc", "https://lm.suitwaffle.com", "https://lm.williampuckering.com", "https://lmmy.io", "https://lmmy.net", "https://lmmy.tvdl.dev", "https://lmmy.ylwsgn.cc", "https://lmy.dotcomitsa.website", "https://lmy.drundo.com.au", "https://local106.com", "https://localghost.org", "https://localhost", "https://localhost", "https://localhost", "https://localhost", "https://localhost", "https://logophilia.net", "https://lolimbeer.com", "https://lostcheese.com", "https://lsmu.schmurian.xyz", "https://lucitt.social", "https://mander.xyz", "https://matejc.com", "https://matts.digital", "https://mayheminc.win", "https://mcr.town", "https://meganice.online", "https://melly.0x-ia.moe", "https://merv.news", "https://mesita.link", "https://midwest.social", "https://milksteak.org", "https://mindshare.space", "https://mlem.a-smol-cat.fr", "https://mobilemmohub.com", "https://monero.house", "https://monero.town", "https://monyet.cc", "https://moose.best", "https://moot.place", "https://moto.teamswollen.org", "https://mujico.org", "https://mydomain.ml", "https://mydomain.ml", "https://mydomain.ml", "https://mylem.my", "https://mylemmy.win", "https://narod.city", "https://negativenull.com", "https://neodrain.net", "https://netmonkey.tech", "https://news.cosocial.ca", "https://news.deghg.org", "https://news.idlestate.org", "https://nlemmy.nl", "https://no.lastname.nz", "https://nonewfriends.club", "https://normalcity.life", "https://notdigg.com", "https://notlemmy.notawebsite.fr", "https://notlemmy.site", "https://novoidspace.com", "https://nrsk.no", "https://nunu.dev", "https://nwdr.club", "https://occult.social", "https://oceanbreeze.earth", "https://odin.lanofthedead.xyz", "https://omg.qa", "https://opendmz.social", "https://orava.dev", "https://orzen.games", "https://outpost.zeuslink.net", "https://partizle.com", "https://pasta.faith", "https://pathfinder.social", "https://pathofexile-discuss.com", "https://pawb.social", "https://philly.page", "https://pootusmaximus.xyz", "https://popplesburger.hilciferous.nl", "https://poptalk.scrubbles.tech", "https://possumpat.io", "https://posta.no", "https://preserve.games", "https://programming.dev", "https://proit.org", "https://psychedelia.ink", "https://purrito.kamartaj.xyz", "https://pwzle.com", "https://quex.cc", "https://r-sauna.fi", "https://r.rosettast0ned.com", "https://r.stoi.cc", "https://r196.club", "https://rabbitea.rs", "https://radiation.party", "https://rammy.site", "https://rational-racoon.de", "https://rblind.com", "https://re.tei.li", "https://read.widerweb.org", "https://readit.space", "https://red.cyberhase.de", "https://reddit.moonbeam.town", "https://reddthat.com", "https://retarded.dev", "https://ripo.st", "https://rlyeh.cc", "https://rustyshackleford.cfd", "https://s.jape.work", "https://sambaspy.com", "https://scif6.nsalanparty.com", "https://seattlelunarsociety.org", "https://sedd.it", "https://seemel.ink", "https://selfhosted.forum", "https://sffa.community", "https://sh.itjust.works", "https://sha1.nl", "https://shinobu.cloud", "https://shitposting.monster", "https://shork.online", "https://sigmet.io", "https://silicon-dragon.com", "https://slangenettet.pyjam.as", "https://slrpnk.net", "https://sneakernet.social", "https://snkkis.me", "https://snuv.win", "https://soccer.forum", "https://social.coalition.space", "https://social.cyb3r.dog", "https://social.dn42.us", "https://social.fossware.space", "https://social.fr4me.io", "https://social.ggbox.fr", "https://social.hamington.net", "https://social.jears.at", "https://social.nerdhouse.io", "https://social.nerdswire.de", "https://social.nerdswire.de", "https://social.poisson.me", "https://social.sour.is", "https://social.vmdk.ca", "https://social2.blahajspin.lol", "https://solstice.etbr.top", "https://sopuli.xyz", "https://sowhois.gay", "https://spgrn.com", "https://stammtisch.hallertau.social", "https://stanx.page", "https://stars.leemoon.network", "https://startrek.website", "https://sub.rdls.dev", "https://sub.wetshaving.social", "https://sublight.one", "https://suppo.fi", "https://support.futbol", "https://support.futbol", "https://surlesworld.com", "https://szmer.info", "https://tabletop.place", "https://tagpro.lol", "https://talka.live", "https://techy.news", "https://tezzo.f0rk.pl", "https://thaumatur.ge", "https://thegarden-u4873.vm.elestio.app", "https://thegarden.land", "https://thegarden.land", "https://thelemmy.club", "https://theotter.social", "https://thepride.hexodine.com", "https://thesidewalkends.io", "https://thesimplecorner.org", "https://thevapor.space", "https://toast.ooo", "https://toons.zone", "https://tortoisewrath.com", "https://tslemmy.duckdns.org", "https://ttrpg.network", "https://tucson.social", "https://typemi.me", "https://upvote.au", "https://versalife.duckdns.org", "https://vlemmy.net", "https://voxpop.social", "https://wallstreets.bet", "https://waveform.social", "https://wayfarershaven.eu", "https://weiner.zone", "https://werm.social", "https://whata.clusterfsck.com", "https://whatyoulike.club", "https://whiskers.bim.boats", "https://wilbo.tech", "https://wirebase.org", "https://wired.bluemarch.art", "https://wizanons.dev", "https://wolfballs.com", "https://wumbo.buzz", "https://www.jrz.city", "https://www.korzekwa.io", "https://xffxe4.lol", "https://yall.theatl.social", "https://yiffit.net", "https://ymmel.nl", "https://yogibytes.page", "https://zemmy.cc", "https://zerobytes.monster", "https://zoo.splitlinux.org"]);
}

function kbinInstances() {
    /* Retrieved with:
    await (async function () {
        async function callApi(url) {
            const resp = await fetch(url, );
            return await resp.json();
        }
    
        function getNextUrl(json) {
            return json.links.next;
        }
    
        function getInstances(json) {
            return json.data.map((instance) => `https://` + instance.domain);
        }
    
        const instances = [];
        function collectInstances(json) {
            const newInstances = getInstances(json);
            console.log(newInstances);
            instances.push(...newInstances);
        }
    
        let url = "https://fedidb.org/api/web/network/software/servers";
        while(url) {
            console.log(url);
            const json = await callApi(url);
            collectInstances(json);
            url = getNextUrl(json);
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
        return instances.sort();
    })();
    */
    return new Set(["https://champserver.net", "https://community.yshi.org", "https://feddit.online", "https://fedi196.gay", "https://fedia.io", "https://fediverse.boo", "https://forum.fail", "https://frmsn.space", "https://gehirneimer.de", "https://jlailu.social", "https://k.fe.derate.me", "https://karab.in", "https://kayb.ee", "https://kbin.buzz", "https://kbin.chat", "https://kbin.cocopoops.com", "https://kbin.dentora.social", "https://kbin.dk", "https://kbin.donar.dev", "https://kbin.fedi.cr", "https://kbin.korgen.xyz", "https://kbin.lgbt", "https://kbin.lol", "https://kbin.mastodont.cat", "https://kbin.melroy.org", "https://kbin.place", "https://kbin.possum.city", "https://kbin.projectsegfau.lt", "https://kbin.rocks", "https://kbin.run", "https://kbin.sh", "https://kbin.social", "https://kbin.tech", "https://kilioa.org", "https://kopnij.in", "https://longley.ws", "https://nadajnik.org", "https://nerdbin.social", "https://nolani.academy", "https://readit.buzz", "https://remy.city", "https://social.tath.link", "https://streetbikes.club", "https://teacup.social", "https://the.coolest.zone", "https://thebrainbin.org", "https://tuna.cat", "https://wiku.hu"])
}