Last active
January 8, 2026 15:42
-
-
Save examosa/50eb28bc16a006b62b3f43893ab457e7 to your computer and use it in GitHub Desktop.
Userscripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Redirect to Farside | |
| // @version 1.5 | |
| // @description Redirects the configured websites to third-party privacy-respecting front ends. | |
| // @author examosa | |
| // @license AGPLv3-or-later | |
| // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/redirect-to-farside.user.js | |
| // @match https://*.fandom.com/* | |
| // @match https://*.medium.com/* | |
| // @match https://stackoverflow.com/* | |
| // @match https://www.reddit.com/* | |
| // @match https://www.youtube.com/* | |
| // @match https://www.quora.com/* | |
| // @match https://www.geeksforgeeks.org/* | |
| // @runAt document_start | |
| // ==/UserScript== | |
| const replacements = Object.entries({ | |
| 'www.geeksforgeeks.org': 'ducksforducks.private.coffee', | |
| }); | |
| const replacement = replacements.find(([hostname]) => hostname === location.hostname); | |
| const target = replacement | |
| ? location.href.replace(...replacement) | |
| : `https://farside.link/${location.href}`; | |
| location.replace(target); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Shortside | |
| // @version 1.0.0 | |
| // @description Redirects the configured websites to third-party privacy-respecting front ends. | |
| // @author examosa | |
| // @license AGPLv3-or-later | |
| // @updateUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js | |
| // @downloadUrl https://gist.github.com/examosa/50eb28bc16a006b62b3f43893ab457e7/raw/shortside.user.js | |
| // @match https://*.fandom.com/* | |
| // @match https://*.medium.com/* | |
| // @match https://stackoverflow.com/* | |
| // @match https://www.reddit.com/* | |
| // @match https://www.youtube.com/* | |
| // @match https://www.quora.com/* | |
| // @match https://www.geeksforgeeks.org/* | |
| // @runAt document_start | |
| // ==/UserScript== | |
| const cdn = (path) => new URL(path, 'https://nobsdelivr.private.coffee/'); | |
| const chooseRandom = (list) => list.at(Math.floor(Math.random() * list.length)); | |
| async function createCache() { | |
| const cacheKey = '1392945386074857472'; | |
| // Greasemonkey API | |
| if (typeof GM === 'object') { | |
| const gmCache = { | |
| async get() { | |
| try { | |
| const value = await GM.getValue(cacheKey); | |
| return JSON.parse(value); | |
| } catch { | |
| return null; | |
| } | |
| }, | |
| set: (value) => GM.setValue(cacheKey, JSON.stringify(value)), | |
| }; | |
| return gmCache; | |
| } | |
| // Vanilla Pudding | |
| const store = await import( | |
| cdn('npm/@vanilla-pudding/message@1.4.0/+esm') | |
| ).then( | |
| (module) => { | |
| const ext = module.useExt(); | |
| if (!ext.getVersion()) { | |
| return null; | |
| } | |
| return ext.bgt.extLocalStore; | |
| }, | |
| () => null, | |
| ); | |
| if (store) { | |
| const messageCache = { | |
| get: () => store.getByStrict(cacheKey), | |
| set: (value) => store.set(cacheKey, value, 10), | |
| }; | |
| return messageCache; | |
| } | |
| // JSON Blob | |
| const blobCache = { | |
| async get() { | |
| const response = await fetch( | |
| `https://jsonblob.com/api/jsonBlob/${cacheKey}`, | |
| ); | |
| if (response.ok) { | |
| return await response.json(); | |
| } | |
| return null; | |
| }, | |
| set() { | |
| console.warn('Unable to cache with JSON Blob'); | |
| }, | |
| }; | |
| return blobCache; | |
| } | |
| const providers = { | |
| libredirect: { | |
| source: 'libredirect/instances/data.json', | |
| normalize: (result) => | |
| Object.entries(result).map(([name, instances]) => ({ | |
| type: name.toLowerCase(), | |
| instances: instances.clearnet, | |
| })), | |
| }, | |
| farside: { | |
| source: 'benbusby/farside/services.json', | |
| normalize: (result) => | |
| result.map((service) => ({ | |
| type: service.type, | |
| instances: service.instances.concat(service.fallback), | |
| })), | |
| }, | |
| fastside: { | |
| source: 'cofob/fastside/services.json', | |
| normalize: (result) => | |
| result.services.map((service) => ({ | |
| type: service.type, | |
| instances: service.instances | |
| .filter((instance) => instance.tags.includes('clearnet')) | |
| .map((instance) => instance.url), | |
| })), | |
| }, | |
| }; | |
| async function fetchInstances() { | |
| const cache = await createCache(); | |
| const cached = await cache.get(); | |
| if (cached) { | |
| return cached; | |
| } | |
| const instances = Object.assign(Object.create(null), { | |
| ducksforducks: ['https://ducksforducks.private.coffee'], | |
| nerdsfornerds: ['https://nn.vern.cc'], | |
| }); | |
| try { | |
| const promises = Object.values(providers).map(async (provider) => { | |
| const response = await fetch(cdn(`gh/${provider.source}`)); | |
| if (!response.ok) { | |
| return []; | |
| } | |
| const result = await response.json(); | |
| return provider.normalize(result); | |
| }); | |
| const sources = await Promise.all(promises).flat(); | |
| for (const source of sources) { | |
| const list = (instances[source.type] ??= []); | |
| for (const url of source.instances) { | |
| if (!list.includes(url)) { | |
| list.push(url); | |
| } | |
| } | |
| } | |
| await cache.set(instances); | |
| } catch {} | |
| return instances; | |
| } | |
| const replacements = Object.entries({ | |
| fandom: ['breezewiki'], | |
| geeksforgeeks: ['ducksforducks', 'nerdsfornerds'], | |
| medium: ['libmedium', 'scribe'], | |
| quora: ['quetre'], | |
| reddit: ['eddrit', 'photon-reddit', 'redlib'], | |
| stackoverflow: ['anonymousoverflow'], | |
| }); | |
| async function run() { | |
| const instances = await fetchInstances(); | |
| const replacement = replacements.find(([host]) => | |
| location.hostname.includes(host), | |
| ); | |
| if (replacement) { | |
| const [, options] = replacement; | |
| const type = chooseRandom(options); | |
| const instancesForType = instances[type]; | |
| if (instancesForType?.length > 0) { | |
| const redirect = new URL( | |
| location.pathname, | |
| chooseRandom(instancesForType), | |
| ); | |
| return location.replace(redirect); | |
| } | |
| } | |
| location.replace(`https://farside.link/${location.href}`); | |
| } | |
| run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment