Pipeline: sponsor follow-up board (stages from the ledger, notes/follow-ups/tags, stage messages into chat), gated by pipelineMode with a coming-soon card until launch; training cards can wait on the same switch
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,7 @@ const updates = require('./updates');
|
||||
const audit = require('./audit'); // counter audit: views vs delivery logs, charges vs shows (Marty, 2026-09-15) // member update emails from Admin > Releases (Marty, 2026-09-14)
|
||||
const leaderboard = require('./leaderboard');
|
||||
const toolkit = require('./toolkit');
|
||||
const pipeline = require('./pipeline'); // sponsor follow-up board (coming soon until site setting pipelineMode = on) (Marty, 2026-09-15)
|
||||
const videomaker = require('./videomaker'); // Circuit tool: promo videos with the member's own end card (ffmpeg in the image) // badge-gated promo toolkit + AI Copy Engine (Surge and up) (Marty, 2026-09-14) // referral contest: /leaderboard, Overview card, weekly + monthly winners (Marty, 2026-09-14) // release notes + roadmap: /whats-new, Overview card, Admin > Releases (Marty, 2026-09-14) // blog -> Blotato -> X + Instagram on publish (Marty, 2026-09-13) // admin member card: search, drilldown, edits (Marty, 2026-09-13) // admin-written coaching articles, server-rendered public /blog with SEO metadata (Marty, 2026-09-12)
|
||||
const TRAFFIC_PAGES = new Set(['/', '/ledger', '/contract', '/shorts', '/plays', '/wallets', '/launch', '/partners', '/earning', '/blog', '/whats-new', '/leaderboard']);
|
||||
let tankWaitCache = null; // dashboard: who is waiting for a sponsor (refreshed every minute)
|
||||
@@ -369,6 +370,7 @@ async function boot() {
|
||||
setInterval(() => ads.scheduleSweep().catch(e => console.error('schedule sweep', e.message)), 5 * 60 * 1000); // scheduled starts/ends
|
||||
// follow-up email sequence: send whatever came due (every 10 min, first pass shortly after boot)
|
||||
coach.init({ dataDir: DATA_DIR, chain, accounts, mailer, ads, tank, lb: () => leaderboard, siteConfig });
|
||||
pipeline.init({ dataDir: DATA_DIR, accounts, coach });
|
||||
tank.init({ dataDir: DATA_DIR, chain, accounts, messages, mailer, site: 'https://instantadpay.com' });
|
||||
legacy.init({ dataDir: DATA_DIR });
|
||||
traffic.init({ dataDir: DATA_DIR });
|
||||
@@ -421,6 +423,8 @@ function siteConfig() {
|
||||
telegramAdminChatId: '', // private chat for admin alerts (sign-up guard bursts); falls back to ADMIN_EMAIL
|
||||
launchAt: '', // public launch moment, ISO 8601 with offset (e.g. 2026-09-18T19:00:00-05:00): countdown on /launch + dashboard mark
|
||||
aiCreditsPerGen: 10, aiFreeSurge: 20, aiFreeCircuit: 60, aiFreeNexus: 150, // AI Copy Engine: free generations per month by badge, then credits per generation
|
||||
pipelineMode: 'off', // Pipeline board: off = 'coming soon' card, preview = only the admin account sees the live board, on = everyone (Marty, 2026-09-15)
|
||||
pipelineEta: 'Sep 28', // what the coming-soon card and the roadmap promise
|
||||
memberWeeklyEmail: '0', // 1 = the weekly 'Your week on InstantAdPay' email goes to every active member, not only sponsors with a line
|
||||
noPayoutIds: '25', // positions kept for linkage only (compromised wallets): no buys signed from them, no new joins routed under them or their upline chain (Marty, 2026-09-14)
|
||||
leaderboardWeeklyPrize: '', leaderboardMonthlyPrize: '', leaderboardWeeklyCredits: '1000,500,250', leaderboardMonthlyCredits: '5000,2500,1000', leaderboardAnnounceGeneral: '1', // referral contest prizes (text shown on /leaderboard; credits granted to the winner automatically at rollover)
|
||||
@@ -1131,7 +1135,9 @@ const server = http.createServer(async (req, res) => {
|
||||
const tw = tankWaitCache.list;
|
||||
tankWaiting = { count: tw.length, names: tw.slice(0, 6).map(w => w.name), eligible: !!(await tank.eligibility(s.email)).ok };
|
||||
} catch (e) {}
|
||||
const sc0 = siteConfig();
|
||||
const out = { memberId, tankWaiting, email: s.email || (acct && acct.email) || null,
|
||||
pipeline: { live: pipeline.visible(sc0.pipelineMode, s.email || (acct && acct.email), ADMIN_EMAIL), mode: sc0.pipelineMode || 'off', eta: sc0.pipelineEta || '' },
|
||||
address: s.address || (acct && acct.address) || null,
|
||||
username: (acct && acct.username) || null,
|
||||
refCode: (acct && acct.code) || null, lineBannerUrl: (acct && acct.lineBannerUrl) || null, /* the launch checklist mark reads it (was 7 of 8 with a banner set, 2026-09-14) */ credits: 0, buyerCount: 0,
|
||||
@@ -1630,6 +1636,22 @@ const server = http.createServer(async (req, res) => {
|
||||
return json(res, 200, await coach.linkStats(s.email));
|
||||
}
|
||||
// -- prospects: the member's own follow-up list
|
||||
// -- Pipeline: the follow-up board (stages from the ledger and the site; notes, follow-ups, tags from the sponsor)
|
||||
if (p === '/api/my/pipeline' && req.method === 'GET') {
|
||||
const s = await auth.fromRequest(req);
|
||||
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
|
||||
const sc = siteConfig();
|
||||
if (!pipeline.visible(sc.pipelineMode, s.email, ADMIN_EMAIL)) return json(res, 200, { live: false, eta: sc.pipelineEta || '' });
|
||||
const b = await pipeline.board(s.email);
|
||||
return json(res, 200, Object.assign({ live: true }, b), { 'Cache-Control': 'no-store' });
|
||||
}
|
||||
if (p === '/api/my/pipeline/note' && req.method === 'POST') {
|
||||
const s = await auth.fromRequest(req);
|
||||
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
|
||||
if (!pipeline.visible(siteConfig().pipelineMode, s.email, ADMIN_EMAIL)) return json(res, 403, { error: 'The Pipeline is not open yet.' });
|
||||
const r = await pipeline.save(s.email, await readBody(req));
|
||||
return json(res, r.error ? 400 : 200, r);
|
||||
}
|
||||
if (p === '/api/my/prospects' && req.method === 'GET') {
|
||||
const s = await auth.fromRequest(req);
|
||||
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
|
||||
@@ -1880,6 +1902,8 @@ const server = http.createServer(async (req, res) => {
|
||||
if (!s) return json(res, 401, { error: 'Sign in first.' });
|
||||
let items = [];
|
||||
try { const j = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'training.json'), 'utf8')); if (Array.isArray(j)) items = j; } catch (e) {}
|
||||
// a card with feature:'pipeline' waits for the switch, so the video can be ready before the tab opens (Marty, 2026-09-15)
|
||||
const scT = siteConfig(); items = items.filter(it => !it.feature || (it.feature === 'pipeline' && pipeline.visible(scT.pipelineMode, s.email, ADMIN_EMAIL)));
|
||||
if (!items.length) items = [{ title: 'Getting started with InstantAdPay',
|
||||
desc: 'How the platform works, how every payout splits on-chain to real wallets, and how to build your line.',
|
||||
docUrl: 'https://instantadpay.com/' }];
|
||||
|
||||
Reference in New Issue
Block a user