|
| 1 | +import * as utils from '../src/utils'; |
| 2 | +import {registerBidder} from '../src/adapters/bidderFactory'; |
| 3 | +import {config} from '../src/config'; |
| 4 | +import {Renderer} from '../src/Renderer'; |
| 5 | +import {BANNER, VIDEO} from '../src/mediaTypes'; |
| 6 | + |
| 7 | +const ENDPOINTS = { |
| 8 | + 'gamoshi': 'https://rtb.gamoshi.io' |
| 9 | +}; |
| 10 | + |
| 11 | +const DEFAULT_TTL = 360; |
| 12 | + |
| 13 | +export const helper = { |
| 14 | + getTopFrame: function () { |
| 15 | + try { |
| 16 | + return window.top === window ? 1 : 0; |
| 17 | + } catch (e) { |
| 18 | + } |
| 19 | + return 0; |
| 20 | + }, |
| 21 | + startsWith: function (str, search) { |
| 22 | + return str.substr(0, search.length) === search; |
| 23 | + }, |
| 24 | + getTopWindowDomain: function (url) { |
| 25 | + const domainStart = url.indexOf('://') + '://'.length; |
| 26 | + return url.substring(domainStart, url.indexOf('/', domainStart) < 0 ? url.length : url.indexOf('/', domainStart)); |
| 27 | + }, |
| 28 | + |
| 29 | + getMediaType: function (bid) { |
| 30 | + if (bid.ext) { |
| 31 | + if (bid.ext.media_type) { |
| 32 | + return bid.ext.media_type.toLowerCase(); |
| 33 | + } else if (bid.ext.vast_url) { |
| 34 | + return VIDEO; |
| 35 | + } else { |
| 36 | + return BANNER; |
| 37 | + } |
| 38 | + } |
| 39 | + return BANNER; |
| 40 | + } |
| 41 | +}; |
| 42 | + |
| 43 | +export const spec = { |
| 44 | + code: 'gamoshi', |
| 45 | + aliases: ['gambid', 'cleanmedia', '9MediaOnline'], |
| 46 | + supportedMediaTypes: ['banner', 'video'], |
| 47 | + |
| 48 | + isBidRequestValid: function (bid) { |
| 49 | + return !!bid.params.supplyPartnerId && utils.isStr(bid.params.supplyPartnerId) && |
| 50 | + (!bid.params['rtbEndpoint'] || utils.isStr(bid.params['rtbEndpoint'])) && |
| 51 | + (!bid.params.bidfloor || utils.isNumber(bid.params.bidfloor)) && |
| 52 | + (!bid.params['adpos'] || utils.isNumber(bid.params['adpos'])) && |
| 53 | + (!bid.params['protocols'] || Array.isArray(bid.params['protocols'])) && |
| 54 | + (!bid.params.instl || bid.params.instl === 0 || bid.params.instl === 1); |
| 55 | + }, |
| 56 | + |
| 57 | + buildRequests: function (validBidRequests, bidderRequest) { |
| 58 | + return validBidRequests.map(bidRequest => { |
| 59 | + const {adUnitCode, auctionId, mediaTypes, params, sizes, transactionId} = bidRequest; |
| 60 | + const baseEndpoint = params['rtbEndpoint'] || ENDPOINTS['gamoshi']; |
| 61 | + const rtbEndpoint = `${baseEndpoint}/r/${params.supplyPartnerId}/bidr?rformat=open_rtb&reqformat=rtb_json&bidder=prebid` + (params.query ? '&' + params.query : ''); |
| 62 | + let url = config.getConfig('pageUrl') || bidderRequest.refererInfo.referer; |
| 63 | + |
| 64 | + const rtbBidRequest = { |
| 65 | + id: auctionId, |
| 66 | + site: { |
| 67 | + domain: helper.getTopWindowDomain(url), |
| 68 | + page: url, |
| 69 | + ref: bidderRequest.refererInfo.referer |
| 70 | + }, |
| 71 | + device: { |
| 72 | + ua: navigator.userAgent, |
| 73 | + dnt: utils.getDNT() ? 1 : 0, |
| 74 | + h: screen.height, |
| 75 | + w: screen.width, |
| 76 | + language: navigator.language |
| 77 | + }, |
| 78 | + imp: [], |
| 79 | + ext: {}, |
| 80 | + user: { |
| 81 | + ext: {} |
| 82 | + } |
| 83 | + }; |
| 84 | + const gdprConsent = bidderRequest.gdprConsent; |
| 85 | + |
| 86 | + if (gdprConsent && gdprConsent.consentString && gdprConsent.gdprApplies) { |
| 87 | + rtbBidRequest.ext.gdpr_consent = { |
| 88 | + consent_string: gdprConsent.consentString, |
| 89 | + consent_required: gdprConsent.gdprApplies |
| 90 | + }; |
| 91 | + rtbBidRequest.regs = { |
| 92 | + ext: { |
| 93 | + gdpr: gdprConsent.gdprApplies === true ? 1 : 0 |
| 94 | + } |
| 95 | + }; |
| 96 | + rtbBidRequest.user = { |
| 97 | + ext: { |
| 98 | + consent: gdprConsent.consentString |
| 99 | + } |
| 100 | + } |
| 101 | + } |
| 102 | + const imp = { |
| 103 | + id: transactionId, |
| 104 | + instl: params.instl === 1 ? 1 : 0, |
| 105 | + tagid: adUnitCode, |
| 106 | + bidfloor: params.bidfloor || 0, |
| 107 | + bidfloorcur: 'USD', |
| 108 | + secure: 1 |
| 109 | + }; |
| 110 | + |
| 111 | + const hasFavoredMediaType = |
| 112 | + params.favoredMediaType && this.supportedMediaTypes.includes(params.favoredMediaType); |
| 113 | + |
| 114 | + if (!mediaTypes || mediaTypes.banner) { |
| 115 | + if (!hasFavoredMediaType || params.favoredMediaType === BANNER) { |
| 116 | + const bannerImp = Object.assign({}, imp, { |
| 117 | + banner: { |
| 118 | + w: sizes.length ? sizes[0][0] : 300, |
| 119 | + h: sizes.length ? sizes[0][1] : 250, |
| 120 | + pos: params.pos || 0, |
| 121 | + topframe: utils.inIframe() ? 0 : 1 |
| 122 | + } |
| 123 | + }); |
| 124 | + rtbBidRequest.imp.push(bannerImp); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + if (mediaTypes && mediaTypes.video) { |
| 129 | + if (!hasFavoredMediaType || params.favoredMediaType === VIDEO) { |
| 130 | + const playerSize = mediaTypes.video.playerSize || sizes; |
| 131 | + const videoImp = Object.assign({}, imp, { |
| 132 | + video: { |
| 133 | + protocols: params.protocols || [1, 2, 3, 4, 5, 6], |
| 134 | + pos: params.pos || 0, |
| 135 | + ext: { |
| 136 | + context: mediaTypes.video.context |
| 137 | + } |
| 138 | + } |
| 139 | + }); |
| 140 | + |
| 141 | + if (utils.isArray(playerSize[0])) { |
| 142 | + videoImp.video.w = playerSize[0][0]; |
| 143 | + videoImp.video.h = playerSize[0][1]; |
| 144 | + } else if (utils.isNumber(playerSize[0])) { |
| 145 | + videoImp.video.w = playerSize[0]; |
| 146 | + videoImp.video.h = playerSize[1]; |
| 147 | + } else { |
| 148 | + videoImp.video.w = 300; |
| 149 | + videoImp.video.h = 250; |
| 150 | + } |
| 151 | + |
| 152 | + rtbBidRequest.imp.push(videoImp); |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + let eids = []; |
| 157 | + if (bidRequest && bidRequest.userId) { |
| 158 | + addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 'ID5ID'); |
| 159 | + addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.tdid`), 'adserver.org', 'TDID'); |
| 160 | + } |
| 161 | + if (eids.length > 0) { |
| 162 | + rtbBidRequest.user.ext.eids = eids; |
| 163 | + } |
| 164 | + |
| 165 | + if (rtbBidRequest.imp.length === 0) { |
| 166 | + return; |
| 167 | + } |
| 168 | + |
| 169 | + return {method: 'POST', url: rtbEndpoint, data: rtbBidRequest, bidRequest}; |
| 170 | + }); |
| 171 | + }, |
| 172 | + |
| 173 | + interpretResponse: function (serverResponse, bidRequest) { |
| 174 | + const response = serverResponse && serverResponse.body; |
| 175 | + if (!response) { |
| 176 | + utils.logError('empty response'); |
| 177 | + return []; |
| 178 | + } |
| 179 | + |
| 180 | + const bids = response.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []); |
| 181 | + let outBids = []; |
| 182 | + |
| 183 | + bids.forEach(bid => { |
| 184 | + const outBid = { |
| 185 | + requestId: bidRequest.bidRequest.bidId, |
| 186 | + cpm: bid.price, |
| 187 | + width: bid.w, |
| 188 | + height: bid.h, |
| 189 | + ttl: DEFAULT_TTL, |
| 190 | + creativeId: bid.crid || bid.adid, |
| 191 | + netRevenue: true, |
| 192 | + currency: bid.cur || response.cur, |
| 193 | + mediaType: helper.getMediaType(bid) |
| 194 | + }; |
| 195 | + |
| 196 | + if (utils.deepAccess(bidRequest.bidRequest, 'mediaTypes.' + outBid.mediaType)) { |
| 197 | + if (outBid.mediaType === BANNER) { |
| 198 | + outBids.push(Object.assign({}, outBid, {ad: bid.adm})); |
| 199 | + } else if (outBid.mediaType === VIDEO) { |
| 200 | + const context = utils.deepAccess(bidRequest.bidRequest, 'mediaTypes.video.context'); |
| 201 | + outBids.push(Object.assign({}, outBid, { |
| 202 | + vastUrl: bid.ext.vast_url, |
| 203 | + vastXml: bid.adm, |
| 204 | + renderer: context === 'outstream' ? newRenderer(bidRequest.bidRequest, bid) : undefined |
| 205 | + })); |
| 206 | + } |
| 207 | + } |
| 208 | + }); |
| 209 | + return outBids; |
| 210 | + }, |
| 211 | + |
| 212 | + getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { |
| 213 | + const syncs = []; |
| 214 | + const gdprApplies = gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean') ? gdprConsent.gdprApplies : false; |
| 215 | + const suffix = gdprApplies ? 'gc=' + encodeURIComponent(gdprConsent.consentString) : 'gc=missing'; |
| 216 | + serverResponses.forEach(resp => { |
| 217 | + if (resp.body) { |
| 218 | + const bidResponse = resp.body; |
| 219 | + if (bidResponse.ext && Array.isArray(bidResponse.ext['utrk'])) { |
| 220 | + bidResponse.ext['utrk'].forEach(pixel => { |
| 221 | + const url = pixel.url + (pixel.url.indexOf('?') > 0 ? '&' + suffix : '?' + suffix); |
| 222 | + return syncs.push({type: pixel.type, url}); |
| 223 | + }); |
| 224 | + } |
| 225 | + if (Array.isArray(bidResponse.seatbid)) { |
| 226 | + bidResponse.seatbid.forEach(seatBid => { |
| 227 | + if (Array.isArray(seatBid.bid)) { |
| 228 | + seatBid.bid.forEach(bid => { |
| 229 | + if (bid.ext && Array.isArray(bid.ext['utrk'])) { |
| 230 | + bid.ext['utrk'].forEach(pixel => { |
| 231 | + const url = pixel.url + (pixel.url.indexOf('?') > 0 ? '&' + suffix : '?' + suffix); |
| 232 | + return syncs.push({type: pixel.type, url}); |
| 233 | + }); |
| 234 | + } |
| 235 | + }); |
| 236 | + } |
| 237 | + }); |
| 238 | + } |
| 239 | + } |
| 240 | + }); |
| 241 | + return syncs; |
| 242 | + } |
| 243 | +}; |
| 244 | + |
| 245 | +function newRenderer(bidRequest, bid, rendererOptions = {}) { |
| 246 | + const renderer = Renderer.install({ |
| 247 | + url: (bidRequest.params && bidRequest.params.rendererUrl) || (bid.ext && bid.ext.renderer_url) || 'https://s.gamoshi.io/video/latest/renderer.js', |
| 248 | + config: rendererOptions, |
| 249 | + loaded: false, |
| 250 | + }); |
| 251 | + try { |
| 252 | + renderer.setRender(renderOutstream); |
| 253 | + } catch (err) { |
| 254 | + utils.logWarn('Prebid Error calling setRender on renderer', err); |
| 255 | + } |
| 256 | + return renderer; |
| 257 | +} |
| 258 | + |
| 259 | +function renderOutstream(bid) { |
| 260 | + bid.renderer.push(() => { |
| 261 | + const unitId = bid.adUnitCode + '/' + bid.adId; |
| 262 | + window['GamoshiPlayer'].renderAd({ |
| 263 | + id: unitId, |
| 264 | + debug: window.location.href.indexOf('pbjsDebug') >= 0, |
| 265 | + placement: document.getElementById(bid.adUnitCode), |
| 266 | + width: bid.width, |
| 267 | + height: bid.height, |
| 268 | + events: { |
| 269 | + ALL_ADS_COMPLETED: () => window.setTimeout(() => { |
| 270 | + window['GamoshiPlayer'].removeAd(unitId); |
| 271 | + }, 300) |
| 272 | + }, |
| 273 | + vastUrl: bid.vastUrl, |
| 274 | + vastXml: bid.vastXml |
| 275 | + }); |
| 276 | + }); |
| 277 | +} |
| 278 | + |
| 279 | +function addExternalUserId(eids, value, source, rtiPartner) { |
| 280 | + if (utils.isStr(value)) { |
| 281 | + eids.push({ |
| 282 | + source, |
| 283 | + uids: [{ |
| 284 | + id: value, |
| 285 | + ext: { |
| 286 | + rtiPartner |
| 287 | + } |
| 288 | + }] |
| 289 | + }); |
| 290 | + } |
| 291 | +} |
| 292 | + |
| 293 | +registerBidder(spec); |
0 commit comments