Skip to content

Conversation

@iamandrea
Copy link

@iamandrea iamandrea commented Dec 18, 2025

Type of change

  • Bugfix

  • Feature

  • New bidder adapter

  • Updated bidder adapter

  • Code style update (formatting, local variables)

  • Refactoring (no functional changes, no api changes)

  • Build related changes

  • CI related changes

  • Does this change affect user-facing APIs or examples documented on http://prebid.org?

  • Other

Description of change

Other information

monofonik and others added 16 commits December 2, 2025 15:05
Implements a new Prebid.js module that displays overlay ads when users reach
the end of content. Key features include:

- Configurable triggers: scroll depth, time on page, exit intent, or custom functions
- Pre-fetch strategies: eager (immediate) or lazy (conditional) bid caching
- Overlay/interstitial display formats with customizable close button delays
- Frequency capping: session and daily limits with localStorage persistence
- Publisher callbacks: onBidCached, onTrigger, onAdRender, onAdClose, onFrequencyCapReached
- Multi-bidder support: works with any Prebid bidder adapter

Demo includes GumGum integration with hardcoded test bid for demonstration.

Files:
- modules/echoAdsModule/index.js:662 - Core module implementation
- modules/echoAdsModule.md - Module documentation
- integrationExamples/gpt/echoAds_gumgum.html - Interactive demo page
- modules/gumgumBidAdapter.js:607 - Test bid injection for demo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Automated deployment script for Google Cloud Storage
- Auto-builds Prebid bundle if not present
- Fixes paths for GCS hosting (absolute URLs)
- Sets proper content-types and public ACLs
- No-cache headers for development testing

Usage: ./deploy-demo.sh
Demo URL: https://storage.googleapis.com/echoads-demo/index.html

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Automatic triggers (scroll, time, exit intent) now only fire once per page load
- Manual triggers can still be called multiple times
- Manual triggers still respect frequency caps but bypass hasBeenTriggered flag
- Improves user experience by preventing duplicate automatic triggers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Automatic triggers (scroll, time) now fire only once per session
- Set hasBeenTriggered immediately to prevent race conditions
- Manual triggers bypass hasBeenTriggered and can fire multiple times
- Clear cached bid immediately when ad is shown (not when closed)
- Each bid is used only once - fresh auction for each manual trigger
- Update demo creative to ACME image hosted on GCS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Changed info box text from Nintendo to ACME creative
- Reflects the updated hardcoded creative in GumGum adapter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use no-store, no-cache, must-revalidate for all demo files
- Ensures fresh content on every reload for testing
- Applies to HTML, JS bundle, and creative image

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Changed formula to show 0% at top, 100% at bottom
- Previously showed percentage of content viewed (39% at top)
- Now shows actual scroll position in scrollable area

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Changed module's scroll calculation to match demo page
- Now correctly shows 0% at top, 100% at bottom
- Previously used content-viewed formula instead of scroll position

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Created /demo folder with static site structure
- Updated ACME creative URL to GitHub Pages
- Fixed prebid.js path for demo deployment
- Includes ACME 300x250 creative image
- Ready for GitHub Pages deployment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Move prebid.js directly to docs/ folder (no build path)
- Avoids gitignore conflicts with build/ folder
- Add deploy-github-pages.sh script for easy deployment
- Updated HTML to reference prebid.js directly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added a server backend (server.py) to allow saving new test creatives directly to test-creatives/creatives.json from the demo UI.
Updated package.json to use the Python server for local development.
Overhauled the index.html demo interactive UX:
Users can now paste ad tags, assign custom names, and save/replace them permanently in the creatives list.
Saved creatives immediately appear in the dropdown for creative selection.
It tries saving to the backend first; fallback to JSON download if server is unavailable.
Optimized controls layout:
New configuration message.
Test Creative section (dropdown, paste tag, save) is directly below the configuration.
Controls & Metrics now show beneath Test Creative.
Logs (event log) appear last, in their own card with a heading.
UI buttons ("Use Pasted Tag", "Save to JSON") are now orange, matching other primary buttons.
@github-actions
Copy link

