Legacy bridge pages, welcome-back credits, holding-tank alerts

- /from/faucetwave and /from/tieroneads (?seg=advertiser): the squeeze page with
  brand copy and no video, sponsor cookie cleared so arrivals land in the holding
  tank, angle + a legacy:<brand> source recorded on the account.
- legacy.js: private list DATA_DIR/legacy.json (email -> brand, segment); listed
  emails get a one-time credit grant at account creation (legacyCreditsAdvertiser
  500 / legacyCreditsEarner 150, admin-editable), recorded in legacy-grants.json.
- Holding-tank alerts: dashboard payload carries who is waiting (Overview notice
  with usernames + adopt link), and a 15-minute tick posts new arrivals to the
  shared Telegram payments topic.
- Chatbot prompt lines + admin setting labels.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-12 07:25:32 -05:00
parent b844a227d3
commit eae2a04dc0
7 changed files with 113 additions and 5 deletions
+1 -1
View File
@@ -485,7 +485,7 @@
}));
// site settings: key / value rows; booleans as checkboxes, numbers stay numbers
const SITE_META = { siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
const SITE_META = { siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', legacyCreditsAdvertiser: 'Legacy welcome credits: former advertisers', legacyCreditsEarner: 'Legacy welcome credits: former earners', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
function drawSite() {
const wrap = $('siteForm');
wrap.innerHTML = Object.entries(siteObj).map(([k, v]) => '<div class="kv-row"><span class="k" title="' + esc(k) + '">' + esc(SITE_META[k] || humanize(k)) + '</span>'
+13 -2
View File
@@ -4,7 +4,17 @@
// set by the server when this page was served.
(function () {
const $ = id => document.getElementById(id);
const LEG = (brand, seg) => seg === 'adv'
? { eyebrow: 'For former ' + brand + ' advertisers', h: 'Your next ad budget <em>pays you back.</em>',
lead: brand + ' is closed. The people who bought ads there are exactly who InstantAdPay was built for: real ad packages from $5, seven formats, and every package in your line paid out by a verified contract on Polygon in the same transaction.',
points: ['Welcome-back credits land the moment your account exists: enough to run a real banner or text campaign today, on us.', 'Seven formats: banners, text ads, login ads, solo ads to member inboxes, video, featured links and verified visits. Views are timed on the server, so a real person saw your ad.', 'When anyone in your line buys ads, the contract pays you in that same transaction. Public on Polygonscan, nothing held, nothing to withdraw.'],
cta: 'Claim your welcome-back credits', sub: 'Free account by email. No password, no wallet today. Use the email you had on ' + brand + ': the credits are tied to it.', video: false }
: { eyebrow: 'For former ' + brand + ' members', h: 'Same daily habit. <em>Real payouts on-chain.</em>',
lead: 'You viewed ads on ' + brand + '. Here you view ads to earn credits, run your own campaign with them for free, and when anyone in your line buys ads you are paid in POL to your own wallet in the same transaction.',
points: ['Welcome-back credits on day one, so your first campaign runs before you have viewed a single ad.', 'Join with just an email. No password, no wallet today. Link a wallet later, only when you want payouts switched on.', 'Every payout is a public transaction on Polygon. Nothing is held, so there is nothing to withdraw and nothing to wait for.'],
cta: 'Claim your welcome-back credits', sub: 'Free account by email. Use the email you had on ' + brand + ': the credits are tied to it.', video: false };
const ANGLES = {
'fw-adv': LEG('Faucet Wave', 'adv'), 'fw-earn': LEG('Faucet Wave', 'earn'), 't1-adv': LEG('Tier One Ads', 'adv'), 't1-earn': LEG('Tier One Ads', 'earn'),
instant: { eyebrow: 'Same-transaction payouts', h: 'Paid before the page <em>reloads.</em>', lead: 'What if your commission landed before the thank-you page finished loading? On InstantAdPay that is not a metaphor. A smart contract on Polygon splits every ad package the moment it sells.',
points: ['A verified contract splits every package in the same transaction it sells: 50 percent to the sponsor, 20 and 10 up the line, 20 to the platform.', 'It lands in your own wallet in seconds. There is no balance to withdraw because nothing is ever held.', 'Every payment is public on Polygonscan, so you can check the claim before you spend a dollar.'],
cta: 'See a payout land in seconds', sub: 'Free account by email. No password, no wallet today. Your invite link is live the moment you are in.' },
@@ -21,7 +31,7 @@
points: ['Every direct buyer pays you 50 percent from their very first package.', 'Two qualifying buyers open level two at 20 percent. Five open level three at 10 percent. Constants in a verified contract.', 'Until a level opens, its share climbs to the next qualified member above, so the plan rewards the people who build.'],
cta: 'Start your line. Two buyers is the target.', sub: 'Free account by email. Your invite link and the team-building plays are waiting inside.' }
};
const v = new URLSearchParams(location.search).get('v');
const v = new URLSearchParams(location.search).get('v') || (document.cookie.match(/(?:^|; )iap\.angle=([^;]+)/) || [])[1] || '';
const a = v && ANGLES[v];
if (a) {
document.body.classList.add('squeeze'); // server sets it too; this covers cached HTML
@@ -29,7 +39,8 @@
if (a.cta) { $('jnCapH').textContent = a.cta; $('jnCapSub').textContent = a.sub || ''; }
// the matching hook video + this angle's three points replace the worked example
const VID = 'https://coolify-saasytop.nyc3.digitaloceanspaces.com/promo/';
const vid = $('jnVideo'); vid.src = VID + v + '.mp4'; vid.poster = VID + v + '.jpg';
const vid = $('jnVideo');
if (a.video === false) vid.hidden = true; else { vid.src = VID + v + '.mp4'; vid.poster = VID + v + '.jpg'; }
$('jnPoints').innerHTML = (a.points || []).map(t => '<li>' + t.replace(/[&<>]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c])) + '</li>').join('');
$('jnMock').hidden = true; $('jnAngle').hidden = false; $('jnPoints').hidden = false;
}
+10
View File
@@ -429,6 +429,16 @@
const lm = $('launchMark');
if (lm) { lm.hidden = !show; lm.innerHTML = show ? '<b>Launch ready: ' + done + ' of ' + items.length + '.</b> ' + (at && Date.now() < at ? 'Doors open ' + new Date(at).toLocaleString([], { weekday: 'short', hour: 'numeric', minute: '2-digit' }) + '. ' : '') + '<a href="/launch">Open the founding-week checklist</a>' : ''; }
} catch (e) {}
// people waiting for a sponsor in the holding tank (Marty, 2026-09-12): every Overview sees it
try {
const tw = d.tankWaiting, tn = $('tankNotice');
if (tn) {
tn.hidden = !(tw && tw.count);
if (tw && tw.count) tn.innerHTML = '<b>' + tw.count + (tw.count === 1 ? ' person is' : ' people are') + ' waiting for a sponsor in the holding tank</b> \u00b7 '
+ tw.names.map(esc).join(', ') + (tw.count > tw.names.length ? ' and more' : '') + '. <a href="#line">Adopt them from My line</a>'
+ (tw.eligible ? '.' : ' (you need your own $20 package first).');
}
} catch (e) {}
if (d.username) { // wall link rides the username
const wl = location.origin + '/wall/' + d.username;
$('wallLine').textContent = wl;