UNPKG

mk9-prebid

Version:

Header Bidding Management Library

411 lines (356 loc) 14.7 kB
import {config} from '../src/config.js' import * as utils from '../src/utils.js' import {registerBidder} from '../src/adapters/bidderFactory.js' import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js' import includes from 'core-js-pure/features/array/includes.js' /** * Adapter for requesting bids from adxcg.net * updated to latest prebid repo on 2017.10.20 * updated for gdpr compliance on 2018.05.22 -requires gdpr compliance module * updated to pass aditional auction and impression level parameters. added pass for video targeting parameters * updated to fix native support for image width/height and icon 2019.03.17 * updated support for userid - pubcid,ttid 2019.05.28 * updated to support prebid 3.0 - remove non https, move to banner.xx.sizes, remove utils.getTopWindowLocation,remove utils.getTopWindowUrl(),remove utils.getTopWindowReferrer() * updated to support prebid 4.0 - standardized video params, updated video validation, add onBidWon, onTimeOut, use standardized getFloor */ const BIDDER_CODE = 'adxcg' const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE] const SOURCE = 'pbjs10' const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', 'startdelay', 'skippable', 'playback_method', 'frameworks'] const USER_PARAMS_AUCTION = ['forcedDspIds', 'forcedCampaignIds', 'forcedCreativeIds', 'gender', 'dnt', 'language'] const USER_PARAMS_BID = ['lineparam1', 'lineparam2', 'lineparam3'] const BIDADAPTERVERSION = 'r20210330PB40' const DEFAULT_MIN_FLOOR = 0; export const spec = { code: BIDDER_CODE, supportedMediaTypes: SUPPORTED_AD_TYPES, /** * Determines whether or not the given bid request is valid. * * @param {object} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { if (!bid || !bid.params) { utils.logWarn(BIDDER_CODE + ': Missing bid parameters'); return false } if (!utils.isStr(bid.params.adzoneid)) { utils.logWarn(BIDDER_CODE + ': adzoneid must be specified as a string'); return false } if (isBannerRequest(bid)) { const banneroAdUnit = utils.deepAccess(bid, 'mediaTypes.banner'); if (!banneroAdUnit.sizes) { utils.logWarn(BIDDER_CODE + ': banner sizes must be specified'); return false; } } if (isVideoRequest(bid)) { // prebid 4.0 use standardized Video parameters const videoAdUnit = utils.deepAccess(bid, 'mediaTypes.video'); if (!Array.isArray(videoAdUnit.playerSize)) { utils.logWarn(BIDDER_CODE + ': video playerSize must be an array of integers'); return false; } if (!videoAdUnit.context) { utils.logWarn(BIDDER_CODE + ': video context must be specified'); return false; } if (!Array.isArray(videoAdUnit.mimes) || videoAdUnit.mimes.length === 0) { utils.logWarn(BIDDER_CODE + ': video mimes must be an array of strings'); return false; } if (!Array.isArray(videoAdUnit.protocols) || videoAdUnit.protocols.length === 0) { utils.logWarn(BIDDER_CODE + ': video protocols must be an array of integers'); return false; } } return true }, /** * Make a server request from the list of BidRequests. * * an array of validBidRequests * Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { let dt = new Date(); let ratio = window.devicePixelRatio || 1; let iobavailable = window && window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype && 'intersectionRatio' in window.IntersectionObserverEntry.prototype let bt = config.getConfig('bidderTimeout'); if (window.PREBID_TIMEOUT) { bt = Math.min(window.PREBID_TIMEOUT, bt); } let referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); let page = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); // add common parameters let beaconParams = { renderformat: 'javascript', ver: BIDADAPTERVERSION, secure: '1', source: SOURCE, uw: window.screen.width, uh: window.screen.height, dpr: ratio, bt: bt, isinframe: utils.inIframe(), cookies: utils.checkCookieSupport() ? '1' : '0', tz: dt.getTimezoneOffset(), dt: utils.timestamp(), iob: iobavailable ? '1' : '0', pbjs: '$prebid.version$', rndid: Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000, ref: encodeURIComponent(referrer), url: encodeURIComponent(page) }; if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { beaconParams.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0'; beaconParams.gdpr_consent = bidderRequest.gdprConsent.consentString; } if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.pubcid'))) { beaconParams.pubcid = validBidRequests[0].userId.pubcid; } if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { beaconParams.tdid = validBidRequests[0].userId.tdid; } if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.id5id.uid'))) { beaconParams.id5id = validBidRequests[0].userId.id5id.uid; } if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.idl_env'))) { beaconParams.idl_env = validBidRequests[0].userId.idl_env; } let biddercustom = config.getConfig(BIDDER_CODE); if (biddercustom) { Object.keys(biddercustom) .filter(param => includes(USER_PARAMS_AUCTION, param)) .forEach(param => beaconParams[param] = encodeURIComponent(biddercustom[param])) } // per impression parameters let adZoneIds = []; let prebidBidIds = []; let sizes = []; let bidfloors = []; validBidRequests.forEach((bid, index) => { adZoneIds.push(utils.getBidIdParameter('adzoneid', bid.params)); prebidBidIds.push(bid.bidId); let bidfloor = getFloor(bid); bidfloors.push(bidfloor); // copy all custom parameters impression level parameters not supported above let customBidParams = utils.getBidIdParameter('custom', bid.params) || {} if (customBidParams) { Object.keys(customBidParams) .filter(param => includes(USER_PARAMS_BID, param)) .forEach(param => beaconParams[param + '.' + index] = encodeURIComponent(customBidParams[param])) } if (isBannerRequest(bid)) { sizes.push(utils.parseSizesInput(bid.mediaTypes.banner.sizes).join('|')); } if (isNativeRequest(bid)) { sizes.push('0x0'); } if (isVideoRequest(bid)) { if (bid.params.video) { Object.keys(bid.params.video) .filter(param => includes(VIDEO_TARGETING, param)) .forEach(param => beaconParams['video.' + param + '.' + index] = encodeURIComponent(bid.params.video[param])) } // copy video standarized params beaconParams['video.context' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.context'); sizes.push(utils.parseSizesInput(bid.mediaTypes.video.playerSize).join('|')); beaconParams['video.mimes' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.mimes').join(','); beaconParams['video.protocols' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.protocols').join(','); } }) beaconParams.adzoneid = adZoneIds.join(','); beaconParams.format = sizes.join(','); beaconParams.prebidBidIds = prebidBidIds.join(','); beaconParams.bidfloors = bidfloors.join(','); let adxcgRequestUrl = utils.buildUrl({ protocol: 'https', hostname: 'hbps.adxcg.net', pathname: '/get/adi', search: beaconParams }); utils.logMessage(`calling adi adxcg`); return { contentType: 'text/plain', method: 'GET', url: adxcgRequestUrl, withCredentials: true }; }, /** * Unpack the response from the server into a list of bids. * * @param {*} serverResponse A successful response from the server. * @return {bidRequests[]} An array of bids which were nested inside the server. */ interpretResponse: function (serverResponse) { utils.logMessage(`interpretResponse adxcg`); let bidsAll = []; if (!serverResponse || !serverResponse.body || !utils.isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid.length) { utils.logWarn(BIDDER_CODE + ': empty bid response'); return bidsAll; } serverResponse.body.seatbid.forEach((bids) => { bids.bid.forEach((serverResponseOneItem) => { let bid = {} // parse general fields bid.requestId = serverResponseOneItem.impid; bid.cpm = serverResponseOneItem.price; bid.creativeId = parseInt(serverResponseOneItem.crid); bid.currency = serverResponseOneItem.currency ? serverResponseOneItem.currency : 'USD'; bid.netRevenue = serverResponseOneItem.netRevenue ? serverResponseOneItem.netRevenue : true; bid.ttl = serverResponseOneItem.ttl ? serverResponseOneItem.ttl : 300; bid.width = serverResponseOneItem.w; bid.height = serverResponseOneItem.h; bid.burl = serverResponseOneItem.burl || ''; if (serverResponseOneItem.dealid != null && serverResponseOneItem.dealid.trim().length > 0) { bid.dealId = serverResponseOneItem.dealid; } if (serverResponseOneItem.ext.crType === 'banner') { bid.ad = serverResponseOneItem.adm; } else if (serverResponseOneItem.ext.crType === 'video') { bid.vastUrl = serverResponseOneItem.nurl; bid.vastXml = serverResponseOneItem.adm; bid.mediaType = 'video'; } else if (serverResponseOneItem.ext.crType === 'native') { bid.mediaType = 'native'; bid.native = parseNative(JSON.parse(serverResponseOneItem.adm)); } else { utils.logWarn(BIDDER_CODE + ': unknown or undefined crType'); } // prebid 4.0 meta taxonomy if (utils.isArray(serverResponseOneItem.adomain)) { utils.deepSetValue(bid, 'meta.advertiserDomains', serverResponseOneItem.adomain); } if (utils.isArray(serverResponseOneItem.cat)) { utils.deepSetValue(bid, 'meta.secondaryCatIds', serverResponseOneItem.cat); } if (utils.isPlainObject(serverResponseOneItem.ext)) { if (utils.isStr(serverResponseOneItem.ext.advertiser_id)) { utils.deepSetValue(bid, 'meta.mediaType', serverResponseOneItem.ext.mediaType); } if (utils.isStr(serverResponseOneItem.ext.advertiser_id)) { utils.deepSetValue(bid, 'meta.advertiserId', serverResponseOneItem.ext.advertiser_id); } if (utils.isStr(serverResponseOneItem.ext.advertiser_name)) { utils.deepSetValue(bid, 'meta.advertiserName', serverResponseOneItem.ext.advertiser_name); } if (utils.isStr(serverResponseOneItem.ext.agency_name)) { utils.deepSetValue(bid, 'meta.agencyName', serverResponseOneItem.ext.agency_name); } } bidsAll.push(bid) }) }) return bidsAll }, onBidWon: (bid) => { if (bid.burl) { utils.triggerPixel(utils.replaceAuctionPrice(bid.burl, bid.originalCpm)); } }, onTimeout(timeoutData) { if (timeoutData == null) { return; } let beaconParams = { A: timeoutData.bidder, bid: timeoutData.bidId, a: timeoutData.adUnitCode, cn: timeoutData.timeout, aud: timeoutData.auctionId, }; let adxcgRequestUrl = utils.buildUrl({ protocol: 'https', hostname: 'hbps.adxcg.net', pathname: '/event/timeout.gif', search: beaconParams }); utils.logWarn(BIDDER_CODE + ': onTimeout called'); utils.triggerPixel(adxcgRequestUrl); }, getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { let params = ''; if (gdprConsent && 'gdprApplies' in gdprConsent) { if (gdprConsent.consentString) { if (typeof gdprConsent.gdprApplies === 'boolean') { params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; } else { params += `?gdpr=0&gdpr_consent=${gdprConsent.consentString}`; } } } if (syncOptions.iframeEnabled) { return [{ type: 'iframe', url: 'https://cdn.adxcg.net/pb-sync.html' + params }]; } } } function isVideoRequest(bid) { return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); } function isBannerRequest(bid) { return bid.mediaType === 'banner' || !!utils.deepAccess(bid, 'mediaTypes.banner'); } function isNativeRequest(bid) { return bid.mediaType === 'native' || !!utils.deepAccess(bid, 'mediaTypes.native'); } function getFloor(bid) { if (!utils.isFn(bid.getFloor)) { return utils.deepAccess(bid, 'params.floor', DEFAULT_MIN_FLOOR); } try { const floor = bid.getFloor({ currency: 'EUR', mediaType: '*', size: '*', bidRequest: bid }); return floor.floor; } catch (e) { utils.logWarn(BIDDER_CODE + ': call to getFloor failed:' + e.message); return DEFAULT_MIN_FLOOR; } } function parseNative(nativeResponse) { let bidNative = {}; bidNative = { clickUrl: nativeResponse.link.url, impressionTrackers: nativeResponse.imptrackers, clickTrackers: nativeResponse.clktrackers, javascriptTrackers: nativeResponse.jstrackers }; nativeResponse.assets.forEach(asset => { if (asset.title && asset.title.text) { bidNative.title = asset.title.text; } if (asset.img && asset.img.url) { bidNative.image = { url: asset.img.url, height: asset.img.h, width: asset.img.w }; } if (asset.icon && asset.icon.url) { bidNative.icon = { url: asset.icon.url, height: asset.icon.h, width: asset.icon.w }; } if (asset.data && asset.data.label === 'DESC' && asset.data.value) { bidNative.body = asset.data.value; } if (asset.data && asset.data.label === 'SPONSORED' && asset.data.value) { bidNative.sponsoredBy = asset.data.value; } }) return bidNative; } registerBidder(spec)