DOC.02LIAP

⚙️ Architecture

Home


System Diagram

┌─────────────────────────────────────────────────────────┐
│                    Browser (UI)                          │
│              http://localhost:8000                       │
│                                                         │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐  │
│  │Live Feed │ │ Results  │ │Msg Prosp.│ │Follow Up │  │
│  └────┬─────┘ └──────────┘ └────┬─────┘ └────┬─────┘  │
│       │ WebSocket /ws            │ /ws/msg     │/ws/followup
└───────┼──────────────────────────┼─────────────┼────────┘
        │                          │             │
┌───────▼──────────────────────────▼─────────────▼────────┐
│                    FastAPI (main.py)                      │
│                                                          │
│  GET /status  GET /results  GET /messages  GET /followups│
│  GET /runs    GET /msg-status  GET /followup-status      │
│  POST /test-ai                                           │
└───────────────┬──────────────┬──────────────┬───────────┘
                │              │              │
        ┌───────▼───┐  ┌───────▼───┐  ┌──────▼──────┐
        │automator  │  │messenger  │  │  logger.py  │
        │   .py     │  │   .py     │  │             │
        │(connects) │  │(messages+ │  │connections  │
        └─────┬─────┘  │ followups)│  │messages     │
              │        └─────┬─────┘  │followups    │
              │              │        │  (CSVs)     │
        ┌─────▼──────────────▼──────┐ └─────────────┘
        │   Playwright Chrome       │
        │  (persistent context)     │
        │                           │
        │   Your LinkedIn Session   │
        └───────────────────────────┘
                      │
              ┌───────▼───────┐
              │  ai.py        │
              │  Ollama local │
              │  or Gemini    │
              └───────────────┘

Request Flow — Connection Run

User clicks Execute
  → app.js opens WebSocket /ws
  → main.py receives {action: "run", companies: [...]}
  → automator.run() launches Chrome
  → Searches LinkedIn for people at each company
  → Scores each profile (AI or keyword)
  → Sends connection request with note
  → Logs to connections.csv
  → Streams log messages back via WebSocket
  → UI renders in Live Feed

Request Flow — First Message Run

User clicks Send Messages (Msg Prospect tab)
  → app.js opens WebSocket /ws/msg
  → main.py receives {action: "msg_run", msg_cap: N}
  → messenger.run_messaging() launches Chrome
  → Navigates to /mynetwork/invite-connect/connections/
  → JS evaluates DOM → scrapes connection cards
  → Filters already-messaged (dedup via messages.csv)
  → For each new connection:
      → navigate to compose URL
      → Ollama generates personalized message
      → Types message, clicks Send
      → Logs to messages.csv
      → Seeds followups.csv (status=pending)

Request Flow — Follow-Up Run

User clicks Send Follow-Ups (Follow Up tab)
  → app.js opens WebSocket /ws/followup
  → main.py receives {action: "followup_run", wait_days: N}
  → messenger.run_followups() launches Chrome
  → Seeds followups.csv from messages.csv (idempotent)
  → Filters: status=pending AND first_msg_sent_at ≤ cutoff
  → For each candidate:
      → Navigate to their LinkedIn profile page
      → Click the Message <button> on the profile
      → Compose overlay opens on the profile page
      → Check last message bubble class:
          .msg-s-event-listitem--other → they replied → mark replied, skip
          .msg-s-event-listitem--self  → we sent last → send follow-up
      → Ollama generates follow-up referencing original message
      → Types message, clicks Send
      → upsert_followup() → status=followed_up

WebSocket Message Protocol

All WebSocket channels use the same JSON message format:

// Server → Client
{ "type": "log",     "message": "Navigating to profile..." }
{ "type": "started"                                         }
{ "type": "done"                                            }
{ "type": "error",   "message": "Something went wrong"     }

// Client → Server
{ "action": "run",          "companies": [...], "speed_multiplier": 1.0 }
{ "action": "msg_run",      "msg_cap": 5, "scan_limit": 20              }
{ "action": "followup_run", "followup_cap": 5, "wait_days": 3           }
{ "action": "pause"  }
{ "action": "resume" }
{ "action": "stop"   }

Deduplication Strategy

FeatureDedup KeyStorage
Connection requestsprofile_url (normalized)connections.csv
First messagesprofile_url + name fallbackmessages.csv
Follow-upsstatus != "pending"followups.csv

URL normalization: strips query params, trailing slash, lowercased.