/*
BBB Live Backend (Google Apps Script)
– Expose web endpoint: doGet?token=…&leader=
– onFormSubmit: process registrations, compute counts, create reward entries
Sheets used:
– “Recruits” columns: [A] id, [B] name, [C] phone, [D] leaderId/referrer, [E] timestamp
– “Rewards” columns: [A] rewardId, [B] leaderId, [C] milestoneCount, [D] timestamp, [E] issued
*/const SHEET_RECRUITS = ‘Recruits’;
const SHEET_REWARDS = ‘Rewards’;// Replace this with a token in Script Properties (see deploy steps)
function getToken() {
return PropertiesService.getScriptProperties().getProperty(‘API_TOKEN’) || ‘change-me’;
}// Utility: generate unique id
function uid() {
return Utilities.getUuid();
}/**
* doGet: return JSON for a leader or global summary
* params: token (required), leader (optional)
*/
function doGet(e) {
try {
const params = e.parameter || {};
const token = params.token;
if (!token || token !== getToken()) {
return ContentService.createTextOutput(JSON.stringify({ error: ‘Unauthorized’ }))
.setMimeType(ContentService.MimeType.JSON);
}const ss = SpreadsheetApp.getActive();
const recruitsSheet = ss.getSheetByName(SHEET_RECRUITS);
const rewardsSheet = ss.getSheetByName(SHEET_REWARDS);const recruits = recruitsSheet.getDataRange().getValues().slice(1); // skip header
const rewards = rewardsSheet.getDataRange().getValues().slice(1);// Global totals
const totalRecruits = recruits.length;// If leader param given, compute leader-specific stats (Tier1 + Tier2)
if (params.leader) {
const leaderId = params.leader;// Tier1: direct recruits whose leaderId == leaderId
const tier1List = recruits.filter(r => String(r[3]) === String(leaderId));
const tier1Count = tier1List.length;// Tier2: recruits referred by tier1 members (i.e., recruits whose leaderId is any of tier1 member ids)
// Assumption: each recruit’s own id is in column A; but tier1List rows are raw rows (id at index 0)
const tier1Ids = tier1List.map(r => String(r[0]));
const tier2List = recruits.filter(r => tier1Ids.indexOf(String(r[3])) !== -1);
const tier2Count = tier2List.length;// Rewards for this leader
const leaderRewards = rewards
.filter(r => String(r[1]) === String(leaderId))
.map(r => ({ rewardId: r[0], milestone: r[2], timestamp: r[3], issued: r[4] }));const payload = {
leader: leaderId,
tier1Count: tier1Count,
tier2Count: tier2Count,
tier1Ids: tier1Ids.slice(0,50), // sample
rewards: leaderRewards,
totalRecruits: totalRecruits,
timestamp: new Date().toISOString()
};return ContentService.createTextOutput(JSON.stringify(payload)).setMimeType(ContentService.MimeType.JSON);
}// If no leader param: return global summary
const summary = {
totalRecruits: totalRecruits,
totalLeaders: Array.from(new Set(recruits.map(r => String(r[3])))).length,
totalRewardsIssued: rewards.length,
timestamp: new Date().toISOString()
};return ContentService.createTextOutput(JSON.stringify(summary)).setMimeType(ContentService.MimeType.JSON);} catch (err) {
return ContentService.createTextOutput(JSON.stringify({ error: err.message })).setMimeType(ContentService.MimeType.JSON);
}
}/**
* onFormSubmit: bound to Google Form submission trigger
* Expects form fields order: Name, Phone, LeaderId (referrer)
*/
function onFormSubmit(e) {
try {
const ss = SpreadsheetApp.getActive();
const recruitsSheet = ss.getSheetByName(SHEET_RECRUITS);
const rewardsSheet = ss.getSheetByName(SHEET_REWARDS);// If using simple form -> e.values contains all answers
const values = e.values; // array including timestamp as first item usually
// Map fields depending on your form layout. Here’s an example mapping:
// values example: [Timestamp, Name, Phone, LeaderId]
const name = values[1] || ‘Unknown’;
const phone = values[2] || ”;
const leaderId = values[3] || ‘none’;// Create new recruit entry
const newId = uid();
const ts = new Date();
recruitsSheet.appendRow([newId, name, phone, leaderId, ts]);// Update leader milestone: count how many recruits the leader has
const recruitsData = recruitsSheet.getDataRange().getValues().slice(1);
const leaderRecruits = recruitsData.filter(r => String(r[3]) === String(leaderId));
const leaderCount = leaderRecruits.length;// Milestone rule: every time leader reaches multiples of 5 recruits -> issue reward if not already issued for that milestone
if (leaderCount >= 5) {
const milestone = Math.floor(leaderCount / 5) * 5; // e.g., 5,10,15…
// Check if reward already issued for this milestone
const rewardsData = rewardsSheet.getDataRange().getValues().slice(1);
const already = rewardsData.some(r => String(r[1]) === String(leaderId) && Number(r[2]) === milestone);
if (!already) {
const rewardId = uid();
rewardsSheet.appendRow([rewardId, leaderId, milestone, ts, ‘Yes’]);
// Optionally: send email or call webhook to notify/claim reward
// sendRewardNotification(leaderId, milestone);
}
}} catch (err) {
Logger.log(‘onFormSubmit error: ‘ + err.message);
}
}/**
* Optional: sendRewardNotification – example function to call an external webhook or email
*/
function sendRewardNotification(leaderId, milestone) {
// Example: send an email (or integrate Twilio/WhatsApp API)
const ss = SpreadsheetApp.getActive();
const recruitsSheet = ss.getSheetByName(SHEET_RECRUITS);
// Find leader contact
const recruits = recruitsSheet.getDataRange().getValues().slice(1);
const leaderRow = recruits.find(r => String(r[0]) === String(leaderId));
const leaderEmail = leaderRow ? leaderRow[2] : null; // modify if email exists
if (leaderEmail) {
MailApp.sendEmail({
to: leaderEmail,
subject: ‘BBB Reward Unlocked!’,
body: `Congrats! You reached ${milestone} recruits. Claim your reward from the dashboard.`
});
}
}