#!/usr/bin/env python3 """ Reset bounced emails back into the send pipeline. Now that DKIM/DMARC is fixed, these should deliver. Usage: python3 recoup_bounces.py email1@example.com email2@example.com ... or: python3 recoup_bounces.py (reads from bounces.txt, one per line) """ import csv, json, sys from pathlib import Path BASE = Path(__file__).parent CONTACTS = BASE / 'contacts' / 'master.csv' LOG = BASE / 'tracking' / 'log.json' BOUNCES = BASE / 'tracking' / 'bounces.txt' def recoup(emails): emails = [e.strip().lower() for e in emails if e.strip()] print(f'Recouping {len(emails)} address(es)...\n') # 1. Un-skip in CSV rows, found = [], [] with open(CONTACTS, newline='') as f: reader = csv.DictReader(f) fieldnames = reader.fieldnames for row in reader: if row['email'].strip().lower() in emails: row['skip'] = '' row['notes'] = row.get('notes','').replace('[BOUNCED]','').replace('[BOUNCED 554 5.1.8]','').strip() found.append(row['email'].strip().lower()) print(f' CSV ✓ un-skipped: {row["email"]}') rows.append(row) with open(CONTACTS, 'w', newline='') as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows) # 2. Reset tracking log with open(LOG) as f: log = json.load(f) for email in emails: if email in log: log[email]['touch_1_sent'] = False log[email].pop('touch_1_date', None) print(f' LOG ✓ reset tracking: {email}') else: print(f' LOG — not in log (never sent or already clean): {email}') with open(LOG, 'w') as f: json.dump(log, f, indent=2) not_found = [e for e in emails if e not in found] if not_found: print(f'\n ✗ Not found in contacts CSV (check spelling):') for e in not_found: print(f' {e}') print(f'\nDone. {len(found)} address(es) back in the pipeline.') print('They will be picked up on the next send run.') if __name__ == '__main__': if len(sys.argv) > 1: recoup(sys.argv[1:]) elif BOUNCES.exists(): with open(BOUNCES) as f: recoup([line.strip() for line in f if line.strip()]) else: print('Usage: python3 recoup_bounces.py email1 email2 ...') print(' or: paste bounced addresses into tracking/bounces.txt and run with no args')