01Overview
What you're building, and the five rules that keep it honest.
This is a semi-automated assistant that helps you reply to Badoo matches faster without turning into spam. It reads your threads, classifies where each one sits in your funnel, and drafts a reply in your voice. You approve every send. Nothing leaves your account without you.
Non-negotiable principles
- Human approves every send. The draft is a suggestion; the button is yours.
- No unsolicited links. The Spotify smart link goes out only after an explicit ask.
- Hard stop on dead threads. If it doesn't develop, the thread is flagged
STOPPED— no "just checking in". - Conservative volume, human-like timing. Ban-risk management, not growth hacking.
- Honesty. If someone asks whether they're talking to a bot, don't lie.
The funnel you're steering
Genuine match → personal opener → real conversation → mention Samel Senja only when music is naturally relevant → send the Spotify smart link only when they explicitly ask for it.
02How it works
Six moving parts, one human gate. This mirrors your architecture diagram.
Approved replies go out — never auto-sent. The green node is the only place a message can enter the world.
Recommended path: B → A
| Option | How it works | Ban risk | Effort | Verdict |
|---|---|---|---|---|
| A · Web-session automation | Playwright drives badoo.com in your own logged-in profile; reads threads, types approved replies. | Medium–high | Medium | Target state — build guardrails first |
| B · Draft-only assistant | Bot shows drafts in a local dashboard; you copy-paste yourself. | Zero | Low | Start here — ship in week 1 |
| C · Mobile app automation | Emulator + UIAutomator / Appium drives the Android app. | High | High | Avoid |
03Lead the conversation
The general doctrine — one global instruction applied to every draft, no matter the stage.
Paste this as the base of your drafter's system prompt. It sets the intent: each message exists to earn the next one, using curiosity rather than need. Drop in your own name where it says [USER] — or use the prompt assembler below to generate the full block.
GLOBAL SYSTEM PROMPT — CONVERSATION LEADERSHIP
You draft messages on behalf of [USER] on Badoo. Each turn your job is NOT to convert
anyone. Your job is to earn the next reply by being genuinely interesting to talk to.
MISSION
Keep a real, warm conversation moving forward, one honest message at a time, and let the
music / Spotify moment arrive naturally — never forced.
LEADERSHIP RULES
G1. One message = one goal: earn the next reply. Never chase two objectives at once.
G2. Lead with curiosity, not need. Give at least as much value as you ask for.
G3. Acknowledge → Add → Hook. Briefly react to their last message, add something about you
or your take, then leave exactly one easy, open hook to answer.
G4. Mirror them: match their length, energy, formality and emoji use. Never out-talk them.
G5. Ask at most ONE question per message. Never interview.
G6. Be specific, not complimentary. Reference concrete details from their profile or last
message. Ban generic praise about looks.
G7. Pace, don't pounce. Escalate topics no faster than they do: small talk → stories →
tastes → music. If they pull back, you pull back.
G8. Tease a little, reveal slowly. Leave hooks; don't dump everything at once.
G9. Callbacks create intimacy. Reuse their earlier details to build in-jokes and a "we"
feeling.
G10. Emotion before facts. Respond to how they feel before what they said.
G11. Never pitch the artist or the link. The music is the destination, not a sales call.
G12. Respect signals. Warmth only grows from real reciprocity. Two low-effort replies = stop.
MESSAGE FORM
- 1–3 short sentences. Plain, casual, human. Light punctuation.
- 0–1 emoji, and only if they use them.
- No links unless the current stage is LINK.
- No "how's your day", no walls of text, no double questions, no double-texting.
HARD BANS
- looks-only compliments / "hey gorgeous" / anything that reads copy-pasted
- guilt, begging, "why didn't you reply", sob stories
- sending the Spotify link before an explicit ask
- claiming to be human if directly asked, or dodging that question
- more than one unanswered follow-up, ever, to the same thread
OUTPUT
Return the draft text only. No quotes, no preamble, no explanation.
The escalation ladder — one rung at a time
| Rung | Topic terrain | Led by | Stage |
|---|---|---|---|
| 1 | Surface: profile, day, mood | Specific callback + easy hook | OPENER |
| 2 | Rapport: stories, energy, what they care about | Mirror + self-reveal + curiosity | CONVO |
| 3 | Shared taste: music, gigs, moods | Their music point → artist, zero pitch | MUSIC |
| 4 | Explicit interest | Confirm + link + let go | LINK |
| 5 | They opt out / fade | Stop clean, no chasing | STOPPED |
You may only lead one rung at a time — and only when the thread actually shows the signal for it.
04Per-stage prompts
The individual instructions. Append exactly one block to the global doctrine, based on the thread's current stage.
OPENER — earn the first reply
STAGE: OPENER — goal: earn their very FIRST reply.
Lead technique: specific profile callback + a light opinion or playful assumption + one easy
question.
Format: 1–2 short sentences.
Do: reference ONE concrete detail (bio line, photo, a prompt, music in their profile);
assume something playful ("you look like the type who…"); make replying effortless.
Don't: compliment looks, talk about yourself first, ask heavy questions, send links.
Shape: "[saw detail] — [playful assumption or take]. [one easy question]?"
Example: "Your playlist is a whole personality — Arctic Monkeys AND Bad Bunny is a dangerous
mix. Which one's the never-skip?"
Advance to CONVO when: they reply with anything substantive.
Stop when: silence after a single spaced follow-up → STOPPED.
CONVO — build depth, let music surface
STAGE: CONVO — goal: build rapport and depth; let music surface naturally.
Lead technique: mirror + share a real piece of you + curiosity that drifts toward taste,
moods, stories.
Format: 1–3 short sentences, mirrored to their length.
Do: use callbacks; react to feelings; offer small self-reveals; end with one open hook.
Don't: mention the artist or the link; interrogate; flirt harder than they do; chase silence.
Shape: "[ack their message] [your take or one-line story] [one hook toward taste / energy /
weekend]."
Advance to MUSIC when: music, gigs, artists or "what you're into" comes up naturally.
Stop when: two consecutive low-effort/one-word replies, or negativity → STOPPED.
MUSIC — make the artist part of the chat
STAGE: MUSIC — goal: make the artist feel like part of the conversation, never a pitch.
Lead technique: connect THEIR music mention to Samel Senja through one genuine, personal
angle, then ask their ear.
Format: 1–2 sentences.
Do: build on their framing ("since you're into [X]…"); stay casual; ask what they think.
Don't: send a link; hype with adjectives; turn it into an ad; name-drop if they haven't shown
openness.
Shape: "[their music point] reminds me of Samel Senja — [one-line honest why]. What do you
think?"
Advance to LINK only when: they explicitly ask ("link?", "what's it called?", "send it").
Stop when: no interest in music → return to CONVO. Never push.
LINK — give it, then let go
STAGE: LINK — goal: satisfy an explicit ask with zero friction. Lead technique: confirm, give the smart link, add one casual line. Then stop selling. Format: 1 short line + link + 1 casual sentence. Do: keep it light; use the approved UTM smart link; let them choose to click. Don't: oversell, ask for feedback immediately, follow up if they go quiet. Shape: "Here you go — [smart link]. Curious which one hits first." After: stage = LINK_SENT. Log the receipt (screenshot + timestamp).
05Per-scenario rules
Reactive overrides applied on top of the doctrine when the last incoming message matches a trigger.
| Trigger in their last message | Override instruction | Resulting stage |
|---|---|---|
| One-word / very low-effort reply | Match their length, don't chase, ONE light playful question. Second occurrence → stop. | CONVO or STOPPED |
| Direct ask "are you a bot / real person?" | Answer honestly and briefly; don't over-explain or dodge. Stay warm. | unchanged |
| Asks for the link, name, or "what's it called?" | Give the approved smart link + one casual line. No upsell. | → LINK |
| Mentions music, a gig, an artist, a playlist | Eligible for the music turn; connect naturally, ask their take. No link. | → MUSIC |
| Sexual / crude escalation too early | Deflect lightly, keep frame; if it repeats, stop. No shaming. | CONVO or STOPPED |
| Vents about an ex / a bad day | Lead with empathy first, light reframe second, no advice-dumping, then one gentle hook. | CONVO |
| Opt-out / negative keyword ("stop", "leave me alone") | Stop immediately — no reply, no follow-up. | → STOPPED |
| Cold for 7+ days after your last message | No follow-up. Flag the thread and close it. | → STOPPED |
| They propose meeting / moving off-platform | Keep it light and within your comfort rules; never guilt, never rush. | CONVO |
Thread state the drafter must read and write
stage—OPENER | CONVO | MUSIC | LINK | LINK_SENT | STOPPEDSTOPPED— hard stop; no further drafts generatedMUSIC_MENTIONED/LINK_SENT— progression flagslow_effort_count— increments on one-word replies;≥ 2forcesSTOPPEDlast_msg_at— drives cadence and the 7-day cold-thread rule
06Stage machine
Your diagram translated to code behaviour — the detector decides the stage; the drafter never guesses it.
| Diagram node | System behaviour |
|---|---|
| You find a genuine match | Poller detects the new match → stage = OPENER |
| Assistant drafts personal opener | Drafter builds an opener from profile signals (bio, prompts, music taste) — never generic |
| You approve and send | Approval dashboard; sender types it (or you paste it, in draft-only mode) |
| Real conversation develops? | Heuristic: they replied ≥ 2 times with substance → CONVO; silence → STOPPED |
| Music becomes relevant? | LLM relevance check — advances only if they mention music, concerts, or taste |
| Mention Samel Senja naturally | Drafter weaves in artist context as conversation, never as a pitch |
| They want the link? | Only an explicit ask ("link?", "what's it called?") advances the stage |
| Send Spotify smart link | Approved send with a UTM-tagged smart link → stage = LINK_SENT |
07Guardrails
Enforced before every send. These are what keep the account alive and the funnel genuine.
- Max 15–20 sends/day; randomized 2–10 min gap between sends.
- Quiet hours 23:00–08:00; poller jitter to avoid a robotic cadence.
- Opt-out / negative keyword → thread
STOPPEDimmediately. - Per thread: zero unanswered follow-ups — at most one, ever, if you insist.
- Dashboard kill switch + full audit log of every send (screenshot, timestamp, draft text).
- Cadence: never reply instantly — mirror their typical delay, floor ≈ 2 min, ceiling a few hours.
Tech stack
Python 3.11+ · Playwright (Chromium, persistent profile) · SQLite · FastAPI + HTMX/Alpine mini-UI · any chat LLM API · short link + UTM for the Spotify smart link (own redirect or Linkfire) · cron / long-running worker on your Mac or a VPS.
08Build in 4 phases
Ship the zero-risk version first, then earn the right to automate sending.
- Week 1 — Draft-only MVPDashboard + manual paste, OPENER + CONVO prompts. Acceptance: 10 real drafts you'd actually send.
- Week 2 — Funnel logicStage machine, SQLite store, music-relevance detection. Acceptance: correct stage transitions on 20 sample threads.
- Week 3 — Assisted sendRead-only scrape + type-on-approve, ≤ 5 sends/day. Acceptance: 20 approved sends, no account warnings.
- Week 4 — Guardrails + analyticsRate limits, UTM tracking, weekly report. Acceptance: metrics dashboard live.
Metrics to watch
Reply rate (opener → reply), conversation rate (reply → 5+ messages), music-relevance rate, link-ask rate, link clicks (UTM), and ban/warning incidents — target 0.
09Wire it up
How the playbook plugs into the drafter, and a live assembler for the prompt.
Assembly order, highest priority last. Output is the draft text only.
- Stage detector decides
stage; the drafter never guesses. If unsure, keep the current stage and let the human decide in the dashboard. - Approval dashboard shows the stage and flags next to each draft so you know why it reads the way it does — and can edit tone without touching the funnel logic.
- Style anchors: add 10–20 messages you actually wrote to the global block, so the doctrine produces your voice, not a generic one.
- Tuning loop: track reply-rate per opener style and per stage; retire phrasing that underperforms and promote what works back into the doctrine.
Press “Assemble” to build global doctrine + the selected stage block, with your name filled in.
Tip: paste the assembled text into your drafter's system prompt, then append the last ~10 messages of the thread at run time.
10Live UI & API map
Captured from the real web app in a read-only pass — the screens, the controls, and the private API the app itself talks to.
Routes (single-page app)
| Route | Screen | What's on it |
|---|---|---|
| /en-us/connections | Inbox | Chat list: name + last-message preview + unread count + Favorite toggle; Edit mode → Unmatch & Block / Report; Most recent sort; Activity indicator. |
| /en-us/messages/<chat_instance_id> | Thread | Message list with date separators and left/right bubbles; read receipt; composer (Add photo · Record audio · Questions game · More options · type box); Open Profile; Report. |
| /en-us/encounters | Discover | Swipe deck; empty state “You've seen everyone”. |
| Bottom nav | — | Nearby · Encounters · Likes · Chats · Profile. |
Private API the app uses
Every action is a POST to https://badoo.com/mwebapi.phtml?SERVER_<METHOD> with a protobuf-as-JSON body ({"$gpb":"badoo.bma.BadooMessage","body":[{"message_type":<id>,…}]}). Auth is the session cookie. The client maps each method name to a numeric message_type.
| Method | Type → resp | Purpose |
|---|---|---|
| SERVER_OPEN_CHAT | 102 → 103 | Open a thread by chat_instance_id; returns chat settings + icebreaker. |
| SERVER_GET_CHAT_MESSAGES | 279 → 280 | Fetch message history / older messages. |
| SERVER_SEND_CHAT_MESSAGE | 104 → 105 | Send a text/media message (chat_message: chat_instance_id, text, message_type, photo/sticker/gift). |
| SERVER_SEND_MULTIPLE_CHAT_MESSAGES | 560 | Send several messages in one call. |
| SERVER_GET_USER_LIST | 245 → 246 | List a folder — folder_id:56 = conversations, 49 = likes; returns display_message, unread_messages_count, sort_timestamp. |
| SERVER_REQUEST_PERSON_NOTICE | 157 | Per-folder counters (unread / matches). |
| SERVER_CHAT_MESSAGE_READ | 555 → 556 | Mark messages read. |
| SERVER_DELETE_CHAT_MESSAGE | 373 → 374 | Delete a message. |
| SERVER_CHAT_IS_WRITING | — | Typing indicator. |
| SERVER_CHECK_BALANCE | 573 → 574 | Credit / balance check. |
| SERVER_SYNC_INSTANT_PAYWALLS | 749 → 750 | Upsell paywalls (“Get more messages”). |
Method names and type ids read from the app's own client bundle; the READ endpoints were also observed live. The SEND endpoint is confirmed from the client source but was not captured live (no message was sent during the pass).
What this means for the build
- UI automation (Option A) is the fragile path. The site is a thin client over
mwebapi.phtml, so an API client is more robust than DOM scraping — but it is an undocumented private API, not an official one. - Fingerprinting is real. The app beacons to
badoo.com/hotpanel/hotpanel2.phtmlwith app version, device id and session id; the payment flow loads ThreatMetrix anti-fraud. A script that skips these looks non-browser. - Sending can be gated. The chat service has
payToSend/needToPayToSendMessage_/checkPendingPaidMessages_, so some sends may require credits. CheckSERVER_CHECK_BALANCEbefore enabling assisted send. - Storage note: the read endpoints expose third-party PII. Keep the SQLite store minimal and honour delete requests.
11Open-source stack & improvements
What already exists on GitHub that you can adopt instead of building — and what to avoid. Star counts as of 2026-09-22.
The closest analogue: a “ManyChat alternative”
ManyChat is a paid Instagram / FB / WhatsApp automation suite. Several open-source clones appeared in 2026 — the pattern (trigger → AI reply → human handoff → approval) is exactly your assistant's shape, so borrow their architecture and UX.
| Project | Stars | What to take from it |
|---|---|---|
| diwenne/openreply | 2.4k | “The open-source Manychat alternative.” Closest reference for AI reply + handoff flows. |
| ChatbotXIO/ChatbotX | 821 | Omnichannel AI marketing bot; agentic reply + campaign model. |
| zernio-dev/zernflow | 193 | Visual chatbot builder across social channels. |
| ayuuxh2/insta-p8 | 128 | Self-hosted DM automation + AI replies (Next.js + Supabase). |
Adopt-instead-of-build candidates
| Your component | Project (stars) | What it gives you |
|---|---|---|
| Approval dashboard | Chatwoot (37k) · libredesk (3.0k) | Shared inbox with pending / private-note states + REST API — replaces the hand-rolled FastAPI mini-UI. |
| Funnel state machine | Typebot.io (10.3k) | Visual flow builder — model OPENER → CONVO → MUSIC → LINK as a canvas. |
| Orchestration / rate limits | n8n (205k) | Cron, jitter, quiet hours, retries, webhook kill switch — no hand-written scheduler. |
| LLM drafting + prompt mgmt | Dify (157k) | Prompt versioning, model routing, run logs, built-in eval UI. |
| Multi-channel bot framework | LangBot (17.9k) | Agent + knowledge-base + plugin pattern; architecture reference. |
| Tuning loop (metrics) | Langfuse (34.9k) | Trace every draft; attribute reply-rate per prompt version. |
| Scrape/browser resilience | Crawlee-python (9.5k) · Skyvern (23k) | Retries/anti-flake; natural-language-driven UI actions. |
| Future: WhatsApp channel | Evolution API (9.7k) | Only if the funnel moves off Badoo (your chats already ask for WhatsApp). |
| Agent frameworks | LangGraph (42k) · LangChain (147k) | Only if the funnel becomes genuinely multi-agent. |
Recommended v2 stack
Keep the current prompt playbook and store; swap the generic parts for proven tools:
- Orchestrator: n8n (schedule polling, enforce rate limits + quiet hours, kill switch).
- Approval: Chatwoot — chosen over libredesk (mature REST API + native WhatsApp channel); each match = a conversation, each draft = a pending reply.
- Drafting: direct LLM API +
conversation-playbook.md(add Dify only if you want a prompt-management UI). - Observability: Langfuse — feeds the “tune prompts from reply-rate” loop.
- Browser: Playwright (keep), with Crawlee patterns for retries.
- Store: SQLite (keep) → Postgres once Chatwoot is adopted (Chatwoot needs Postgres).
Channels: Badoo (primary) + WhatsApp (follow-up)
The funnel leaks at the same point every time — matches ask “do you have WhatsApp?”. Treat channels as pluggable adapters behind one draft → approve → send pipeline, not two systems.
| Adapter | Read | Send | Notes |
|---|---|---|---|
| Badoo (primary) | SERVER_GET_USER_LIST · OPEN_CHAT · GET_CHAT_MESSAGES | SERVER_SEND_CHAT_MESSAGE | Session-cookie auth; full map in §10. |
| WhatsApp (follow-up) | Evolution API webhooks | Evolution API send | Only on an explicit ask; still human-approved. Bridge: Evolution API (self-host) or official Cloud API. |
Guardrails carry over unchanged: same rate cap, quiet hours, no unsolicited links, human approval on every send. Channel risk is additive — moving to WhatsApp relaxes no rule.
Explicitly not recommended
Anti-detection / stealth stacks (SeleniumBase stealth, invisible_playwright, patchright, undetected-* ) — these exist to evade bot detection. They're evasion tooling, they're excluded on policy, and they don't reduce your ban risk: they raise it, and they make the activity look more deliberate. Bulk account/mass-DM tools (bulk profile creators, mass swipers) — ToS-violating at scale and exactly what gets accounts nuked. Stay personal-scale, human-approved.
12Deployed (live)
This page and the Chatwoot + n8n pipeline run on the dedicated VPS (148.230.103.146) as of 2026-09-23.
| What | URL | State |
|---|---|---|
| This setup page | https://lasunsetsession.online/ | live (HTTPS ✅) |
| Chatwoot approval inbox | https://inbox.lasunsetsession.online | live (login required) |
| n8n | https://n8n.lasunsetsession.online | live (bot's own instance) |
| New-match webhook | https://n8n.lasunsetsession.online/webhook/badoo-match | live |
| Draft job (run now) | /webhook/badoo-draft-run | live |
| Send job (run now) | /webhook/badoo-send-run | live |
Pipeline verified end-to-end
A new match posts to the webhook → Chatwoot contact + pending conversation + stage label. The draft job reads the thread, calls a local LLM, and writes a private draft note; you approve in Chatwoot; the send job sends and advances the stage. Tested live: match → reply → draft → approve → send, all green.
- Done: dedicated VPS
148.230.103.146with Docker + Compose; Chatwoot in/opt/chatwoot(own Postgres + Redis) behind HTTPS atinbox.lasunsetsession.online; the bot's own n8n atn8n.lasunsetsession.onlinewith 3 active workflows; local LLM drafter (Ollamamistral:7b-instruct); sender sink; Cockpit (:9090) + Portainer (:9443). The swift-visa VPS footprint was removed; its visa stack is untouched. - Next: point the sender at the real Badoo/WhatsApp channel; Badoo poller feeding the webhook; WhatsApp adapter (needs your QR).
Ops commands + rollback in DEPLOYMENT.md; API calls + label map in CHATWOOT-GLUE.md. Credentials live on the VPS only — never in this page.
13Reality check
Read this before you write a line of code.
Badoo's terms prohibit bots and automation. Driving the web UI with a script can get the account restricted or banned. Keep volume low, keep every send human-approved, and be honest if a match asks whether they're talking to a bot.
Badoo has no official public messaging API. Any bot either drives their web/app UI with automation, or works as a drafting tool you operate yourself. The safest design is therefore "bot drafts, human sends" first, then optionally upgrade to assisted in-browser sending at low volume.
Keep it personal-scale
- Never mass-DM or resell this.
- Don't scrape or store more profile data than you need.
- Delete stored conversations on request.
- If Badoo warns or restricts the account, drop straight back to draft-only mode.