Whoa there, partner! 🌵🤠 We wrangled some duplicated code in your PR:

Reducing code duplication by importing common functions from a library not only makes our code cleaner but also easier to maintain. Please move the common code from both files into a library and import it in each. We hate that we have to mention this, however, commits designed to hide from this utility by renaming variables or reordering an object are poor conduct. We will not look upon them kindly! Keep up the great work! 🚀

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +594 to +598
if (selectedCreative) {
logWarn('[GumGum] DEMO MODE: Returning test bid for Echo Ads demo (' + selectedCreative.name + ')');
return [{
ad: selectedCreative.creative,
mediaType: BANNER,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Demo flag overrides real GumGum bids

When window.selectedTestCreative is present, the adapter short-circuits interpretResponse and returns a hardcoded banner with a fixed CPM instead of using the actual GumGum server response. Any page (including non-demo pages that happen to define that global) will replace legitimate bids with the static creative and price, breaking auction results and reporting whenever that flag is set.

Useful? React with 👍 / 👎.

Comment on lines +533 to +537
// Render the ad first - insert HTML directly
if (cachedBid.ad) {
adContainer.innerHTML = cachedBid.ad;
} else {
logError(`${MODULE_NAME}: Cached bid has no ad creative`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Echo Ads injects creatives without rendering scripts

The overlay renderer assigns cachedBid.ad directly to innerHTML; script tags inside the creative (or creatives delivered via adUrl) will not execute, so most bidder markup will never render or fire trackers, despite the module claiming to work with any Prebid bidder. The overlay will therefore show blank content for standard script-based banners rather than the winning ad.

Useful? React with 👍 / 👎.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bot is right, you may want to create an iframe and pass its document to pbjs.renderAd instead

@@ -0,0 +1,1422 @@
<!--
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this demo stuff needs to be a folder inside integrationExamples

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this includes the first two bash scripts

}
},
onBidCached: function(bidInfo) {
logEvent('✓ Bid cached: ' + bidInfo.bidder + ' $' + bidInfo.cpm.toFixed(2) + ' CPM (' + bidInfo.size + ')');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no emojis

logEvent('✓ Echo Ad closed by user');
},
onFrequencyCapReached: function() {
logEvent('⚠ Frequency cap reached - ad not shown');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no emojis

@patmmccann patmmccann changed the title Save tag Echo Ads module: initial release Dec 18, 2025
Comment on lines +183 to +194
// Add the ad unit
pbjs.addAdUnits([moduleConfig.adUnit]);

auctionInProgress = true;

// Request bids
pbjs.requestBids({
adUnitCodes: [moduleConfig.adUnit.code],
bidsBackHandler: function(bids) {
logInfo(`${MODULE_NAME}: Bids returned`, bids);
}
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can pass the ad unit directly to requestBids({adUnits: [moduleConfig.adUnit]}) which I think is preferable since it doesn't affect subsequent auctions as addAdUnits does.

If you expect this to be the only way to get the "special" bid you could also cache it from bidsBackHandler instead of using an event handler.

Comment on lines +533 to +537
// Render the ad first - insert HTML directly
if (cachedBid.ad) {
adContainer.innerHTML = cachedBid.ad;
} else {
logError(`${MODULE_NAME}: Cached bid has no ad creative`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bot is right, you may want to create an iframe and pass its document to pbjs.renderAd instead

@github-actions
Copy link

Whoa there, partner! 🌵🤠 We wrangled some duplicated code in your PR:

Reducing code duplication by importing common functions from a library not only makes our code cleaner but also easier to maintain. Please move the common code from both files into a library and import it in each. We hate that we have to mention this, however, commits designed to hide from this utility by renaming variables or reordering an object are poor conduct. We will not look upon them kindly! Keep up the great work! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants