From 9f82da6d12846b0d5ea6dab5569bc5b83e45cbc4 Mon Sep 17 00:00:00 2001 From: Phillip Markert Date: Wed, 20 Mar 2024 15:54:28 -0400 Subject: [PATCH 1/3] Adding v1.6 of measurement/init script --- measurement.js | 132 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 45 deletions(-) diff --git a/measurement.js b/measurement.js index 0eaefbc..f082ae4 100644 --- a/measurement.js +++ b/measurement.js @@ -1,73 +1,115 @@ // ==UserScript== -// @name LI Measurement Script +// @name LI Prebid Identity Initialization and Measurement Script // @namespace http://liveintent.com -// @version 2024-03-12-1 -// @description LiveIntent PCID Initialization and GAM Measurement Script -// @author You +// @version 1.6 +// @description Performs an a/b test for the LiveIntent prebid identity module and reports on the relative performance of each group. +// @author phillip@liveintent.com +// @homepage https://github.com/LiveIntent/lipid +// @run-at document-start // @grant none // ==/UserScript== -(function() { - // start liveintent module init and measurement v1.5 - const LI_PUBLISHER_ID = "INSERT PUBLISHER_ID HERE"; - const LI_DISTRIBUTOR_ID = undefined; - const LOGGED_IN_USERS_EMAIL_OR_EMAIL_HASH = ""; +(function () { + /* This script sets up an a/b test for the LiveIntentId module, reporting on the relative + performance of each group. The default behavior randomly selects 5% of visits to be in a + CONTROL group and only enables the module for the other 95% of visits (the TREATED group). + This a/b test selection logic can optionally be performed by an external process and connected + here by overriding the LI_MODULE_ENABLED value to match. If the external process also handles + configuring the LiveIntentId module (only when the TREATED group is selected!), then the + LI_CONFIGURE_MODULE value should be set to false. */ + + // start liveintent module init and measurement script v1.6 + const LI_MODULE_ENABLED = Math.random() < 0.95; // Override w/ true = TREATED, false = CONTROL + const LI_CONFIGURE_MODULE = true; // false if module is to be enabled by external process const LI_REPORTING_KEY = "li-module-enabled"; + const pbjs = (window.pbjs = window.pbjs || { que: [] }); const googletag = (window.googletag = window.googletag || { cmd: [] }); - const LI_MODULE_ENABLED = Math.random() < 0.95; - let bidsEnriched; - - function setTargeting(enriched, wonAll) { - googletag.cmd.push(function () { - let targeting = LI_MODULE_ENABLED ? "t1" : "t0"; - if(enriched!==undefined) targeting += enriched ? "-e1" : "-e0"; - if(wonAll!==undefined) targeting += wonAll ? "-wa" : "-ws"; - googletag.pubads().setTargeting(LI_REPORTING_KEY, targeting); - }); - } - - setTargeting(); - - pbjs.que.push(function () { - if (LI_MODULE_ENABLED) { + + function configureLiveIntentIdModule() { + if (LI_CONFIGURE_MODULE) { pbjs.mergeConfig({ userSync: { + /* 300 is recommended for the best combination of resolution/performance. + If auctionDelay=0, then storage should be configured below. */ auctionDelay: 300, + userIds: [ { name: "liveIntentId", params: { - publisherId: LI_PUBLISHER_ID, - distributorId: LI_DISTRIBUTOR_ID, - emailHash: LOGGED_IN_USERS_EMAIL_OR_EMAIL_HASH, + /* Specify either a publisherId or distributorId, but not both. */ + // publisherId: "INSERT PUBLISHER_ID HERE", + // distributorId: "INSERT DISTRIBUTOR_ID HERE", + + /* If available, setting this will further improve resolution. + Leave as undefined, empty-string, or commented-out if not available. */ + // emailHash: "LOGGED IN USERS EMAIL OR EMAIL HASH", + requestedAttributesOverrides: { - uid2: true, - bidswitch: true, - medianet: true, - magnite: true, - pubmatic: true, - index: true, - openx: true, + uid2: true, + bidswitch: true, + medianet: true, + magnite: true, + pubmatic: true, + index: true, + openx: true, }, }, + + /* Storage should ONLY be configured if auctionDelay=0 */ + // storage: { + // expires: 1, + // name: "pbjs_li_nonid", + // type: "cookie" + // } }, ], }, }); } - + } + + function setTargeting(enriched, wonAll) { + googletag.cmd.push(function () { + // t1 = module enabled, t0 = module disabled + let targeting = LI_MODULE_ENABLED ? "t1" : "t0"; + // e1 = enriched, e0 = not enriched + if (enriched !== undefined) targeting += enriched ? "-e1" : "-e0"; + // wa = Prebid won all ad-slots, ws = Prebid won some ad-slots + if (wonAll !== undefined) targeting += wonAll ? "-wa" : "-ws"; + googletag.pubads().setTargeting(LI_REPORTING_KEY, targeting); + }); + } + + setTargeting(); // Only reports t value at first. + const auctionsEnriched = {}; // Track enrichment per auctionId + pbjs.que.push(() => { + if (LI_MODULE_ENABLED) { + configureLiveIntentIdModule(); + } + pbjs.onEvent("auctionInit", function (args) { - bidsEnriched = args.adUnits && args.adUnits.some((au) => au.bids && - au.bids.some((b) => b.userIdAsEids && b.userIdAsEids.some((eid) => eid.source - === "liveintent.com" || (eid.uids && eid.uids.some((uid) => uid.ext && - uid.ext.provider === "liveintent.com"))))); - setTargeting(bidsEnriched); + auctionsEnriched[args.auctionId ?? 0] = args.adUnits?.some((adUnit) => + adUnit.bids?.some((bid) => + bid.userIdAsEids?.some( + (eid) => + eid.source === "liveintent.com" || + eid.uids?.some((uid) => uid.ext?.provider === "liveintent.com") + ) + ) + ); + // Reports t and e values after auctionInit event. + setTargeting(auctionsEnriched[args.auctionId]); }); - - pbjs.onEvent("bidWon", function (args) { - setTargeting(bidsEnriched, pbjs.getAllPrebidWinningBids().length === 0); + + pbjs.onEvent("adRenderSucceeded", function (args) { + // Reports t, e, and w values after adRenderSucceeded event. + setTargeting( + auctionsEnriched[args.bid.auctionId ?? 0], + pbjs.getAllPrebidWinningBids().length === 0 + ); }); }); - // end liveintent module init and measurement + // end liveintent module init and measurement script })(); From 4499d75cfad3c643b7bbb7f6b0296758ef3aef2c Mon Sep 17 00:00:00 2001 From: Phillip Markert Date: Wed, 20 Mar 2024 20:18:48 -0400 Subject: [PATCH 2/3] New API based initialization module --- measurement.js | 147 +++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 73 deletions(-) diff --git a/measurement.js b/measurement.js index f082ae4..8eccff6 100644 --- a/measurement.js +++ b/measurement.js @@ -10,103 +10,104 @@ // ==/UserScript== (function () { - /* This script sets up an a/b test for the LiveIntentId module, reporting on the relative - performance of each group. The default behavior randomly selects 5% of visits to be in a - CONTROL group and only enables the module for the other 95% of visits (the TREATED group). - This a/b test selection logic can optionally be performed by an external process and connected - here by overriding the LI_MODULE_ENABLED value to match. If the external process also handles - configuring the LiveIntentId module (only when the TREATED group is selected!), then the - LI_CONFIGURE_MODULE value should be set to false. */ - // start liveintent module init and measurement script v1.6 - const LI_MODULE_ENABLED = Math.random() < 0.95; // Override w/ true = TREATED, false = CONTROL - const LI_CONFIGURE_MODULE = true; // false if module is to be enabled by external process - const LI_REPORTING_KEY = "li-module-enabled"; - const pbjs = (window.pbjs = window.pbjs || { que: [] }); - const googletag = (window.googletag = window.googletag || { cmd: [] }); - function configureLiveIntentIdModule() { - if (LI_CONFIGURE_MODULE) { + pbjs.que.push(() => { + const googletag = (window.googletag = window.googletag || { cmd: [] }); + + function setTargeting(enriched, wonAll) { + googletag.cmd.push(function () { + // t1 = module enabled, t0 = module disabled + let targeting = liModule.enabled ? "t1" : "t0"; + // e1 = enriched, e0 = not enriched + if (enriched !== undefined) targeting += enriched ? "-e1" : "-e0"; + // wa = Prebid won all ad-slots, ws = Prebid won some ad-slots + if (wonAll !== undefined) targeting += wonAll ? "-wa" : "-ws"; + googletag.pubads().setTargeting(liModule.reportingKey, targeting); + }); + } + + const liModuleConfigured = pbjs.getConfig.userSync.userIds?.some( + (m) => m.name === "liveIntentId" + ); + + // update window.liModule w/deep merge of defaults and capture as a constant to prevent updates + const liModule = (window.liModule = Object.assign( + { + enabled: liModuleConfigured || Math.random() < 0.95, + reportingKey: "li-module-enabled", + auctionDelay: 300, + params: Object.assign( + { + requestedAttributesOverrides: { + uid2: true, + bidswitch: true, + medianet: true, + magnite: true, + pubmatic: true, + index: true, + openx: true, + }, + }, + window.liModule.params + ), + }, + window.liModule + )); + + // For legacy support + window.liModuleEnabled = liModule.enabled; + + const configureLiveIntentModule = () => { pbjs.mergeConfig({ userSync: { /* 300 is recommended for the best combination of resolution/performance. If auctionDelay=0, then storage should be configured below. */ - auctionDelay: 300, - + auctionDelay: liModule.auctionDelay, userIds: [ - { + Object.assign({ name: "liveIntentId", - params: { - /* Specify either a publisherId or distributorId, but not both. */ - // publisherId: "INSERT PUBLISHER_ID HERE", - // distributorId: "INSERT DISTRIBUTOR_ID HERE", - - /* If available, setting this will further improve resolution. - Leave as undefined, empty-string, or commented-out if not available. */ - // emailHash: "LOGGED IN USERS EMAIL OR EMAIL HASH", - - requestedAttributesOverrides: { - uid2: true, - bidswitch: true, - medianet: true, - magnite: true, - pubmatic: true, - index: true, - openx: true, - }, - }, - - /* Storage should ONLY be configured if auctionDelay=0 */ - // storage: { - // expires: 1, - // name: "pbjs_li_nonid", - // type: "cookie" - // } - }, + params: liModule.params, + storage: liModule.auctionDelay + ? undefined + : { + expires: 1, + name: "pbjs_li_nonid", + type: "cookie", + }, + }), ], }, }); + }; + if (liModule.enabled && !liModuleConfigured) { + liModule.deferInit + ? setTimeout(configureLiveIntentModule, 0) + : configureLiveIntentModule(); } - } - - function setTargeting(enriched, wonAll) { - googletag.cmd.push(function () { - // t1 = module enabled, t0 = module disabled - let targeting = LI_MODULE_ENABLED ? "t1" : "t0"; - // e1 = enriched, e0 = not enriched - if (enriched !== undefined) targeting += enriched ? "-e1" : "-e0"; - // wa = Prebid won all ad-slots, ws = Prebid won some ad-slots - if (wonAll !== undefined) targeting += wonAll ? "-wa" : "-ws"; - googletag.pubads().setTargeting(LI_REPORTING_KEY, targeting); - }); - } - setTargeting(); // Only reports t value at first. - const auctionsEnriched = {}; // Track enrichment per auctionId - pbjs.que.push(() => { - if (LI_MODULE_ENABLED) { - configureLiveIntentIdModule(); - } + setTargeting(); // Only reports the t value at first. pbjs.onEvent("auctionInit", function (args) { - auctionsEnriched[args.auctionId ?? 0] = args.adUnits?.some((adUnit) => - adUnit.bids?.some((bid) => - bid.userIdAsEids?.some( - (eid) => - eid.source === "liveintent.com" || - eid.uids?.some((uid) => uid.ext?.provider === "liveintent.com") + liModule.auctionsEnriched[args.auctionId ?? 0] = args.adUnits?.some( + (adUnit) => + adUnit.bids?.some((bid) => + bid.userIdAsEids?.some( + (eid) => + eid.source === "liveintent.com" || + eid.uids?.some((uid) => uid.ext?.provider === "liveintent.com") + ) ) - ) ); // Reports t and e values after auctionInit event. - setTargeting(auctionsEnriched[args.auctionId]); + setTargeting(liModule.auctionsEnriched[args.auctionId]); }); pbjs.onEvent("adRenderSucceeded", function (args) { // Reports t, e, and w values after adRenderSucceeded event. setTargeting( - auctionsEnriched[args.bid.auctionId ?? 0], + liModule.auctionsEnriched[args.bid.auctionId ?? 0], pbjs.getAllPrebidWinningBids().length === 0 ); }); From 7b4de45e29861addb0dc2ad87907f2717abcbfaa Mon Sep 17 00:00:00 2001 From: Phillip Markert Date: Wed, 20 Mar 2024 21:57:05 -0400 Subject: [PATCH 3/3] - Multiple options for a/b selection. - explicit - percentage selection - automatic - Use pbjs.cmd to get later in the pipeline - Auto-configures storage based upon auctionDelay - Controllable via lipid --- measurement.js | 83 +++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/measurement.js b/measurement.js index 8eccff6..cdbb091 100644 --- a/measurement.js +++ b/measurement.js @@ -11,33 +11,34 @@ (function () { // start liveintent module init and measurement script v1.6 - const pbjs = (window.pbjs = window.pbjs || { que: [] }); + const pbjs = (window.pbjs = window.pbjs || { cmd: [] }); - pbjs.que.push(() => { - const googletag = (window.googletag = window.googletag || { cmd: [] }); - - function setTargeting(enriched, wonAll) { - googletag.cmd.push(function () { - // t1 = module enabled, t0 = module disabled - let targeting = liModule.enabled ? "t1" : "t0"; - // e1 = enriched, e0 = not enriched - if (enriched !== undefined) targeting += enriched ? "-e1" : "-e0"; - // wa = Prebid won all ad-slots, ws = Prebid won some ad-slots - if (wonAll !== undefined) targeting += wonAll ? "-wa" : "-ws"; - googletag.pubads().setTargeting(liModule.reportingKey, targeting); - }); - } - - const liModuleConfigured = pbjs.getConfig.userSync.userIds?.some( + pbjs.cmd.push(() => { + const userSync = pbjs.getConfig().userSync; + // Is the module already configured + const liModuleConfigured = userSync.userIds?.some( (m) => m.name === "liveIntentId" ); + /* + Configuration Phase: Determines group (control vs. treated) and configures appropriately + There are three options for how to determine the group: + Option A: via window.liModule.enabled or window.liModuleEnabled (if set) + Option B: random selection based upon window.liModule.testPercentage (if set) + Option C: automatically if liveIntent module is configured in prebid userSync + */ + // update window.liModule w/deep merge of defaults and capture as a constant to prevent updates const liModule = (window.liModule = Object.assign( { - enabled: liModuleConfigured || Math.random() < 0.95, - reportingKey: "li-module-enabled", - auctionDelay: 300, + enabled: + window.liModuleEnabled ?? window.liModule.testPercentage + ? // normalize testPercentage to a number between 0 and 1 + Math.random() < + (window.liModule.testPercentage < 1 + ? window.liModule.testPercentage + : window.liModule.testPercentage / 100) + : liModuleConfigured, params: Object.assign( { requestedAttributesOverrides: { @@ -52,24 +53,28 @@ }, window.liModule.params ), + reportingKey: "li-module-enabled", + auctionsEnriched: {}, }, window.liModule )); - - // For legacy support + // Legacy support window.liModuleEnabled = liModule.enabled; - const configureLiveIntentModule = () => { + if (liModule.enabled && !liModuleConfigured) { + liModule.deferInit + ? setTimeout(configureLiveIntentModule, 0) + : configureLiveIntentModule(); + } + + function configureLiveIntentModule() { pbjs.mergeConfig({ userSync: { - /* 300 is recommended for the best combination of resolution/performance. - If auctionDelay=0, then storage should be configured below. */ - auctionDelay: liModule.auctionDelay, userIds: [ Object.assign({ name: "liveIntentId", params: liModule.params, - storage: liModule.auctionDelay + storage: userSync.auctionDelay ? undefined : { expires: 1, @@ -80,14 +85,24 @@ ], }, }); - }; - if (liModule.enabled && !liModuleConfigured) { - liModule.deferInit - ? setTimeout(configureLiveIntentModule, 0) - : configureLiveIntentModule(); } - setTargeting(); // Only reports the t value at first. + // Reporting Phase: Reports the group and performance of the module + const googletag = (window.googletag = window.googletag || { cmd: [] }); + + function setTargeting(enriched, wonAll) { + googletag.cmd.push(function () { + // t1 = module enabled, t0 = module disabled + let targeting = liModule.enabled ? "t1" : "t0"; + // e1 = enriched, e0 = not enriched + if (enriched !== undefined) targeting += enriched ? "-e1" : "-e0"; + // wa = Prebid won all ad-slots, ws = Prebid won some ad-slots + if (wonAll !== undefined) targeting += wonAll ? "-wa" : "-ws"; + googletag.pubads().setTargeting(liModule.reportingKey, targeting); + }); + } + + setTargeting(); pbjs.onEvent("auctionInit", function (args) { liModule.auctionsEnriched[args.auctionId ?? 0] = args.adUnits?.some( @@ -100,12 +115,10 @@ ) ) ); - // Reports t and e values after auctionInit event. setTargeting(liModule.auctionsEnriched[args.auctionId]); }); pbjs.onEvent("adRenderSucceeded", function (args) { - // Reports t, e, and w values after adRenderSucceeded event. setTargeting( liModule.auctionsEnriched[args.bid.auctionId ?? 0], pbjs.getAllPrebidWinningBids().length === 0