Skip to content

Commit

Permalink
Contxtful RTD Provider: Initial Release (#10550)
Browse files Browse the repository at this point in the history
* feat: added contxtfulRtdProvider

* fix: removed id in query param

* fix: googletag

* doc: typo

* fix: added contxtful in adloader

* doc: extra line

* fix: added connector config option
  • Loading branch information
sebastienrufiange authored Jan 11, 2024
1 parent 1837f79 commit 07f4809
Show file tree
Hide file tree
Showing 5 changed files with 508 additions and 1 deletion.
91 changes: 91 additions & 0 deletions integrationExamples/gpt/contxtfulRtdProvider_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<html>

<head>
<script src="http://localhost:9999/build/dev/prebid.js" async></script>
<script async src="https://www.googletagservices.com/tag/js/gpt.js"></script>
<script async>
const FAILSAFE_TIMEOUT = 8000;
const PREBID_TIMEOUT = 5000;

const bidders = [
{
bidder: 'appnexus',
params: {
placementId: 13144370
}
}
];

var adUnits = [
{
code: 'div-gpt-ad-1460505748561-0',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]],
}
},
bids: bidders,
}
];


var pbjs = pbjs || {};
pbjs.que = pbjs.que || [];
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
googletag.cmd.push(function () {
googletag.pubads().disableInitialLoad();
googletag.defineSlot('/19968336/header-bid-tag-0', [[300, 250], [300, 600]], 'div-gpt-ad-1460505748561-0').addService(googletag.pubads());
googletag.pubads().enableSingleRequest();
googletag.enableServices();
});

pbjs.que.push(function () {
pbjs.setConfig({
debug: true,
realTimeData: {
auctionDelay: 100,
dataProviders: [
{
name: "contxtful",
waitForIt: true,
params: {
version: "Contact contact@contxtful.com for the API version",
customer: "Contact contact@contxtful.com for the customer ID"
}
}
]
}
});
pbjs.addAdUnits(adUnits);
pbjs.requestBids({
bidsBackHandler: sendAdserverRequest,
timeout: PREBID_TIMEOUT
});
});

function sendAdserverRequest() {
if (pbjs.adserverRequestSent) return;
pbjs.adserverRequestSent = true;
googletag.cmd.push(function () {
pbjs.que.push(function () {
pbjs.setTargetingForGPTAsync();
googletag.pubads().refresh();
});
});
}

setTimeout(function () {
sendAdserverRequest();
}, FAILSAFE_TIMEOUT);

</script>
</head>

<body>
<h2>Contxtful RTD Provider</h2>
<div id='div-gpt-ad-1460505748561-0'></div>
</div>
</body>

</html>
150 changes: 150 additions & 0 deletions modules/contxtfulRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* Contxtful Technologies Inc
* This RTD module provides receptivity feature that can be accessed using the
* getReceptivity() function. The value returned by this function enriches the ad-units
* that are passed within the `getTargetingData` functions and GAM.
*/

import { submodule } from '../src/hook.js';
import {
logInfo,
logError,
isStr,
isEmptyStr,
buildUrl,
} from '../src/utils.js';
import { loadExternalScript } from '../src/adloader.js';

const MODULE_NAME = 'contxtful';
const MODULE = `${MODULE_NAME}RtdProvider`;

const CONTXTFUL_RECEPTIVITY_DOMAIN = 'api.receptivity.io';

let initialReceptivity = null;
let contxtfulModule = null;

/**
* Init function used to start sub module
* @param { { params: { version: String, customer: String, hostname: String } } } config
* @return { Boolean }
*/
function init(config) {
logInfo(MODULE, 'init', config);
initialReceptivity = null;
contxtfulModule = null;

try {
const {version, customer, hostname} = extractParameters(config);
initCustomer(version, customer, hostname);
return true;
} catch (error) {
logError(MODULE, error);
return false;
}
}

/**
* Extract required configuration for the sub module.
* validate that all required configuration are present and are valid.
* Throws an error if any config is missing of invalid.
* @param { { params: { version: String, customer: String, hostname: String } } } config
* @return { { version: String, customer: String, hostname: String } }
* @throws params.{name} should be a non-empty string
*/
function extractParameters(config) {
const version = config?.params?.version;
if (!isStr(version) || isEmptyStr(version)) {
throw Error(`${MODULE}: params.version should be a non-empty string`);
}

const customer = config?.params?.customer;
if (!isStr(customer) || isEmptyStr(customer)) {
throw Error(`${MODULE}: params.customer should be a non-empty string`);
}

const hostname = config?.params?.hostname || CONTXTFUL_RECEPTIVITY_DOMAIN;

return {version, customer, hostname};
}

