Skip to content

Latest commit

 

History

History
273 lines (186 loc) · 8.88 KB

File metadata and controls

273 lines (186 loc) · 8.88 KB

System Architecture & Workflow

An end-to-end overview of phone registration, Telegram requests, Android SMS capture, backend matching, private OTP delivery, and session expiration.

flowchart TB

    %% =====================================================
    %% 01 — SETUP & PHONE REGISTRATION
    %% =====================================================

    subgraph S1["01 · Setup & Phone Registration"]
        direction TB

        ADMIN["System Admin"]
        CONFIG["Configure Android Receiver<br/>1–2 phone numbers"]
        READY{"Receiver ready?"}
        READINESS["SMS permissions<br/>Background execution<br/>JobScheduler readiness"]
        TEST["TEST AND SEND<br/>POST /api/test"]
        PHONE_STORE[("Phone Store<br/>Registered phones<br/>Usage counters")]

        ADMIN --> CONFIG
        CONFIG --> READY

        READY -- "No" --> READINESS
        READINESS --> READY

        READY -- "Yes" --> TEST
        TEST --> PHONE_STORE
    end


    %% =====================================================
    %% 02 — TELEGRAM REQUEST & SESSION CREATION
    %% =====================================================

    subgraph S2["02 · Telegram Request & Session Creation"]
        direction TB

        USER["Requester"]
        START["Start bot privately<br/>One-time prerequisite"]
        CODE["Send /code 09xxxxxxxxx<br/>in active Telegram group"]
        WEBHOOK["Telegram Webhook<br/>POST /telegram/webhook"]
        VALIDATE["Validate group, mode,<br/>command and phone"]

        REGISTERED{"Phone registered?"}
        REJECT_PHONE["Reject request<br/>Phone not registered"]

        ACTIVE{"Active session<br/>already exists?"}
        REJECT_DUP["Reject duplicate request<br/>Existing session preserved"]

        PRIVATE_READY["Send private<br/>Session Ready message"]
        PRIVATE_OK{"Private message sent?"}
        START_REQUIRED["Ask requester to<br/>start the bot first"]

        CREATE["Create active session"]
        SESSION_STORE[("Session Store<br/>Requester · Phone · Session ID<br/>CreatedAt · ExpiresAt")]
        TIMER["Register 120-second<br/>expiration timer"]
        GROUP_ACCEPT["Reply in group<br/>Request accepted"]

        USER --> START
        START --> CODE
        CODE --> WEBHOOK
        WEBHOOK --> VALIDATE
        VALIDATE --> REGISTERED

        REGISTERED -- "No" --> REJECT_PHONE
        REGISTERED -- "Yes" --> ACTIVE

        ACTIVE -- "Yes" --> REJECT_DUP
        ACTIVE -- "No" --> PRIVATE_READY

        PRIVATE_READY --> PRIVATE_OK
        PRIVATE_OK -- "No" --> START_REQUIRED
        PRIVATE_OK -- "Yes" --> CREATE

        CREATE --> SESSION_STORE
        CREATE --> TIMER
        CREATE --> GROUP_ACCEPT
    end


    %% Registered phones are used by the request validation path
    PHONE_STORE --> REGISTERED


    %% =====================================================
    %% 03 — DIVAR & ANDROID SMS CAPTURE
    %% =====================================================

    subgraph S3["03 · Divar & Android SMS Capture"]
        direction TB

        DIVAR_INPUT["Requester enters the same<br/>phone number in Divar"]
        DIVAR["Divar requests verification"]
        SMS["Verification SMS arrives"]

        RECEIVER["Android SMS BroadcastReceiver"]
        RECOVERY["Recent SMS Recovery<br/>Missed-broadcast fallback"]

        PARSE{"Valid Divar SMS<br/>with 6-digit OTP?"}
        IGNORE["Ignore message"]

        EXTRACT["Normalize digits<br/>Extract 6-digit OTP"]
        CANDIDATES["Resolve SIM metadata<br/>Build candidate phones"]

        DEDUP{"Fingerprint already seen<br/>within 24 hours?"}
        DISCARD["Discard duplicate"]

        QUEUE[("Persistent Local Queue<br/>FIFO · PendingDelivery<br/>60-second TTL")]

        JOB["JobScheduler<br/>Network required"]
        SUBMIT["Submit OTP + candidate phones<br/>POST /api/sms"]

        DIVAR_INPUT --> DIVAR
        DIVAR --> SMS
        SMS --> RECEIVER
        RECEIVER --> PARSE

        RECOVERY -. "fallback" .-> PARSE

        PARSE -- "No" --> IGNORE
        PARSE -- "Yes" --> EXTRACT

        EXTRACT --> CANDIDATES
        CANDIDATES --> DEDUP

        DEDUP -- "Yes" --> DISCARD
        DEDUP -- "No" --> QUEUE

        QUEUE --> JOB
        JOB --> SUBMIT
    end


    GROUP_ACCEPT --> DIVAR_INPUT


    %% =====================================================
    %% 04 — BACKEND MATCHING & SECURE DELIVERY
    %% =====================================================

    subgraph S4["04 · Backend Matching & Secure Delivery"]
        direction TB

        AUTH["Authenticate Android request<br/>App Secret + App Mode"]
        AUTH_OK{"Request authenticated?"}

        API_REJECT["Reject API request<br/>Queue item remains pending"]

        CLEAN["Prune expired sessions"]
        MATCH["Match candidate phones<br/>against active sessions"]
        MATCH_COUNT{"Active session matches"}

        NO_SESSION["0 matches<br/>Accepted · Not delivered"]
        AMBIGUOUS["Multiple matches<br/>Ambiguous · Sessions preserved"]

        ONE_MATCH["Exactly one match<br/>Resolve original requester"]

        PRIVATE_OTP["Send OTP privately<br/>through Telegram"]
        DELIVERY_OK{"Private delivery successful?"}

        RETRY["HTTP 502<br/>Keep session + queue item"]
        CLOSE["Close active session<br/>Cancel expiration timer"]

        COUNTER["Increment successfulDeliveries"]
        AUDIT[("Audit History<br/>Delivery / session events")]

        GROUP_SUCCESS["Reply to original group message<br/>Code received and sent<br/>OTP never appears in group"]

        DONE(["OTP Delivered"])

        AUTH --> AUTH_OK

        AUTH_OK -- "No" --> API_REJECT
        AUTH_OK -- "Yes" --> CLEAN

        CLEAN --> MATCH
        MATCH --> MATCH_COUNT

        MATCH_COUNT -- "0" --> NO_SESSION
        MATCH_COUNT -- "Multiple" --> AMBIGUOUS
        MATCH_COUNT -- "Exactly 1" --> ONE_MATCH

        ONE_MATCH --> PRIVATE_OTP
        PRIVATE_OTP --> DELIVERY_OK

        DELIVERY_OK -- "No" --> RETRY
        DELIVERY_OK -- "Yes" --> CLOSE

        CLOSE --> COUNTER
        CLOSE --> AUDIT
        CLOSE --> GROUP_SUCCESS
        GROUP_SUCCESS --> DONE
    end


    SUBMIT --> AUTH
    SESSION_STORE --> MATCH

    API_REJECT -. "retry while item is fresh" .-> JOB
    RETRY -. "exponential backoff" .-> JOB

    NO_SESSION -->|"200 · accepted"| DEQUEUE1["Remove queue item"]
    AMBIGUOUS -->|"200 · accepted"| DEQUEUE2["Remove queue item"]
    DONE -->|"200 · delivered"| DEQUEUE3["Remove queue item"]


    %% =====================================================
    %% 05 — SESSION EXPIRATION
    %% =====================================================

    subgraph S5["05 · Session Expiration"]
        direction TB

        DEADLINE{"Session still active<br/>after 120 seconds?"}
        TIMER_END["No action<br/>Session already completed"]
        EXPIRE["Expire session"]
        EXPIRE_AUDIT["Record expiration event"]
        NOTIFY_PRIVATE["Notify requester privately"]
        NOTIFY_GROUP["Reply Session expired<br/>in Telegram group"]
        EXPIRED(["Session Expired"])

        DEADLINE -- "No" --> TIMER_END
        DEADLINE -- "Yes" --> EXPIRE

        EXPIRE --> EXPIRE_AUDIT
        EXPIRE --> NOTIFY_PRIVATE
        EXPIRE --> NOTIFY_GROUP

        NOTIFY_PRIVATE --> EXPIRED
        NOTIFY_GROUP --> EXPIRED
    end


    TIMER -. "120s deadline" .-> DEADLINE


    %% =====================================================
    %% STYLES
    %% =====================================================

    classDef actor fill:#f8fafc,stroke:#64748b,color:#0f172a,stroke-width:1.5px;
    classDef telegram fill:#eff6ff,stroke:#2563eb,color:#172554,stroke-width:1.5px;
    classDef backend fill:#f5f3ff,stroke:#7c3aed,color:#2e1065,stroke-width:1.5px;
    classDef android fill:#f0fdf4,stroke:#16a34a,color:#14532d,stroke-width:1.5px;
    classDef external fill:#fff7ed,stroke:#ea580c,color:#7c2d12,stroke-width:1.5px;
    classDef store fill:#fffbeb,stroke:#d97706,color:#78350f,stroke-width:1.8px;
    classDef decision fill:#ffffff,stroke:#475569,color:#0f172a,stroke-width:1.8px;
    classDef warning fill:#fff7ed,stroke:#ea580c,color:#7c2d12,stroke-width:1.5px;
    classDef failure fill:#fef2f2,stroke:#dc2626,color:#7f1d1d,stroke-width:1.5px;
    classDef success fill:#ecfdf5,stroke:#059669,color:#064e3b,stroke-width:2px;

    class ADMIN,USER actor;

    class START,CODE,WEBHOOK,PRIVATE_READY,GROUP_ACCEPT,PRIVATE_OTP,GROUP_SUCCESS,NOTIFY_PRIVATE,NOTIFY_GROUP telegram;

    class VALIDATE,CREATE,TIMER,AUTH,CLEAN,MATCH,ONE_MATCH,CLOSE,COUNTER,EXPIRE,EXPIRE_AUDIT backend;

    class CONFIG,READINESS,TEST,RECEIVER,RECOVERY,EXTRACT,CANDIDATES,JOB,SUBMIT android;

    class DIVAR_INPUT,DIVAR,SMS external;

    class PHONE_STORE,SESSION_STORE,QUEUE,AUDIT store;

    class READY,REGISTERED,ACTIVE,PRIVATE_OK,PARSE,DEDUP,AUTH_OK,MATCH_COUNT,DELIVERY_OK,DEADLINE decision;

    class REJECT_PHONE,REJECT_DUP,START_REQUIRED,NO_SESSION,AMBIGUOUS warning;

    class IGNORE,DISCARD,API_REJECT,RETRY failure;

    class DONE,EXPIRED success;
Loading