Skip to content

Commit

Permalink
Adnuntius Bid Adaptor: Handling deals better
Browse files Browse the repository at this point in the history
  • Loading branch information
antosarho committed May 22, 2023
1 parent 73a1a72 commit 3b7c8d6
Show file tree
Hide file tree
Showing 3 changed files with 635 additions and 344 deletions.
177 changes: 98 additions & 79 deletions modules/adnuntiusBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import { config } from '../src/config.js';
import { getStorageManager } from '../src/storageManager.js';

const BIDDER_CODE = 'adnuntius';
const BIDDER_CODE_DEAL_ALIAS_BASE = 'adndeal';
const BIDDER_CODE_DEAL_ALIAS_1 = BIDDER_CODE_DEAL_ALIAS_BASE + '1';
const BIDDER_CODE_DEAL_ALIAS_2 = BIDDER_CODE_DEAL_ALIAS_BASE + '2';
const BIDDER_CODE_DEAL_ALIAS_3 = BIDDER_CODE_DEAL_ALIAS_BASE + '3';
const BIDDER_CODE_DEAL_ALIAS_4 = BIDDER_CODE_DEAL_ALIAS_BASE + '4';
const BIDDER_CODE_DEAL_ALIAS_5 = BIDDER_CODE_DEAL_ALIAS_BASE + '5';
const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i';
const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i';
const GVLID = 855;
const DEFAULT_VAST_VERSION = 'vast4'
// const DEFAULT_NATIVE = 'native'
const MAXIMUM_DEALS_LIMIT = 5;

