Build Eloqra

KINDAreplaces $4.99/mosaves $59.88/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A small script tag that puts a chat widget on your site, a WebSocket server that relays messages to a Telegram bot, replies that go back to the visitor when you reply to the bot's message, and an optional Mini App console for when one conversation at a time stops being enough.

estimated effort weekendthe files for this build are in the project pack

ServerNode 22 with a WebSocket serverInboxA Telegram bot through the Bot APIWidgetA hand-written script under 15 KBStorageSQLiteConsoleAn optional Telegram Mini App

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the server, the socket and the webhook.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. API keyfree

    Why It is the inbox. Without it there is nowhere for messages to go.

    Get it Message BotFather in Telegram, create a bot, and take the token. If you want the Mini App in Phase 5, also register its URL and a menu button there. open ↗

    Verify A getMe call with the token returns your bot

  3. have ready5 to 10 USD a month for a server, plus a domain

    Why Telegram will not deliver webhooks to plain HTTP and will not load a Mini App over it. This is not a preference; it simply does not work.

    Get it A small server with a domain and automatic certificates, or a tunnel that terminates TLS for local development.

    Verify setWebhook succeeds and a test update is delivered

  4. have readyfree

    Why Same-origin testing hides the CORS and origin problems Phase 1 exists to catch.

    Get it Any static host, or a second local server on a different port, which is a different origin.

    Verify The widget loads and connects from that page

  5. have readyfree

    Why Phase 5's webview sizing, safe areas and long press copy cannot be judged in a desktop client.

    Get it Any phone with Telegram. Test in both normal and fullscreen presentation.

    Verify The Mini App opens on the phone and fits correctly

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- sites(id, name, origin_allowlist, widget_key)
- conversations(id, site_id, visitor_token, state, started_at, last_message_at, telegram_topic_id)
- messages(id, conversation_id, seq, author, body, sent_at, telegram_message_id) · seq is a per-conversation monotonic integer
- visitors(visitor_token, first_seen_at, last_seen_at, user_agent_class) · a token you issued, not an identity you inferred
- The link between a Telegram message and a conversation is stored, because replying to a bot message is how an operator answers and that mapping is the whole mechanism.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
TELEGRAM_BOT_TOKENsecretrequired123456:replace-with-tokenFrom BotFather. Server only, never in client code or logs.
TELEGRAM_CHAT_IDrequired123456789Where visitor messages are forwarded. Your own chat, or a group.
PUBLIC_URLrequiredhttps://chat.example.comUsed for the webhook and the Mini App URL. Must be HTTPS.
DB_PATHrequired./data/chat.dbConversations, messages and visitors.
ORIGIN_ALLOWLISTrequiredhttps://yoursite.comWhich origins may open a socket. Checked at handshake.
VISITOR_TOKEN_SECRETsecretrequiredgenerate-a-long-random-valueSigns visitor tokens so one cannot be forged or reused from another browser.
MAX_NEW_CONVERSATIONS_PER_HOURrequired20Per site. Conversation creation is the expensive operation because each one becomes a notification.

The build, in order

  1. Widget, origin control and a socket

    A script tag on your site opens a conversation, and an origin you did not authorise cannot.

    1. Shadow root so the host page's styles cannot break it. No framework, no build step for the widget itself.

      Files public/widget.js

    2. Check the Origin header against the allowlist before accepting the connection.

      Files src/ws.js

    3. Signed, server-issued, stored in local storage. Never generated in the browser.

      Files src/visitors.js

    done when · tick each as it passes
    watch out
    • The widget key ships in public HTML, so it identifies a site and grants nothing. Every authorisation decision belongs on the server, and the visitor token must be issued by the server rather than generated in the browser, or anyone can claim another visitor's conversation.
  2. Messages into Telegram

    A visitor's message arrives as a notification on your phone.

    1. BotFather issues it. Server environment only, and never logged.

      Files .env.example

    2. Site, conversation and the message. Enough to answer without opening anything else.

      Files src/telegram/send.js

    3. Telegram message identifier against conversation. This is what Phase 3 depends on.

      Files src/telegram/map.js

    done when · tick each as it passes
    watch out
    • The bot token is the whole of your bot's security and it appears in every API call. Keep it on the server only, never in the widget or any client, and rotate it immediately if it ever reaches a log you share.
  3. Replying by replying

    Replying to the bot's message in Telegram sends your answer to the visitor.

    1. A real certificate. Telegram will not deliver to plain HTTP and the failure is silent.

      terminal
      curl -F "url=https://your.host/tg/webhook" https://api.telegram.org/bot$TOKEN/setWebhook
    2. The update's reply target identifies the conversation. Without the mapping there is nowhere to send it.

      Files src/telegram/webhook.js

    3. Queue and deliver on reconnection, using the sequence number so nothing arrives twice.

      Files src/deliver.js

    done when · tick each as it passes
    watch out
    • Telegram requires HTTPS for webhooks and will not deliver to plain HTTP at all, and the same applies to loading a Mini App later. Get a certificate before you start this phase rather than debugging silence afterwards.
  4. Delivery that survives a dropped connection

    Nothing is lost or duplicated when a visitor's connection drops.

    1. Per conversation, with a unique constraint on conversation and sequence.

      Files src/messages.js

    2. An HTTP endpoint returning messages after a sequence, called before resuming the socket.

      Files src/backfill.js

    3. Buffer live messages during backfill and deduplicate by sequence. This window is where duplicates come from.

      Files test/reconnect.test.js

    done when · tick each as it passes
    watch out
    • Sequencing by timestamp breaks under concurrent writes and clock differences, and two messages in the same millisecond reorder or collide. Use a monotonic integer allocated by the database with a unique constraint, so a duplicate insert fails loudly rather than quietly.
  5. The Mini App console, if you need it

    A real thread list inside Telegram, with the webview quirks handled.

    1. Recompute the hash with your bot token and reject a mismatch. This is the authentication and it must be server side.

      Files src/miniapp/auth.js

    2. Use the stable viewport height Telegram provides, not viewport units, and apply both safe area insets.

      Files src/miniapp/layout.css

    3. Selection is disabled, so implement the affordance yourself and test it on a real phone.

      Files src/miniapp/copy.js

    done when · tick each as it passes
    watch out
    • The initialisation data Telegram passes must be verified by recomputing its hash on the server with your bot token. Trusting it unverified means anyone can claim to be any user.
    • Dynamic viewport units overshoot inside Telegram's webview, producing an interface that extends under the interface chrome. Size from the stable viewport height Telegram reports, and apply both safe area insets in fullscreen or your header sits under the status bar.
    • Text selection is disabled in the webview, so copying a message needs a long press handler you write yourself.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more customer support.