Give a syndicated offer a title a stranger can read

A campaign's name is the member's own internal label, and some of them are
two characters ("MG"). DripOffers rejects anything under five, and more to
the point a click-earner scanning the offerwall learns nothing from "MG".

So the ad's own headline comes first, then the label, and a very short
label is qualified with the destination host — "MG (mailer.gold)" — rather
than dropped or dressed up in marketing copy we invented on the member's
behalf. Nothing usable at all returns null instead of a made-up title.

Found by the backfill: campaign 88 failed on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-18 06:52:24 -05:00
parent d4e2f59f9b
commit 71ab9555e2
2 changed files with 28 additions and 2 deletions
+20 -2
View File
@@ -108,6 +108,23 @@ function countriesFor(geo, tiers) {
return [...new Set(out.map(c => String(c).toUpperCase()))].join(',');
}
// What a click-earner sees in the offerwall list. A campaign's `name` is the member's own
// internal label and is often too short to mean anything to a stranger ("MG"), so the ad's
// headline comes first, then the label, and a very short label is qualified with the
// destination host rather than dropped or dressed up in copy we invented for them. The
// platform's own floor is five characters.
function titleFor(campaign, url) {
const clean = v => String(v || '').replace(/\s+/g, ' ').trim();
const head = clean(campaign.title), label = clean(campaign.name);
let host = '';
try { host = new URL(url).hostname.replace(/^www\./, ''); } catch (e) {}
const pick = head.length >= 5 ? head
: label.length >= 5 ? label
: label && host ? label + ' (' + host + ')'
: host;
return pick.length >= 5 ? pick.slice(0, 250) : null;
}
// Push a campaign. Returns null when it is not worth syndicating (or cannot be targeted
// faithfully) rather than throwing, so the caller treats "too small", "wrong format" and
// "disabled" the same quiet way.
@@ -125,7 +142,8 @@ async function push(campaign, opts) {
const countries = countriesFor(campaign.geo, (opts && opts.tiers) || null);
if (countries === null) return null;
const title = String(campaign.name || campaign.title || 'InstantAdPay').slice(0, 250);
const title = titleFor(campaign, url);
if (!title) return null;
// The offerwall shows this line under the title, so it has to read like an offer rather
// than like an internal campaign record.
const description = String(campaign.title || campaign.body || campaign.name || title).slice(0, 250);
@@ -156,6 +174,6 @@ async function remove(campaignId) {
}
async function packs() { return enabled() ? call('packs', {}) : null; }
module.exports = { enabled, push, readServed, pause, resume, remove, packs,
module.exports = { enabled, push, readServed, pause, resume, remove, packs, titleFor,
clicksFor, countriesFor, kindOk, refFor,
MIN_CREDITS, MAX_CLICKS, MIN_CLICKS, PACK_ID, KINDS };
+8
View File
@@ -24,6 +24,14 @@ t('tier 3 alone is skipped, not silently widened', drip.countriesFor('3', tiers)
t('credits convert to clicks one for one', drip.clicksFor(4000) === 4000, String(drip.clicksFor(4000)));
t('one campaign cannot swallow the platform', drip.clicksFor(500000) === drip.MAX_CLICKS, String(drip.clicksFor(500000)));
// the offerwall title a stranger actually reads (the platform's floor is 5 characters)
const T = (c, u) => drip.titleFor(c, u || 'https://www.mailer.gold/x');
t('the ad headline wins over the internal label', T({ title: 'Get paid instantly', name: 'MG' }) === 'Get paid instantly', String(T({ title: 'Get paid instantly', name: 'MG' })));
t('a usable label is used as-is', T({ name: 'Branded Voice' }) === 'Branded Voice', String(T({ name: 'Branded Voice' })));
t('a two-letter label is qualified, not dropped', T({ name: 'MG' }) === 'MG (mailer.gold)', String(T({ name: 'MG' })));
t('no label at all falls back to the destination', T({}) === 'mailer.gold', String(T({})));
t('nothing usable returns null rather than a made-up title', T({}, 'not a url') === null, String(T({}, 'not a url')));
t('a click format is accepted', drip.kindOk('banner') && drip.kindOk('visits') && drip.kindOk('text'));
t('video and solo are not click formats', !drip.kindOk('video') && !drip.kindOk('solo') && !drip.kindOk('featured'));