PolHunter v0.2: the hunt engine
Hand-off sign-in from InstantAdPay (lib/sso.js: HMAC token, five minutes, single use; no sign-up, no mailer), missions with per-member per-visit proof codes (lib/missions.js: the embed is answered only from the mission's own origin, only after the dwell; hiding place rotates by day and member), weighted-low rewards with a daily cap that queues rather than refuses (lib/rewards.js), the faucet sender on ethers with a low-balance alert (lib/faucet.js), the one-line embed for the sites (public/embed.js), the hunter board, the public ledger, an admin page, and the site's own look. Telegram is behind the OUTBOUND gate like everything else. 13-check end-to-end test in test/run.js. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/* PolHunter embed. One line on a mission site:
|
||||
<script src="https://polhunter.com/embed.js" data-slots="3" async></script>
|
||||
plus, where a code may appear, elements with data-ph-slot="0" ... data-ph-slot="N-1".
|
||||
|
||||
When a hunter arrives with ?ph=<token> (their own, from their PolHunter board) the token is kept
|
||||
in sessionStorage for this tab, and after the mission's dwell the code is fetched from
|
||||
polhunter.com and rendered in ONE of the slots (which one is picked per hunter per day). The
|
||||
request carries this page's Origin; polhunter.com answers only for the mission's own host and
|
||||
only after the dwell. Anyone else loading this page sees nothing at all. */
|
||||
(function () {
|
||||
var API = 'https://polhunter.com';
|
||||
try {
|
||||
var q = new URLSearchParams(location.search), t = q.get('ph');
|
||||
if (t) { sessionStorage.setItem('ph.token', t); q.delete('ph'); history.replaceState(null, '', location.pathname + (q.toString() ? '?' + q : '') + location.hash); }
|
||||
t = sessionStorage.getItem('ph.token'); if (!t) return;
|
||||
} catch (e) { return; }
|
||||
// slots: explicit [data-ph-slot="i"] elements, or the i-th match of data-selector (e.g. ".video-card"
|
||||
// on a training page, ".card" on a programs page), else a fixed badge in the corner
|
||||
var me = document.currentScript, slots = Number((me && me.getAttribute('data-slots')) || 1) || 1, sel = me && me.getAttribute('data-selector');
|
||||
function slotEl(i) {
|
||||
var el = document.querySelector('[data-ph-slot="' + i + '"]'); if (el) return el;
|
||||
if (sel) { var all = document.querySelectorAll(sel); if (all.length) { var host = all[i % all.length]; var d0 = document.createElement('div'); d0.style.cssText = 'margin:10px 0'; host.appendChild(d0); return d0; } }
|
||||
var d = document.createElement('div'); d.setAttribute('data-ph-slot', String(i)); d.style.cssText = 'position:fixed;right:16px;bottom:16px;z-index:99999'; document.body.appendChild(d); return d;
|
||||
}
|
||||
function render(code, slot) {
|
||||
var el = slotEl(slot); el.innerHTML = '';
|
||||
var box = document.createElement('div');
|
||||
box.setAttribute('role', 'note');
|
||||
box.style.cssText = 'display:inline-flex;align-items:center;gap:10px;padding:10px 14px;border-radius:14px;background:linear-gradient(135deg,#1a0f3d,#2b1a63);color:#f1eefb;font:600 14px/1.2 system-ui,-apple-system,"Segoe UI",sans-serif;box-shadow:0 8px 30px rgba(130,71,229,.45),0 0 0 1px rgba(255,255,255,.08);letter-spacing:.02em';
|
||||
box.innerHTML = '<span style="width:22px;height:22px;border-radius:50%;background:radial-gradient(circle at 35% 35%,#c9a4ff,#8247e5 60%,#4b2a8c);box-shadow:0 0 14px rgba(130,71,229,.9);flex:none"></span><span>PolHunter code <b style="font-family:ui-monospace,Menlo,monospace;font-size:16px;letter-spacing:.14em;color:#f3be43">' + code + '</b></span>';
|
||||
el.appendChild(box);
|
||||
}
|
||||
var tries = 0;
|
||||
function ask() {
|
||||
tries++; if (tries > 40) return;
|
||||
fetch(API + '/api/embed/code?t=' + encodeURIComponent(t), { mode: 'cors', credentials: 'omit' }).then(function (r) { return r.json(); }).then(function (r) {
|
||||
if (r && r.code) { render(r.code, Number(r.slot) % slots); try { sessionStorage.removeItem('ph.token'); } catch (e) {} return; }
|
||||
if (r && r.wait) { setTimeout(ask, Math.min(r.wait, 10) * 1000); return; }
|
||||
// expired / gone / wrong origin: say nothing
|
||||
}).catch(function () { setTimeout(ask, 8000); });
|
||||
}
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ask); else ask();
|
||||
})();
|
||||
Reference in New Issue
Block a user