Created
August 30, 2024 16:56
-
-
Save kverb/1e0a24f3a16046c6e827b021ca4b2c1a to your computer and use it in GitHub Desktop.
WSJ Ladder on cloudflare workers
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
// 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