Pool spent: board notice + closed missions, starts/claims refused until midnight Central; no-wallet onboarding panel; landing: Before your first hunt steps + teaser player above the fold
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ async function notify(kind, p) {
|
||||
if (kind === 'low') return telegram('⚠️ <b>PolHunter faucet is low</b>: ' + fmt(p.balance) + ' POL left in ' + p.address + ' (alert threshold ' + fmt(p.threshold) + '). Top up from Receiver B.');
|
||||
if (kind === 'failed') return telegram('❌ <b>PolHunter</b> · drip to #' + p.memberId + ' failed: ' + p.error);
|
||||
}
|
||||
// Marty's rule (2026-09-19): when the day's pool is spent, say so; claims reopen at midnight Central
|
||||
const SPENT_MSG = 'Today\u2019s POL pool is spent. No more claims today. Claims reopen at midnight Central.';
|
||||
function explorer() { return Number(process.env.HUNT_CHAIN_ID) === 80002 ? 'https://amoy.polygonscan.com' : 'https://polygonscan.com'; }
|
||||
|
||||
// ---- helpers -------------------------------------------------------------------------------
|
||||
@@ -119,12 +121,13 @@ const server = http.createServer(async (req, res) => {
|
||||
if (p === '/api/my/board') {
|
||||
const done = new Set(rewards.mine(me.memberId).map(x => x.missionId));
|
||||
const wdone = me.wallet ? new Set(store.read('payouts', []).filter(x => x.wallet && x.wallet.toLowerCase() === me.wallet && x.status !== 'failed').map(x => x.missionId)) : new Set();
|
||||
return json(res, 200, { me: { memberId: me.memberId, username: me.username, wallet: me.wallet }, missions: missions.forMember(me.memberId).map(m => Object.assign(pubMission(m), { done: done.has(m.id) || wdone.has(m.id) })), drips: rewards.mine(me.memberId).slice(0, 20), faucet: { on: faucetOn } });
|
||||
return json(res, 200, { me: { memberId: me.memberId, username: me.username, wallet: me.wallet }, missions: missions.forMember(me.memberId).map(m => Object.assign(pubMission(m), { done: done.has(m.id) || wdone.has(m.id) })), drips: rewards.mine(me.memberId).slice(0, 20), faucet: { on: faucetOn }, pool: rewards.pool() });
|
||||
}
|
||||
if (p === '/api/my/start' && req.method === 'POST') {
|
||||
const b = await readBody(req); const m = missions.get(String(b.missionId || '')); if (!m || !m.active) return json(res, 404, { error: 'That mission is not open.' });
|
||||
if (!me.wallet) return json(res, 400, { error: 'Link a wallet on InstantAdPay first so the drip has somewhere to land, then open PolHunter again.' });
|
||||
if (rewards.completed(me.memberId, m.id, me.wallet)) return json(res, 400, { error: 'You already completed this one.' });
|
||||
{ const pool = rewards.pool(); if (pool.spent) return json(res, 400, { error: SPENT_MSG, spent: true, resetsAt: pool.resetsAt }); }
|
||||
if (limited('start:' + me.memberId, 20, 3600000)) return json(res, 429, { error: 'Easy. Twenty starts an hour is plenty.' });
|
||||
const t = missions.issue(me.memberId, m.id);
|
||||
// a mission URL may place the token itself with {token} (a Telegram Mini App takes it in
|
||||
@@ -139,6 +142,7 @@ const server = http.createServer(async (req, res) => {
|
||||
const c = missions.check(String(b.token || ''), me.memberId, b.code); if (c.error) return json(res, 400, { error: c.error });
|
||||
const m = missions.get(c.rec.missionId); if (!m) return json(res, 404, { error: 'That mission is gone.' });
|
||||
if (rewards.completed(me.memberId, m.id, me.wallet)) return json(res, 400, { error: 'You already completed this one.' });
|
||||
{ const pool = rewards.pool(); if (pool.spent) return json(res, 400, { error: SPENT_MSG, spent: true, resetsAt: pool.resetsAt }); }
|
||||
const g = rewards.grant(me, m); if (g.error) return json(res, 400, g);
|
||||
return json(res, 200, { ok: true, pol: g.rec.pol, queued: g.queued, message: g.queued ? 'Found it. Today’s POL is spoken for, so yours is queued and pays out next.' : 'Found it. ' + fmt(g.rec.pol) + ' POL is on its way to your wallet.' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user