feat: Add Chatwoot Agent Bot prototype and FAQ knowledge base
- Created chatwoot-agent-bot/ with Node.js webhook server - Bot detects intent (greeting, billing, technical, features, account) - Auto-responds from FAQ knowledge base or escalates to human - FAQ-KB.md: Living knowledge base that grows with customer questions - CHATWOOT-SETUP.md: Complete deployment and configuration guide - Supports Telegram notifications on escalation - Bot runs on port 3001, ready for Chatwoot webhook integration
This commit is contained in:
175
agents/marketing-seo/scripts/seo-agent-v2.py
Executable file
175
agents/marketing-seo/scripts/seo-agent-v2.py
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Marketing-SEO Agent v2 - With GA4 Integration
|
||||
24/7 Monitoring: Site Uptime + Traffic Analytics
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
import urllib.request
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from google.analytics.data import BetaAnalyticsDataClient
|
||||
from google.analytics.data_v1beta.types import RunReportRequest, DateRange, Metric
|
||||
|
||||
WORKSPACE = Path(__file__).parent.parent
|
||||
LOG_DIR = WORKSPACE / "logs"
|
||||
STATE_FILE = WORKSPACE / "state" / "agent-state.json"
|
||||
CONFIG_DIR = WORKSPACE / "config"
|
||||
GA_CREDS = CONFIG_DIR / "ga-credentials.json"
|
||||
GA_PROPERTY = "526394825"
|
||||
|
||||
SITES = [
|
||||
"https://www.hoaledgeriq.com",
|
||||
"https://app.hoaledgeriq.com"
|
||||
]
|
||||
MONITOR_INTERVAL = 3600
|
||||
|
||||
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def log(msg):
|
||||
ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
line = f"[{ts}] {msg}"
|
||||
print(line)
|
||||
log_file = LOG_DIR / f"seo-agent-{datetime.now().strftime('%Y%m%d')}.log"
|
||||
with open(log_file, 'a') as f:
|
||||
f.write(line + '\n')
|
||||
|
||||
def check_site(url):
|
||||
"""Check if site is up"""
|
||||
start = time.time()
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "SEO-Agent/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=15) as r:
|
||||
return r.getcode() == 200, r.getcode(), round(time.time() - start, 2)
|
||||
except Exception as e:
|
||||
return False, str(e), None
|
||||
|
||||
def get_ga4_data():
|
||||
"""Get GA4 traffic data"""
|
||||
try:
|
||||
client = BetaAnalyticsDataClient.from_service_account_json(str(GA_CREDS))
|
||||
request = RunReportRequest(
|
||||
property=f"properties/{GA_PROPERTY}",
|
||||
date_ranges=[DateRange(start_date="1daysAgo", end_date="today")],
|
||||
metrics=[
|
||||
Metric(name="sessions"),
|
||||
Metric(name="activeUsers"),
|
||||
Metric(name="newUsers"),
|
||||
Metric(name="bounceRate"),
|
||||
Metric(name="averageSessionDuration")
|
||||
]
|
||||
)
|
||||
response = client.run_report(request)
|
||||
|
||||
if response.rows:
|
||||
r = response.rows[0]
|
||||
return {
|
||||
"sessions": int(r.metric_values[0].value),
|
||||
"users": int(r.metric_values[1].value),
|
||||
"new_users": int(r.metric_values[2].value),
|
||||
"bounce_rate": float(r.metric_values[3].value),
|
||||
"avg_duration": float(r.metric_values[4].value)
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
return {"sessions": 0, "users": 0, "new_users": 0}
|
||||
|
||||
def send_alert(title, message, severity="warning"):
|
||||
"""Send Telegram alert"""
|
||||
log(f"🔔 ALERT [{severity}]: {title}")
|
||||
try:
|
||||
tg_msg = f"🔔 *SEO Alert: {title}*\n\n{message}\n\n⏰ {datetime.now().strftime('%H:%M')}"
|
||||
subprocess.run(["openclaw", "message", "send", "--text", tg_msg],
|
||||
capture_output=True, timeout=10)
|
||||
except:
|
||||
pass
|
||||
|
||||
def hourly_check():
|
||||
"""Hourly monitoring: Sites + GA4"""
|
||||
log("=== Hourly Site + Traffic Check ===")
|
||||
|
||||
site_status = {}
|
||||
for site in SITES:
|
||||
is_up, status, time_ms = check_site(site)
|
||||
site_status[site] = {"up": is_up, "status": status, "time_ms": time_ms}
|
||||
if is_up:
|
||||
log(f"✅ {site}: UP ({status}) - {time_ms}s")
|
||||
else:
|
||||
log(f"❌ {site}: DOWN ({status})")
|
||||
send_alert(f"SITE DOWN: {site}", f"Status: {status}", "critical")
|
||||
|
||||
# GA4 traffic
|
||||
ga = get_ga4_data()
|
||||
if "error" not in ga:
|
||||
log(f"📊 GA4 Traffic: {ga.get('sessions',0)} sessions, {ga.get('users',0)} users")
|
||||
else:
|
||||
log(f"⚠️ GA4 Error: {ga.get('error')}")
|
||||
|
||||
return {"sites": site_status, "ga4": ga}
|
||||
|
||||
def main():
|
||||
log("🚀 Marketing-SEO Agent v2 Started")
|
||||
log(f"Sites: {', '.join(SITES)}")
|
||||
log(f"GA4 Property: {GA_PROPERTY}")
|
||||
|
||||
last_check = 0
|
||||
|
||||
while True:
|
||||
now = datetime.now()
|
||||
now_ts = int(now.timestamp())
|
||||
|
||||
if now_ts - last_check >= MONITOR_INTERVAL:
|
||||
hourly_check()
|
||||
last_check = now_ts
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
def daily_rank_check():
|
||||
"""Daily check - if any keywords break into top 100, alert"""
|
||||
import re
|
||||
|
||||
state_file = WORKSPACE / "state" / "rank-data.json"
|
||||
if not state_file.exists():
|
||||
return
|
||||
|
||||
with open(state_file) as f:
|
||||
data = json.load(f)
|
||||
|
||||
positions = data.get('positions', {})
|
||||
|
||||
# Check if any are now ranked (non-null)
|
||||
ranked = sum(1 for p in positions.values() if p is not None)
|
||||
total = len(positions)
|
||||
|
||||
if ranked > 0:
|
||||
log(f"📈 Rank Progress: {ranked}/{total} keywords now ranking")
|
||||
|
||||
# Alert on new rankings
|
||||
report = "🎉 *RANKING PROGRESS!*\n\n"
|
||||
for kw, pos in positions.items():
|
||||
if pos:
|
||||
report += f"• {kw}: #{pos}\n"
|
||||
|
||||
send_alert("New Rankings Detected!", report, "info")
|
||||
else:
|
||||
log(f"📊 SEO Status: ({ranked}/{total} keywords in top 100 - baseline phase)")
|
||||
|
||||
def get_monthly_milestone():
|
||||
"""Return current SEO milestone based on launch date"""
|
||||
launch = datetime(2026, 3, 22) # Launch date
|
||||
now = datetime.now()
|
||||
days_live = (now - launch).days
|
||||
|
||||
if days_live < 30:
|
||||
return "Month 1: Focus on technical SEO + content creation"
|
||||
elif days_live < 90:
|
||||
return "Month 2-3: Target long-tail keywords, build backlinks"
|
||||
elif days_live < 180:
|
||||
return "Month 4-6: Optimize for primary keywords"
|
||||
else:
|
||||
return "Phase 2: Established - maintenance + expansion"
|
||||
|
||||
# Add to daily check
|
||||
Reference in New Issue
Block a user