Fix referral attribution (username joins) + broadcast rate-limit regression

- Dashboard "Your line" roster now matches referrals joined under the member's
  USERNAME link (invite link is /join/<username>), not just code + member id —
  username joins were invisible.
- lastBroadcastAt now counts only kind='broadcast'. Chat rides the same table,
  so chatting had been tripping the once-a-day broadcast limit ("try again in
  24h" on the first broadcast).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-07 06:38:27 -05:00
parent 7caff0eea2
commit 41d3293486
4 changed files with 11 additions and 4 deletions
+1 -1
View File
@@ -2,4 +2,4 @@
# https://curl.se/docs/http-cookies.html # https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk. # This file was generated by libcurl! Edit at your own risk.
#HttpOnly_127.0.0.1 FALSE / FALSE 1788867258 iap.sid ef5bf189318d5c20a0d3c252de07f6c5d3b15d05c4749e71344c5e279ff8f35f #HttpOnly_127.0.0.1 FALSE / FALSE 1788867454 iap.sid 9c0338979f5ee57c2d40f88328c0563065aab070b613c3d7665e042e79ac0478
+2 -2
View File
@@ -19,11 +19,11 @@ const DAY = 86400000;
async function lastBroadcastAt(fromEmail) { async function lastBroadcastAt(fromEmail) {
const e = String(fromEmail || '').toLowerCase(); const e = String(fromEmail || '').toLowerCase();
if (db.enabled()) { if (db.enabled()) {
const r = await db.q('SELECT MAX(sent) m FROM sponsor_messages WHERE from_email=?', [e]); const r = await db.q("SELECT MAX(sent) m FROM sponsor_messages WHERE from_email=? AND kind='broadcast'", [e]);
return r.length && r[0].m ? Number(r[0].m) : 0; return r.length && r[0].m ? Number(r[0].m) : 0;
} }
if (!J.db) J.load(); if (!J.db) J.load();
const mine = J.db.items.filter(i => i.fromEmail === e).map(i => i.sent); const mine = J.db.items.filter(i => i.fromEmail === e && (i.kind || 'broadcast') === 'broadcast').map(i => i.sent);
return mine.length ? Math.max(...mine) : 0; return mine.length ? Math.max(...mine) : 0;
} }
// deliver one message to many recipients (already-resolved emails). Returns count. // deliver one message to many recipients (already-resolved emails). Returns count.
+3 -1
View File
@@ -513,9 +513,11 @@ const server = http.createServer(async (req, res) => {
out.earnedWei = earned.toString(); out.earnedWei = earned.toString();
out.earnCount = n; out.earnCount = n;
} }
// who joined through this member (code and, once on-chain, numeric id) // who joined through this member: their invite link uses username when set,
// else code, and the numeric id once on-chain — match all three
const refs = []; const refs = [];
if (acct && acct.code) refs.push(acct.code); if (acct && acct.code) refs.push(acct.code);
if (acct && acct.username) refs.push(acct.username);
if (memberId) refs.push(String(memberId)); if (memberId) refs.push(String(memberId));
const joined = await accounts.listByReferrer(refs); const joined = await accounts.listByReferrer(refs);
out.referrals = joined.map(r => ({ out.referrals = joined.map(r => ({
+5
View File
@@ -0,0 +1,5 @@
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_127.0.0.1 FALSE / FALSE 1788867454 iap.sid 422fe0acfd5b80732cbe04445cca71d0b01c216439aefade3aa928110e0419c2