/**
* Initialize sub module for a customer.
* This will load the external resources for the sub module.
* @param { String } version
* @param { String } customer
* @param { String } hostname
*/
function initCustomer(version, customer, hostname) {
const CONNECTOR_URL = buildUrl({
protocol: 'https',
host: hostname,
pathname: `/${version}/prebid/${customer}/connector/p.js`,
});

const externalScript = loadExternalScript(CONNECTOR_URL, MODULE_NAME);
addExternalScriptEventListener(externalScript);
}

/**
* Add event listener to the script tag for the expected events from the external script.
* @param { HTMLScriptElement } script
*/
function addExternalScriptEventListener(script) {
if (!script) {
return;
}

script.addEventListener('initialReceptivity', ({ detail }) => {
let receptivityState = detail?.ReceptivityState;
if (isStr(receptivityState) && !isEmptyStr(receptivityState)) {
initialReceptivity = receptivityState;
}
});

script.addEventListener('rxEngineIsReady', ({ detail: api }) => {
contxtfulModule = api;
});
}

/**
* Return current receptivity.
* @return { { ReceptivityState: String } }
*/
function getReceptivity() {
return {
ReceptivityState: contxtfulModule?.GetReceptivity()?.ReceptivityState || initialReceptivity
};
}

/**
* Set targeting data for ad server
* @param { [String] } adUnits
* @param {*} _config
* @param {*} _userConsent
* @return {{ code: { ReceptivityState: String } }}
*/
function getTargetingData(adUnits, _config, _userConsent) {
logInfo(MODULE, 'getTargetingData');
if (!adUnits) {
return {};
}

const receptivity = getReceptivity();
if (!receptivity?.ReceptivityState) {
return {};
}

return adUnits.reduce((targets, code) => {
targets[code] = receptivity;
return targets;
}, {});
}

export const contxtfulSubmodule = {
name: MODULE_NAME,
init,
extractParameters,
getTargetingData,
};

submodule('realTimeData', contxtfulSubmodule);
65 changes: 65 additions & 0 deletions modules/contxtfulRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Overview

**Module Name:** Contxtful RTD Provider
**Module Type:** RTD Provider
**Maintainer:** [prebid@contxtful.com](mailto:prebid@contxtful.com)

# Description

The Contxtful RTD module offers a unique feature—Receptivity. Receptivity is an efficiency metric, enabling the qualification of any instant in a session in real time based on attention. The core idea is straightforward: the likelihood of an ad’s success increases when it grabs attention and is presented in the right context at the right time.

To utilize this module, you need to register for an account with [Contxtful](https://contxtful.com). For inquiries, please contact [prebid@contxtful.com](mailto:prebid@contxtful.com).

# Configuration

## Build Instructions

To incorporate this module into your `prebid.js`, compile the module using the following command:

```sh
gulp build --modules=contxtfulRtdProvider,<other modules...>
```

## Module Configuration

Configure the `contxtfulRtdProvider` by passing the required settings through the `setConfig` function in `prebid.js`.

```js
import pbjs from 'prebid.js';

pbjs.setConfig({
"realTimeData": {
"auctionDelay": 1000,
"dataProviders": [
{
"name": "contxtful",
"waitForIt": true,
"params": {
"version": "<API Version>",
"customer": "<Contxtful Customer ID>"
}
}
]
}
});
```

### Configuration Parameters

| Name | Type | Scope | Description |
|------------|----------|----------|-------------------------------------------|
| `version` | `string` | Required | Specifies the API version of Contxtful. |
| `customer` | `string` | Required | Your unique customer identifier. |

# Usage

The `contxtfulRtdProvider` module loads an external JavaScript file and authenticates with Contxtful APIs. The `getTargetingData` function then adds a `ReceptivityState` to each ad slot, which can have one of two values: `Receptive` or `NonReceptive`.

```json
{
"adUnitCode1": { "ReceptivityState": "Receptive" },
"adUnitCode2": { "ReceptivityState": "NonReceptive" }
}
```

This module also integrates seamlessly with Google Ad Manager, ensuring that the `ReceptivityState` is available as early as possible in the ad serving process.
3 changes: 2 additions & 1 deletion src/adloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const _approvedLoadExternalJSList = [
'geoedge',
'mediafilter',
'qortex',
'dynamicAdBoost'
'dynamicAdBoost',
'contxtful'
]

/**
Expand Down
Loading

0 comments on commit 07f4809

Please sign in to comment.