Add sponsor contact emails and Telegram team-build activity alerts
Sponsors can carry an optional contact email (Add Sponsor form, ✉ inline edit in the queue, shown under the name). New config teamRootId: any NEW on-chain event at or below that member ID — registration, upgrade, or payout seen by the live tail — posts to the Telegram group topic, with the tx link and the sponsor's contact email when one is on file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,8 @@ const STATE_FILE = path.join(DATA_DIR, 'chain-index.json');
|
||||
|
||||
let state = null;
|
||||
let busy = false;
|
||||
let onEvent = null; // callback(evt) for NEW events seen by the live tail (never snapshot history)
|
||||
function emit(evt) { if (onEvent) { try { onEvent(evt); } catch (e) { console.error('chain onEvent error', e.message); } } }
|
||||
|
||||
function loadState() {
|
||||
try { state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); } catch (e) { state = null; }
|
||||
@@ -196,6 +198,13 @@ async function processRange(fromBlock, toBlock) {
|
||||
if (!state.members[id]) {
|
||||
state.members[id] = { account: topicAddr(l.topics[2]), referrerId: topicInt(l.topics[3]), tier: wInt(l.data, 0), joinedAt: ts };
|
||||
newIds.push(id);
|
||||
emit({ type: 'registered', id, referrerId: topicInt(l.topics[3]), tierName: tierName(wInt(l.data, 0)), tx, ts });
|
||||
}
|
||||
} else if (l.topics[0] === T_UPGRADED) {
|
||||
const id = topicInt(l.topics[1]);
|
||||
if (!state.members[id] || (state.members[id].level || 0) < wInt(l.data, 0)) {
|
||||
if (state.members[id]) state.members[id].level = wInt(l.data, 0);
|
||||
emit({ type: 'upgraded', id, newLevel: wInt(l.data, 0), levelName: levelName(wInt(l.data, 0)), tx, ts });
|
||||
}
|
||||
} else if (l.topics[0] === T_REFERRAL || l.topics[0] === T_UPLINE) {
|
||||
if (state.payouts.some(p => p.key === key)) continue;
|
||||
@@ -210,10 +219,13 @@ async function processRange(fromBlock, toBlock) {
|
||||
if (txUpgrades[tx]) p.upgrade = txUpgrades[tx];
|
||||
p.passed = passedOver(p.fromId, p.toId);
|
||||
}
|
||||
// upgrade a snapshot-sourced twin in place, else append
|
||||
// upgrade a snapshot-sourced twin in place, else append (twin = already known from snapshot, so not a NEW event)
|
||||
const twinIdx = state.payouts.findIndex(x => !x.tx && samePayout(x, p));
|
||||
if (twinIdx >= 0) state.payouts[twinIdx] = p;
|
||||
else { state.payouts.push(p); state.totals.count++; state.totals.pol = +(state.totals.pol + p.pol).toFixed(6); }
|
||||
else {
|
||||
state.payouts.push(p); state.totals.count++; state.totals.pol = +(state.totals.pol + p.pol).toFixed(6);
|
||||
emit({ type: 'payout', kind: p.kind, toId: p.toId, fromId: p.fromId, pol: p.pol, levelName: levelName(p.level), upgrade: p.upgrade, tx, ts });
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const id of newIds) {
|
||||
@@ -255,12 +267,27 @@ async function tick() {
|
||||
} finally { busy = false; }
|
||||
}
|
||||
|
||||
function startIndexer() {
|
||||
function startIndexer(eventCb) {
|
||||
onEvent = eventCb || null;
|
||||
loadState();
|
||||
tick();
|
||||
setInterval(tick, POLL_MS).unref();
|
||||
}
|
||||
|
||||
// true if `id` sits at or below `rootId` in the matrix (walks the upline chain)
|
||||
function isInTeam(id, rootId) {
|
||||
if (!state || !id || !rootId) return false;
|
||||
if (id === rootId) return true;
|
||||
const seen = new Set([id]);
|
||||
let cur = state.members[id] && state.members[id].uplineId;
|
||||
for (let i = 0; i < 60 && cur && !seen.has(cur); i++) {
|
||||
if (cur === rootId) return true;
|
||||
seen.add(cur);
|
||||
cur = state.members[cur] && state.members[cur].uplineId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPayoutsPublic() {
|
||||
const recent = state ? state.payouts.slice(-40).reverse() : [];
|
||||
return {
|
||||
@@ -341,4 +368,4 @@ function getMatrixTree() {
|
||||
return { ready: true, snapshotAt: state.snapshotAt, memberCount: Object.keys(state.members).length, root, unplaced: unplaced.length ? unplaced : undefined };
|
||||
}
|
||||
|
||||
module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, getMatrixTree, CONTRACT };
|
||||
module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, getMatrixTree, isInTeam, CONTRACT };
|
||||
|
||||
Reference in New Issue
Block a user