Created
January 28, 2023 06:12
-
-
Save ryanbaer/51d55aa0562b75d400d8e993ee3e8e8c to your computer and use it in GitHub Desktop.
ScriptKit
This file contains 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
/* | |
# 🧙♂️ Resolve Short Links | |
## Description | |
- ⌨️ Prompts the user for a URL | |
- 🌏 Makes a lightweight request to retrieve the actual URL | |
- 🧼 Sanitizes unnecessary tracking information | |
- 📋 Automatically copies the resolved URL to the clipboard. | |
## Explanation | |
This is useful for removing tracking / other information from URLs before sharing with others. | |
Many short links embed user / tracking information in the URL on their end. | |
Every time you share this shortened URL, your information is passed along. | |
- TikTok, for example, uses this to display your username on the page when others view the video you shared. | |
- Additionally, many short links will append query parameters that include tracking information as well, such as Facebook's `fbclid`. | |
This script resolves the short link by sending a `HEAD` request to the URL. | |
- Additionally, any unnecessary query parameters are stripped off | |
- The result is a valid URL with the minimum amount of information needed to access the resource | |
## Examples | |
|Site|Input|Resolved & Sanitized| | |
|-|-|-| | |
|Bitly|https://bit.ly/3IBRlRk?fbclid=csGcfbGQWeVOwKtolljwoSVhwcxZsnDungMTiiycUaTyBkgGlErwsLXEpaVOX|https://www.insider.com/private-caribbean-island-for-sale-photos-2022-8| | |
|TikTok|https://www.tiktok.com/t/kZnATxcP/|https://www.tiktok.com/@the_tim_davidson/video/7148742306085063978| | |
|Facebook Watch|https://fb.watch/ZLce_Hif0_/|https://www.facebook.com/watch/?v=752603792999748| | |
*/ | |
// Name: 🧙♂️ Resolve Short Links | |
// Description: Resolve short links & removes any tracking info | |
// Author: Ryan Baer | |
import "@johnlindquist/kit"; | |
const Resolver = { | |
requiredParams: { | |
// youtube.com/watch?v=[videoId] requires the 'v' param to locate the video | |
"youtube.com": ["v"], | |
// facebook.com/watch?v=[videoId] requires the 'v' param to locate the video | |
"facebook.com": ["v"], | |
}, | |
getURL(input: string) { | |
let processed = input; | |
if (!input.match(/^https?:\/\//)) { | |
processed = `https://${processed}`; | |
} | |
return new URL(processed); | |
}, | |
sanitizeUrl(url: URL) { | |
function getBaseDomain(hostname: string) { | |
const [tld, base] = hostname.split(".").reverse(); | |
return `${base}.${tld}`; | |
} | |
// Clone so we're not deleting from the list as we're iterating it. | |
const searchParams = new URLSearchParams(url.searchParams); | |
searchParams.forEach((_value, key) => { | |
const baseDomain = getBaseDomain(url.hostname); | |
const skip = this.requiredParams[baseDomain]; | |
if (skip?.includes(key)) { | |
return; | |
} | |
url.searchParams.delete(key); | |
}); | |
return url.href; | |
}, | |
async fetchHead(input: string) { | |
let url: URL; | |
try { | |
url = this.getURL(input); | |
} catch (error) { | |
return { resolvedUrl: undefined, error: "Invaid or unreachable URL" }; | |
} | |
const options = { method: "HEAD", credentials: "omit" } as const; | |
try { | |
const response = await fetch(url, options); | |
const responseUrl = new URL(response.url); | |
const sanitizedUrl = this.sanitizeUrl(responseUrl); | |
return { resolvedUrl: sanitizedUrl, error: undefined }; | |
} catch (error) { | |
return { resolvedUrl: undefined, error: this.getErrorMessage(error) }; | |
} | |
}, | |
getErrorMessage(error: unknown) { | |
return error instanceof Error | |
? error.message | |
: "Something unexpected happened."; | |
}, | |
async resolve(url: string) { | |
return this.fetchHead(url); | |
}, | |
}; | |
const url = await arg("Enter a shortened URL"); | |
const response = await Resolver.resolve(url); | |
const { resolvedUrl, error } = response; | |
const result = error ?? resolvedUrl ?? "(Something went wrong)"; | |
const shouldCopy = !!resolvedUrl; | |
if (shouldCopy) { | |
await copy(result); | |
} | |
await div( | |
md(`# 🔬 Result${shouldCopy ? " (automatically copied to clipboard)" : ""} | |
\`\`\` | |
${result} | |
\`\`\` | |
`) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment