// // Playpass (c) Playco // https://github.com/playpassgames/playpass/blob/main/LICENSE.txt import { analytics } from "./analytics"; // See playpass-opengrapher/lambda/src/app.ts type OpengrapherMetadata = { // Open Graph tag names and values tags?: Record; // The Amplitude key we should post events to amplitude?: string; // The base URL we should redirect to, *without* the encoded payload url: string; payload: Payload; }; type Payload = { /** The entry channel to use for tracking. */ channel?: string, /** When this link was created. */ createdAt?: number; /** Custom data exposed to the game. */ data?: unknown, /** Feature flags that should be force set or unset for this player. */ featureFlags?: Record, /** The player ID that created this share link. */ referrer?: string, /** Tracking parameters to send along with the entry event. */ // TODO(2022-03-18): Rename to "entry" when we remove gcinstant gcinstant?: Record, /** See CreateLinkOptions.trackProps. */ trackProps?: Record, /** The original HTTP Referer header if we were redirected through the opengrapher. */ httpReferrer?: string, }; let cachedPayload: Payload; export function decode (): Payload { if (cachedPayload) { return cachedPayload; } try { cachedPayload = decodeRaw(location.href); } catch (error: unknown) { console.error("Failed to decode payload", error); cachedPayload = {}; analytics.track("PayloadDecodeError", {url: location.href}); } return cachedPayload; } export function decodeRaw (href: string): Payload { const url = new URL(href); const urlLink = url.searchParams.get("link"); if (urlLink) { return JSON.parse(urlLink) || {}; } const hash = new URL(url.hash.substring(1), url.origin); const hashLink = hash.searchParams.get("link"); if (hashLink) { return JSON.parse(hashLink) || {}; } // LEGACY (2022-03-xx). Handle legacy link format where we stuck the JSON directly in the hash try { return JSON.parse(decodeURIComponent(url.hash.substring(1))); } catch (error) { // Swallow errors in this case } return {}; } export function constructMetaPayload( explicitURL: string | undefined, payload: Payload, opts: { tags?: Map, amplitudeKey?: string } = {} ) { // The URL should already have been stripped, but strip it again here just to be safe const url = stripPayloadsFromUrl(explicitURL ? new URL(explicitURL, location.origin).href : location.href); const meta: OpengrapherMetadata = { // Include Open Graph tags if available tags: (opts.tags && opts.tags.size) ? Object.fromEntries(opts.tags) : undefined, amplitude: opts.amplitudeKey, url, payload, }; return meta; } export function encode (explicitURL: string | undefined, payload: Payload, opts: { tags?: Map, amplitudeKey?: string } = {} ): string { // Setting up the opengrapher share URL fallback is currently a manual CloudFront configuration // process. So for now we keep a whitelist for those CF distributions that have been configured, // and default to using playpass.link otherwise. Once we automate this for new domains we can // remove this. const enabledHosts: Record = { "beadle.gg": true, "tweedle.app": true, "qomedyquiz.com": true, }; const opengrapherOrigin = enabledHosts[location.hostname] ? location.origin : "https://playpass.link"; const meta = constructMetaPayload(explicitURL, payload, opts); const opengrapherUrl = new URL(opengrapherOrigin + "/share"); opengrapherUrl.searchParams.set("meta", JSON.stringify(meta)); return opengrapherUrl.href; } /** Gets the custom link data. */ export function getLinkData (): unknown { return decode().data; } /** Remove payload params from the given URL. */ export function stripPayloadsFromUrl (href: string): string { const url = new URL(href); // Strip the "link" param from the hash const hash = new URL(url.hash.substring(1), url.origin); if (hash.searchParams.has("link")) { hash.searchParams.delete("link"); url.hash = hash.pathname + hash.search; // If this would result in a bare #/ hash, clean out the entire thing if (url.hash == "#/") { url.hash = ""; } } url.searchParams.delete("link"); // Also remove our old gcinstant legacy payload param url.searchParams.delete("payload"); // Finally, remove any params added by ad networks // url.searchParams.delete('fbclid'); // url.searchParams.delete('twclid'); return url.toString(); }