← back to Butlr
butlr: sequential Ventura-Blvd hours sweep dialer (one call at a time, up to 40)
11722b0becaef9895a485ee84a6c81dbabb3455a · 2026-05-26 11:01:00 -0700 · SteveStudio2
Files touched
A scripts/ventura-hours-sweep.js
Diff
commit 11722b0becaef9895a485ee84a6c81dbabb3455a
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Tue May 26 11:01:00 2026 -0700
butlr: sequential Ventura-Blvd hours sweep dialer (one call at a time, up to 40)
---
scripts/ventura-hours-sweep.js | 86 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/scripts/ventura-hours-sweep.js b/scripts/ventura-hours-sweep.js
new file mode 100644
index 0000000..0bd2fad
--- /dev/null
+++ b/scripts/ventura-hours-sweep.js
@@ -0,0 +1,86 @@
+// Sequential Ventura-Blvd "what are your hours" sweep.
+// Places up to 40 calls ONE AT A TIME — each call must reach a terminal
+// status (done/failed/canceled) before the next is placed. Live audio
+// broadcasts on live.agentabrams.com as each call runs.
+//
+// Run on the Kamatera butlr host:
+// cd /root/public-projects/butlr && nohup node scripts/ventura-hours-sweep.js > /tmp/ventura_sweep.log 2>&1 &
+//
+// Stop it any time: pkill -f ventura-hours-sweep
+
+require('dotenv').config({ path: '/root/public-projects/butlr/.env' });
+const fs = require('fs');
+const data = require('../lib/data');
+const twilio = require('../lib/twilio');
+
+const USER_ID = 'rcA9SnnO';
+const MAX_CALLS = 40;
+const PER_CALL_TIMEOUT_MS = 130000; // give a call up to ~2min to finish
+const GAP_MS = 4000; // breath between calls
+const TERMINAL = new Set(['done', 'failed', 'canceled', 'cancelled']);
+const GOAL = 'Ask: what are your hours today? Wait for their answer, then thank them warmly and end the call. Ask nothing else; keep it under two sentences.';
+
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+function log(...a) { console.log(new Date().toISOString(), ...a); }
+
+function shortLocality(addr) {
+ // OSM display_name: "Name, number, street, neighborhood, city, county, state, zip, country"
+ const parts = String(addr || '').split(',').map((s) => s.trim()).filter(Boolean);
+ // drop the leading name part, keep number+street + city-ish
+ const tail = parts.slice(1, 4).join(', ');
+ return tail.slice(0, 60);
+}
+
+async function waitTerminal(id) {
+ const start = Date.now();
+ while (Date.now() - start < PER_CALL_TIMEOUT_MS) {
+ const c = data.getCallUnscoped(id);
+ if (c && TERMINAL.has(c.status)) return c.status;
+ await sleep(3000);
+ }
+ return 'timeout';
+}
+
+(async () => {
+ let list;
+ try { list = JSON.parse(fs.readFileSync('/tmp/vall.json', 'utf8')); }
+ catch (e) { log('FATAL: cannot read /tmp/vall.json —', e.message); process.exit(1); }
+ list = list.slice(0, MAX_CALLS);
+ log(`=== Ventura hours sweep: ${list.length} businesses, sequential ===`);
+
+ let placed = 0, skipped = 0;
+ for (let i = 0; i < list.length; i++) {
+ const b = list[i];
+ const loc = shortLocality(b.addr);
+ const row = {
+ business_name: (b.name + (loc ? ' — ' + loc : '')).slice(0, 120),
+ business_phone: b.e164,
+ goal: GOAL,
+ callback_phone: '+16067130489',
+ callback_name: 'Steve Adams',
+ category: 'other',
+ category_label: 'Ventura hours sweep',
+ max_hold_minutes: 2,
+ max_spend_cents: 50,
+ consent_recording: 'on',
+ consent_terms: 'on',
+ notes: 'ventura 40 hours sweep ' + (i + 1) + '/' + list.length,
+ allow_repeat: true,
+ };
+ const r = data.addCall(row, USER_ID);
+ if (!r.ok) { skipped++; log(`SKIP ${i + 1}/${list.length} ${b.e164} ${b.name} — ${(r.errors || []).join('; ')}`); continue; }
+ try {
+ await twilio.dialCall(r.call);
+ placed++;
+ log(`PLACED ${i + 1}/${list.length} id=${r.call.id} ${b.e164} | ${b.name}`);
+ } catch (e) {
+ log(`DIAL-ERR ${i + 1}/${list.length} id=${r.call.id} — ${e.message}`);
+ continue;
+ }
+ const status = await waitTerminal(r.call.id);
+ log(` └─ ${r.call.id} ended: ${status}`);
+ await sleep(GAP_MS);
+ }
+ log(`=== sweep done: placed=${placed} skipped=${skipped} ===`);
+ process.exit(0);
+})();
← 4997af2 live page: harden audio — auto-listen on appear, resume ctx
·
back to Butlr
·
live page: show recording length as m:ss badge + preload=met c415746 →