Skip to content

Instantly share code, notes, and snippets.

@kverb
Created August 30, 2024 16:56
Show Gist options
  • Save kverb/1e0a24f3a16046c6e827b021ca4b2c1a to your computer and use it in GitHub Desktop.
Save kverb/1e0a24f3a16046c6e827b021ca4b2c1a to your computer and use it in GitHub Desktop.
WSJ Ladder on cloudflare workers
// JS Snippet for a cloudflare worker to get the "Shareable" URL for a wsj.com article by URL.
// Requires a valid, authenticated wsj.com cookie jar.
// Uses a cloudflare workers KV namespace bound to `TestKV` to store/fetch the cookies.
// getting these cookies is a pain. This was extracted using the chrome dev tools > copy request as Fetch and manually filtering out just the cookie header
const default_cookies = 'datadome=7S65yfEc866mp9GpkZUQXtvYKJHblCG5VlkmmCEzHErmZn4Cxj1DizrKaq6u_WW~SL3xkFUyv2sas2PJxIMAdJxPJ3skZr009v4kbL4sHsdYAnJHZhN0wtr8F8WRDz1l; djcs_route=7055a21f-09ec-48b1-b187-06518dfd9f6f; ca_rt=3IX-ARt2INJ47c8pbe87kw.SvzY1D2JoWw2n_c_Y3_wN78ns1LcSsITZEjFkMVz9K-dt9Nv7zaM-KWrg7GZRSWwHURy6sMrpRN6x68I6P9mtncYsvXMhVCnFdZnEnVxlj8; _pcid=%7B%22browserId%22%3A%22ltofwg00gm2jgtk9%22%7D; cX_P=ltofwg00gm2jgtk9; _pubcid=ddebe880-2003-4d12-8da8-2511b02cc014; _sp_su=false; TR=V2-a80a4882c66bd273e8409f9b32bf2dc04f6968616ebc9adf8c862073eeb1efea; ab_uuid=2eaa406a-31e0-49a8-bad2-bb149907d4d2; _ncg_domain_id_=00f9e1d1-9dc2-476b-afa6-49ec8df18f5e.0.1722602327.1754138327; AMCVS_CB68E4BA55144CAA0A4C98A5%40AdobeOrg=1; s_cc=true; ajs_anonymous_id=d8ea4e48-ad00-49e4-b43b-c85671413ff7; _fbp=fb.1.1722602327979.938299252; _meta_facebookTag_sync=1722602327980; _meta_cross_domain_id=bf5d6e97-ac4f-4e17-9192-478b235d9977; djvideovol=1; _pctx=%7Bu%7DN4IgrgzgpgThIC5QEMAcAGZAWVqBMAxgGxEBGAJngOwDMUqW6AnAGZOk16kt7kHpYWRJkVREAjESikCTZORaoCYvOlpRp4qCyjJgAdwgArAL6JQABxjaAlgA9EIQ0ZAAaEABcAnhaiOAwgAaICYm7pCwAMoeyB6QjsgAdgD2iW4gEDYeUACS5I5MeEU04mLiNKgArHg4wujoIUA; utag_main=v_id:019113198e500014ca00fca7ffca05065005b05d00a89$_sn:2$_se:5$_ss:0$_st:1722611323361$vapi_domain:wsj.com$ses_id:1722608257119%3Bexp-session$_pn:5%3Bexp-session$_prevpage:WSJ_Article_Style%20News_Hawaiian-Style%20Shirts%20Are%20a%20Summer%20Party%20Clich%C3%A9.%20Try%20These%20Cooler%20Takes%20Instead.%3Bexp-1722613123373; _dj_id.9183=.1722602330.2.1722609523.1722602330.ee10f889-b69b-4323-bd6f-71a3e7b40f8d.77f50d29-aa20-4028-b011-b1c1152c59a0.6391a979-9f41-413c-a254-0f077c532a86.1722608257330.5; s_tp=12506; s_ppv=WSJ_Article_Style%2520News_Hawaiian-Style%2520Shirts%2520Are%2520a%2520Summer%2520Party%2520Clich%25E9.%2520Try%2520These%2520Cooler%2520Takes%2520Instead.%2C98%2C90%2C12205; wsjregion=na%2Cus; gdprApplies=false; vcdpaApplies=true; ca_id=eJw10F9PwyAQAPDvwnO7MWiB7skuraY6NzPnfDCmAXo4XNstLds0xu8u--MLOX4HB3c_6MMeoC1b2QAaowdoUYCMbGz9_W8r6JRHaKSt_XYD7c3m4G2gt433_d5WnmMKBHMMYcziKoyUTEJlpAyZSXjsA-CM-tOuk3pzviAFlpEQRDOmKsIpiAgnJlGUKEMqjSPDEibYiIHSiayM0IL5FyiAGoEB6Yt12xp6NH5Dt4s8X-R3YTHLilWRvaRTn319vr-sYfGUZtcwnWWLeZGVy3QyzZdXfJxPimmO3gMk925dOnvqe8QxjwQnhAZIdyAdVKV03llEuKAiTgJkz8BJjAmmhPkhfe0uEMXkDLb3H0Rr53b9eDg8Ho-DY_95mtxQ1xZah37_AM5Ub3U.mLWaAlD_9_9zmPwRY9ALOZPH_nWO0qSPpFMiKjag23mNlFpazYwRxTCnu1iUXjHWilDrrLe5_MTU6CdCFi3N_W9xoEGa2qL0JfEjSYLDp58YyXlVOmFs-UbFGKsGdf8qTylyQ9kElLWxw87X3esaPoSnnqH9XIoriL2UlEsOy8U-WeCDhMEa9TVFaVvJHwo-spdlAxneN-k4gt04AkI_cEUjuzP2r9eAD9Dl6LEN0iAxUo9mtQx9mzq4qP-de2R2BOeyHyhqF1K66MovEjAFtDgSa_PEI-kg1COnQ58C3z5f2iWVkpI9MZWgw86T8NtHSzsFiY3mQsbHfCgB7a-QA38ci1cS8RUkzNnYuXZ3_PUXhSSTvPzi0YYnAsW7lT1VFk7XfCdoCcL5ylt5gpHRsWnA0ePQ6D6X5MIyuIq8-_xVQsiw3tSr1UVMDIvzHpJe3AoIL3w31tdYzlP7_BJ6ZLnXpaOBYTIvBFRE7DRIGfQA9pE5ysPU4FTTT8x4DWmuOHwK6E1Q70sqdnwxd-IECqwE8-mFe0z0xpEwKTCohgqyjwyFceMAjclwMdZLiICzgSglSvohe9_CNFECwAzxA41zsqAkLK4liRNRmyWfASMeu-F2YnbFdvQk23lQcPuGvYZdWEIaDmnlM9Oz2rHNXDBgywYew9h5N8MTT1I2Bbo; usr_prof_v2=eyJwIjp7InBzIjoxLCJxIjowLjk2fSwiYSI6eyJyIjoiMjAyNC0wMy0xNlQwMDowMDowMC4wMDBaIiwiZSI6IjIwMjUtMDMtMTVUMDA6MDA6MDAuMDAwWiIsInMiOiIyMDI0LTAzLTE2VDAwOjAwOjAwLjAwMFoiLCJweW10IjoiQ0MiLCJzYiI6IlJlZ3VsYXIiLCJmIjoiQW5udWFsIiwic3JjIjoiTU9TQUlDIiwibSI6IkIzVC1XU0ogRElHIFBLRyAkOTkveXIsICQyOTkuODgveXIsICQ0NjcuODgveXIiLCJvIjoiRElHSVRBTCBQQUNLQUdFIiwid3AiOiJSZWd1bGFyIiwic3MiOiJhY3RpdmUiLCJvcGUiOiI5OS4wMCIsImFyIjoiQVIiLCJzc2MiOiJBQ1RJVkUiLCJkcyI6Ik9OTElORSIsInN0IjoiMjAyNC0wMy0xNlQwMDowMDowMC4wMDBaIiwidCI6NX0sImNwIjp7ImVjIjoiTGFwc2luZyIsInBjIjowLjAxNjM1LCJwc3IiOjAuMTkyODcsInRkIjoxNjYsImFkIjoyLCJxYyI6MzQsInFvIjoyOCwic2NlbiI6eyJjaGUiOjAuMDE2ODgsImNobiI6MC4wMTQ5LCJjaGEiOjAuMDMwMTgsImNocCI6MC4wMjg5M319LCJpYyI6NX0%3D; _pubcid_cst=4Sy1LKosJA%3D%3D; DJSESSION=country%3Dus%7C%7Ccontinent%3Dna%7C%7Cregion%3D%7C%7Czip%3D22747; ccpaApplies=true; regulationApplies=gdpr%3Afalse%2Ccpra%3Atrue%2Cvcdpa%3Atrue; s_sq=djglobal%3D%2526pid%253DWSJLive_Video_WSJ%252520Poll%25253A%252520Harris%252520Takes%252520Narrow%252520Lead%252520Against%252520Trump%252520in%252520Neck-and-Neck%252520Race_229%2526pidt%253D1%2526oid%253Dhttps%25253A%25252F%25252Fwww.wsj.com%25252Fopinion%25253Fmod%25253Dnav_top_section%2526ot%253DA%26djwsj%3D%2526pid%253Dhttps%25253A%25252F%25252Fwww.wsj.com%25252Fworld%25252Fgeorgia-russian-pressure-democracy-bcd4c65e%25253Fmod%25253Dhp_lead_pos7%2526oid%253Dfunctionrg%252528%252529%25257B%25257D%2526oidt%253D2%2526ot%253DLI; AMCV_CB68E4BA55144CAA0A4C98A5%40AdobeOrg=1585540135%7CMCIDTS%7C19966%7CMCMID%7C92120438299723325723600409385265416491%7CMCAID%7CNONE%7CMCOPTOUT-1725027667s%7CNONE%7CvVersion%7C4.4.0';
const COOKIE_KEY = 'wsj_cookie'; // the key for cookie value in the KV namespace 'TestKV'
const htmlForm = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WSJ Ladder</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
form { display: flex; flex-direction: column; gap: 10px; }
input[type="url"] { padding: 5px; }
button { padding: 5px; cursor: pointer; }
</style>
</head>
<body>
<h1>WSJ Ladder</h1>
<form action="/" method="get">
<input type="url" name="url" placeholder="Paste WSJ URL here" required>
<button type="submit">Get Shareable URL</button>
</form>
</body>
<script>
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
}).catch(err => {
console.error('Failed to copy: ', err);
});
}
</script>
</html>
`;
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
const wsjUrl = url.searchParams.get('url')
if (!wsjUrl) {
return new Response(htmlForm, {
headers: { 'Content-Type': 'text/html' }
})
}
let shareUrl;
let errorMessage = '';
try {
shareUrl = await getShareUrl(wsjUrl);
} catch (error) {
errorMessage = error.message;
}
if (request.headers.get('Accept').includes('text/html')) {
let htmlResponse = htmlForm;
if (errorMessage) {
htmlResponse += `
<p style="color: red;">Error: ${errorMessage}</p>
<p>The current cookie may be invalid or expired. Please update it below:</p>
<form action="/" method="get">
<textarea name="new_cookie" rows="5" cols="50" placeholder="Paste new WSJ cookie here"></textarea>
<button type="submit">Update Cookie</button>
</form>
`;
} else if (shareUrl) {
htmlResponse += `
<p>Shortened URL: <br /><a href="${shareUrl}" target="_blank">${shareUrl}</a> <br />
<button onclick="copyToClipboard('${shareUrl}')">Copy</button></p>
`;
}
htmlResponse += `
`;
return new Response(htmlResponse, {
headers: { 'Content-Type': 'text/html' }
});
} else {
if (errorMessage) {
return new Response(errorMessage, {
status: 500,
headers: { 'Content-Type': 'text/plain' }
});
}
return new Response(shareUrl, {
headers: { 'Content-Type': 'text/plain' }
});
}
}
async function getShareUrl(url) {
const wsjUrl = 'https://www.wsj.com/emailthis/share-url';
const wsjBody = JSON.stringify({ method: 'copyurl', url: url });
let wsjCookie = await TestKV.get(COOKIE_KEY);
if (!wsjCookie) {
wsjCookie = default_cookies;
}
const wsjResponse = await fetch(wsjUrl, {
method: 'POST',
headers: {
'accept': 'application/json',
'accept-language': 'en-US,en;q=0.9',
'content-type': 'application/json',
'origin': 'https://www.wsj.com',
'referer': 'https://www.wsj.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'cookie': wsjCookie
},
body: wsjBody
})
console.log(wsjResponse.statusText);
if (wsjResponse.status !== 200) {
throw new Error(`WSJ API returned status ${wsjResponse.status}`);
}
const wsjData = await wsjResponse.json();
if (!wsjData.shareUrl) {
throw new Error('WSJ API response did not contain a shareUrl');
}
return wsjData.shareUrl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment