File size: 9,804 Bytes
d712cef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | import sqlite3
import json
import os
import time
from datetime import datetime, timedelta
from EmailGenerator import generate_cold_email
# --- CONSTANTS & PATHS ---
BASE_PATH = os.environ.get('WORKSPACE_ROOT', '.')
# Source: companies imported from .xlsx / .numbers files
DB_EXCEL_DATA = os.path.join(BASE_PATH, 'Database/outreach_data/excel_data.db')
# Destination: review queue shown in the frontend dashboard
DB_TRACKING = os.path.join(BASE_PATH, 'Database/EmailsUnderReview/emailsUnderReview.db')
# Ensure both directories exist before any DB connection is attempted
os.makedirs(os.path.dirname(DB_EXCEL_DATA), exist_ok=True)
os.makedirs(os.path.dirname(DB_TRACKING), exist_ok=True)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DATABASE SETUP
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def init_tracking_db():
"""Create the tracking table if it doesn't exist yet."""
os.makedirs(os.path.dirname(DB_TRACKING), exist_ok=True)
with sqlite3.connect(DB_TRACKING) as conn:
conn.execute('''
CREATE TABLE IF NOT EXISTS tracking (
id INTEGER PRIMARY KEY AUTOINCREMENT,
body_json TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
followup_date DATETIME,
status TEXT DEFAULT 'under review',
-- core identifiers
company_name TEXT,
company_email TEXT,
-- rich outreach fields (carried from excel_data.db)
website TEXT,
address TEXT,
total_shipments TEXT,
top_suppliers TEXT,
hs_codes TEXT,
company_description TEXT,
key_executives TEXT,
deep_research_pdf TEXT,
-- email meta
generated_subject TEXT,
date_added DATETIME,
Unique_application_id TEXT
)
''')
conn.commit()
def dict_factory(cursor, row):
return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DUPLICATE CHECK
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def already_tracked(company_name: str, company_email: str) -> bool:
"""Return True if this company is already in the tracking DB."""
with sqlite3.connect(DB_TRACKING) as conn:
row = conn.execute(
"SELECT id FROM tracking WHERE company_name = ? AND company_email = ?",
(company_name, company_email)
).fetchone()
return row is not None
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# CORE AGENT LOGIC
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def process_next_company():
"""
Picks the next unprocessed company from outreach_companies,
generates a cold email, saves everything to emailsUnderReview.db,
then marks the source row as 'processed'.
"""
# 1. Fetch next pending company
try:
with sqlite3.connect(DB_EXCEL_DATA) as conn:
conn.row_factory = dict_factory
company = conn.execute(
"SELECT * FROM outreach_companies WHERE status = 'under_review' LIMIT 1"
).fetchone()
except sqlite3.OperationalError:
# Table doesn't exist yet β Excel_Processor.py hasn't been run
print(f"[{datetime.now():%H:%M:%S}] Source table not initialised yet. Run Excel_Processor.py first.")
return False
if not company:
print(f"[{datetime.now():%H:%M:%S}] No pending companies found. Sleeping...")
return False
company_name = company.get("company_name", "Unknown")
company_email = company.get("email", "not updated")
# 2. Duplicate guard
if already_tracked(company_name, company_email):
print(f"[{datetime.now():%H:%M:%S}] Duplicate: '{company_name}' already tracked β marking processed.")
with sqlite3.connect(DB_EXCEL_DATA) as conn:
conn.execute(
"UPDATE outreach_companies SET status = 'processed' WHERE id = ?",
(company["id"],)
)
return True
# 3. Generate cold email
print(f"[{datetime.now():%H:%M:%S}] Generating email for '{company_name}'...")
try:
raw_email = generate_cold_email(company) # returns "Subject: ...\n\n<body>"
except Exception as e:
print(f" β Email generation failed: {e}")
return False
# Split subject from body
subject, body = "", raw_email
if raw_email.startswith("Subject:"):
lines = raw_email.split("\n\n", 1)
subject = lines[0].replace("Subject:", "").strip()
body = lines[1].strip() if len(lines) > 1 else raw_email
# 4. Build the JSON blob stored in body_json (keeps backward compat with the frontend)
# 5. Generate a persistent Unique Application ID immediately
import random, string
unique_app_id = ''.join(random.choices(string.digits, k=20))
final_payload = {
"body": {
"generated_content": body,
"subject": subject,
"Unique_application_id": unique_app_id,
"outreach_data": {
"company_name": company_name,
"company_email": company_email,
"website": company.get("website", ""),
"address": company.get("address", ""),
"total_shipments": company.get("total_shipments", ""),
"top_suppliers": company.get("top_suppliers", ""),
"hs_codes": company.get("hs_codes", ""),
"company_description": company.get("company_description", ""),
"key_executives": company.get("key_executives", ""),
"deep_research_pdf": company.get("deep_research_pdf", ""),
}
}
}
body_json_str = json.dumps(final_payload)
# 5. Save to tracking DB
now = datetime.now()
followup_dt = now + timedelta(days=4)
with sqlite3.connect(DB_TRACKING) as conn:
conn.execute('''
INSERT INTO tracking (
body_json, timestamp, followup_date, status,
company_name, company_email,
website, address, total_shipments, top_suppliers,
hs_codes, company_description, key_executives, deep_research_pdf,
generated_subject, date_added, Unique_application_id
) VALUES (
?, ?, ?, ?,
?, ?,
?, ?, ?, ?,
?, ?, ?, ?,
?, ?, ?
)
''', (
body_json_str,
now.strftime("%Y-%m-%d %H:%M:%S"),
followup_dt.strftime("%Y-%m-%d %H:%M:%S"),
'under review',
company_name,
company_email,
company.get("website", ""),
company.get("address", ""),
company.get("total_shipments", ""),
company.get("top_suppliers", ""),
company.get("hs_codes", ""),
company.get("company_description", ""),
company.get("key_executives", ""),
company.get("deep_research_pdf", ""),
subject,
now.strftime("%Y-%m-%d %H:%M:%S"),
unique_app_id
))
conn.commit()
# 6. Mark source row as processed
with sqlite3.connect(DB_EXCEL_DATA) as conn:
conn.execute(
"UPDATE outreach_companies SET status = 'processed' WHERE id = ?",
(company["id"],)
)
conn.commit()
print(f" β
Saved to tracking DB β '{company_name}' | email: {company_email}")
print(f" Subject: {subject}")
return True
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# AGENT DAEMON LOOP
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
print("=" * 60)
print(" Textile Outreach Agent β starting single run")
print("=" * 60)
init_tracking_db()
try:
result = process_next_company()
if not result:
print("Nothing to process.")
except Exception as e:
print(f"[{datetime.now():%H:%M:%S}] Agent error: {e}")
raise |