← back to Pitch Guard
Fix recent-followup detection — broad regex for all followup templates + body fallback when snippet misses
0bd7aef388e86bbd7ed29e1784440ea33fc6c211 · 2026-05-19 09:28:07 -0700 · Steve
Files touched
Diff
commit 0bd7aef388e86bbd7ed29e1784440ea33fc6c211
Author: Steve <steve@designerwallcoverings.com>
Date: Tue May 19 09:28:07 2026 -0700
Fix recent-followup detection — broad regex for all followup templates + body fallback when snippet misses
---
server.js | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/server.js b/server.js
index c88c1fc..0088a3f 100644
--- a/server.js
+++ b/server.js
@@ -185,18 +185,30 @@ app.get('/api/contact', async (req, res) => {
if (threads.length > 1) level = Math.max(level, 1);
const risk = level >= 3 ? 'high' : level >= 2 ? 'elevated' : level >= 1 ? 'caution' : 'clear';
- // Did we already send this contact a standard followup in the last 7 days?
- // (in:sent only — excludes the unsent draft so a queued draft doesn't count)
+ // Did we already send this contact a followup recently? The followup bot
+ // uses several phrasings, and Gmail snippets can show quoted text instead
+ // of the opening line — so match broadly and body-check when the snippet misses.
+ // (in:sent only — an unsent draft doesn't count.)
let recentFollowup = null;
try {
- const FOLLOWUP_RE = /following up on the (quote|pricing)|wanted to check in and see if you/i;
- const fu = await george('/api/search', { query: { account: ACCOUNT, q: `in:sent to:${email} newer_than:7d`, maxResults: 10 } });
- for (const m of (fu.messages || [])) {
- if (m.error) continue;
- if (!FOLLOWUP_RE.test(`${m.subject || ''} ${m.snippet || ''}`)) continue;
- const ts = Date.parse(m.date) || 0;
- if (!recentFollowup || ts > recentFollowup.ts) {
+ const FU_RE = /follow[\s-]?up|following up|check(ing)? in|circl(e|ing) back|haven'?t heard|gentle (nudge|reminder)|chase up|did(n'?t| not) (you )?see/i;
+ const fu = await george('/api/search', { query: { account: ACCOUNT, q: `in:sent to:${email} newer_than:21d`, maxResults: 20 } });
+ const cand = (fu.messages || []).filter(m => !m.error)
+ .sort((a, b) => (Date.parse(b.date) || 0) - (Date.parse(a.date) || 0));
+ let bodyChecks = 0;
+ for (const m of cand) {
+ let isFu = FU_RE.test(`${m.subject || ''} ${m.snippet || ''}`);
+ if (!isFu && bodyChecks < 6) { // snippet missed it — confirm via the full body
+ bodyChecks++;
+ try {
+ const full = await george('/api/messages/' + encodeURIComponent(m.id), { query: { account: ACCOUNT } });
+ isFu = FU_RE.test(full.body || '');
+ } catch (e) {}
+ }
+ if (isFu) {
+ const ts = Date.parse(m.date) || 0;
recentFollowup = { ts, date: m.date, daysAgo: Math.max(0, Math.floor((Date.now() - ts) / 86400000)) };
+ break; // newest-first → first match is the most recent
}
}
} catch (e) { /* non-fatal — followup check is advisory */ }
← e7305dd Add Delete draft button to the Pitch panel footer
·
back to Pitch Guard
·
Add 5-day followup candidate scanner (dry-run) — Followup Ca 164394a →