const checkSegment = function (segment) {
if (isStr(segment)) return segment;
Expand All @@ -29,40 +35,13 @@ const getSegmentsFromOrtb = function (ortb2) {
return segments
}

// function createNative(ad) {
// const native = {};
// const assets = ad.assets
// native.title = ad.text.title.content;
// native.image = {
// url: assets.image.cdnId,
// height: assets.image.height,
// width: assets.image.width,
// };
// if (assets.icon) {
// native.icon = {
// url: assets.icon.cdnId,
// height: assets.icon.height,
// width: assets.icon.width,
// };
// }

// native.sponsoredBy = ad.text.sponsoredBy?.content || '';
// native.body = ad.text.body?.content || '';
// native.cta = ad.text.cta?.content || '';
// native.clickUrl = ad.destinationUrls.destination || '';
// native.impressionTrackers = ad.impressionTrackingUrls || [ad.renderedPixel];

// return native;
// }

const handleMeta = function () {
const storage = getStorageManager({ bidderCode: BIDDER_CODE })
let adnMeta = null
if (storage.localStorageIsEnabled()) {
adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData'))
}
const meta = (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {}
return meta
return (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {}
}

const getUsi = function (meta, ortb2, bidderRequest) {
Expand All @@ -71,12 +50,23 @@ const getUsi = function (meta, ortb2, bidderRequest) {
return usi
}

const AU_ID_REGEX = new RegExp('^[0-9A-Fa-f]{1,20}$');

export const spec = {
code: BIDDER_CODE,
aliases: [
BIDDER_CODE_DEAL_ALIAS_1,
BIDDER_CODE_DEAL_ALIAS_2,
BIDDER_CODE_DEAL_ALIAS_3,
BIDDER_CODE_DEAL_ALIAS_4,
BIDDER_CODE_DEAL_ALIAS_5
],
gvlid: GVLID,
supportedMediaTypes: [BANNER, VIDEO],
isBidRequestValid: function (bid) {
return !!(bid.bidId || (bid.params.member && bid.params.invCode));
// The auId MUST be a hexadecimal string
const validAuId = AU_ID_REGEX.test(bid.params.auId);
return !!(validAuId && (bid.bidId || (bid.params.member && bid.params.invCode)));
},

buildRequests: function (validBidRequests, bidderRequest) {
Expand All @@ -100,39 +90,36 @@ export const spec = {
if (gdprApplies !== undefined) request.push('consentString=' + consentString);
if (segments.length > 0) request.push('segments=' + segments.join(','));
if (usi) request.push('userId=' + usi);
if (bidderConfig.useCookie === false) request.push('noCookies=true')
for (var i = 0; i < validBidRequests.length; i++) {
if (bidderConfig.useCookie === false) request.push('noCookies=true');
if (bidderConfig.maxDeals > 0) request.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT));
for (let i = 0; i < validBidRequests.length; i++) {
const bid = validBidRequests[i]
let network = bid.params.network || 'network';
const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT));
const targeting = bid.params.targeting || {};

if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context !== 'outstream') {
network += '_video'
}

// if (bid.mediaTypes && bid.mediaTypes.native) {
// network += '_native'
// }

bidRequests[network] = bidRequests[network] || [];
bidRequests[network].push(bid);

networks[network] = networks[network] || {};
networks[network].adUnits = networks[network].adUnits || [];
if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page;
if (adnMeta) networks[network].metaData = adnMeta;
const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId }
const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId, maxDeals: maxDeals }
if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes
networks[network].adUnits.push(adUnit);
}

const networkKeys = Object.keys(networks)
for (var j = 0; j < networkKeys.length; j++) {
for (let j = 0; j < networkKeys.length; j++) {
const network = networkKeys[j];
const networkRequest = [...request]
if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) }
const requestURL = gdprApplies ? ENDPOINT_URL_EUROPE : ENDPOINT_URL
// if (network.indexOf('_native') > -1) { networkRequest.push('tt=' + DEFAULT_NATIVE) }
requests.push({
method: 'POST',
url: requestURL + '?' + networkRequest.join('&'),
Expand All @@ -146,50 +133,82 @@ export const spec = {

interpretResponse: function (serverResponse, bidRequest) {
const adUnits = serverResponse.body.adUnits;
const bidResponsesById = adUnits.reduce((response, adUnit) => {
if (adUnit.matchedAdCount >= 1) {
const ad = adUnit.ads[0];
const effectiveCpm = (ad.bid) ? ad.bid.amount * 1000 : 0;
const adResponse = {
...response,
[adUnit.targetId]: {
requestId: adUnit.targetId,
cpm: effectiveCpm,
width: Number(ad.creativeWidth),
height: Number(ad.creativeHeight),
creativeId: ad.creativeId,
currency: (ad.bid) ? ad.bid.currency : 'EUR',
dealId: ad.dealId || '',
meta: {
advertiserDomains: (ad.destinationUrls.destination) ? [ad.destinationUrls.destination.split('/')[2]] : []

},
netRevenue: false,
ttl: 360,
}
}

if (adUnit.vastXml) {
adResponse[adUnit.targetId].vastXml = adUnit.vastXml
adResponse[adUnit.targetId].mediaType = VIDEO
// } else if (ad.assets && ad.assets.image && ad.text && ad.text.title && ad.text.body && ad.destinationUrls && ad.destinationUrls.destination) {
// adResponse[adUnit.targetId].native = createNative(ad);
// adResponse[adUnit.targetId].mediaType = NATIVE;
} else {
adResponse[adUnit.targetId].ad = adUnit.html
}

return adResponse
} else return response

function buildAdResponse(bidderCode, ad, adUnit, dealCount) {
const destinationUrls = ad.destinationUrls || {};
const advertiserDomains = [];
for (const value of Object.values(destinationUrls)) {
advertiserDomains.push(value.split('/')[2])
}
const adResponse = {
bidderCode: bidderCode,
requestId: adUnit.targetId,
cpm: (ad.bid) ? ad.bid.amount * 1000 : 0,
width: Number(ad.creativeWidth),
height: Number(ad.creativeHeight),
creativeId: ad.creativeId,
currency: (ad.bid) ? ad.bid.currency : 'EUR',
dealId: ad.dealId || '',
dealCount: dealCount,
meta: {
advertiserDomains: advertiserDomains
},
netRevenue: false,
ttl: 360,
};
// Deal bids provide the rendered ad content along with the
// bid; whereas regular bids have it stored on the ad-unit.
const isDeal = dealCount > 0;
const renderSource = isDeal ? ad : adUnit;
if (renderSource.vastXml) {
adResponse.vastXml = renderSource.vastXml
adResponse.mediaType = VIDEO
} else {
adResponse.ad = renderSource.html
}
return adResponse;
}

const bidsById = bidRequest.bid.reduce((response, bid) => {
return {
...response,
[bid.bidId]: bid
};
}, {});

const bidResponse = bidRequest.bid.map(bid => bid.bidId).reduce((request, adunitId) => {
if (bidResponsesById[adunitId]) { request.push(bidResponsesById[adunitId]) }
return request
const hasBidAdUnits = adUnits.filter((au) => {
const bid = bidsById[au.targetId];
if (bid && bid.bidder === BIDDER_CODE) {
return au.matchedAdCount > 0;
} else {
// We do NOT accept bids when using this adaptor via one of the
// "deals" aliases; those requests are for ONLY getting deals from Adnuntius
return false;
}
});
const hasDealsAdUnits = adUnits.filter((au) => {
return au.deals && au.deals.length > 0;
});

const dealAdResponses = hasDealsAdUnits.reduce((response, au) => {
const bid = bidsById[au.targetId];
if (bid) {
(au.deals || []).forEach((deal, i) => {
response.push(buildAdResponse(bid.bidder, deal, au, i + 1));
});
}
return response;
}, []);

return bidResponse
},
const bidAdResponses = hasBidAdUnits.reduce((response, au) => {
const bid = bidsById[au.targetId];
if (bid) {
response.push(buildAdResponse(bid.bidder, au.ads[0], au, 0));
}
return response;
}, []);

return [...dealAdResponses, ...bidAdResponses];
}
}
registerBidder(spec);
3 changes: 2 additions & 1 deletion modules/adnuntiusBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Maintainer: info@adnuntius.com
# Description

Adnuntius Bidder Adapter for Prebid.js.
Only Banner format is supported.
Banner and Video formats are supported.

# Test Parameters
```
Expand All @@ -27,6 +27,7 @@ Only Banner format is supported.
params: {
auId: "8b6bc",
network: "adnuntius",
maxDeals: 0
}
},
]
Expand Down
Loading

0 comments on commit 3b7c8d6

Please sign in to comment.