D
Engineering

Message Flow

Alur pesan dua arah — inbound dari WA gateway via callback_message, outbound via chat_outbox + Node engine change stream + BullMQ. Detail handler, koleksi, dan edge case.

Pesanan mengalir dua arah melalui backend-go. Inbound dari WhatsApp gateway via webhook callback_message. Outbound dari handler Go menulis ke chat_outbox, lalu Node engine mengirim ke WA gateway.

Inbound — POST /v1/callback_message

Dipanggil oleh WhatsApp gateway :5002 setiap pesan masuk. Handler: handlers/chat_callback.go:89 CallbackMessage.

text
WA gateway :5002
    │ POST /v1/callback_message
    ▼
CallbackMessage (chat_callback.go:89)
    │
    ├─ 1. Parse body → Payload
    ├─ 2. Resolve device via InstanceID (GetDevice)
    ├─ 3. Branch: Result==true && Report==false (inbound, bukan status)
    │     └─ Extract pesan, split number@server
    │        ├─ s.whatsapp.net → private chat
    │        └─ g.us          → group chat
    ├─ 4. Categorize (private/group) + auto-create group doc
    ├─ 5. Auto-create customer (GetCustomer → CreateCustomer)
    ├─ 6. First-chat / new-chat detection
    │     └─ isonnew = true jika chatList nil/new/close/pending
    ├─ 7. CS rotator dispatch (chat_callback.go:305)
    │     └─ h.svc.GetRotator(ctx, device.StoreId, instance) → returnid_cs
    ├─ 8. Update chat_history dengan CS terpilih
    ├─ 9. Build & save ChatList (create atau update)
    ├─ 10. Save inbound ke chat_inbox (chat_callback.go:529)
    │      └─ CreateChatInbox(newChatInbox)
    └─ 11. Bot dispatch (goroutine, chat_callback.go:549-561)
          └─ if HandleByBot && !is_start && !fromMe:
               go submitBot(newChatInbox) → createOutboxBot

Bot dispatch — chatbot engine

submitBot (chat_callback.go:1357) memanggil chatbot engine:

EndpointURLFungsi
Chatbot replyEngineBot + "api/chatbot-api" (:1637)Kirim context + dapatkan reply
Instruction/QnAEngineBot + "api/question" (:1763)Question dispatch

Bot dispatch dijalankan dalam goroutine — non-blocking. Reply dari bot ditulis ke chat_outbox via createOutboxBot, lalu Node engine mengirim ke WA gateway.

Outbound — chat_outbox + Node engine

Handler Go tidak langsung memanggil WA gateway. Ia menulis row ke chat_outbox, lalu Node engine memproses.

text
Handler (mis. SubmitMessage)
    │
    ├─ saveMessageMongo (chat_submit.go:425)
    │   ├─ Loop per phone number:
    │   │   ├─ CreateChatInbox (chat_submit.go:815) — simpan ke chat_inbox dulu
    │   │   └─ CreateChatOutbox (chat_submit.go:856) — simpan ke chat_outbox
    │   └─ Return resultChatData, chatInboxList
    │
    ▼
chat_outbox collection (MongoDB)
    │
    ▼
Node engine (terpisah, PM2)
    ├─ cron_message/dispatcher.js  — watch change stream (beta devices)
    │   └─ Enqueue ke Redis BullMQ queue (per-device)
    └─ cron_mdv2.js                — poll chat_outbox (legacy devices)
    │
    ▼
worker_message.js (BullMQ consumer)
    ├─ get_result_data.js — enrich payload
    └─ POST :5002 (WA gateway)
    │
    ▼
On success:
    ├─ chat_inbox.status_ack = "sent"
    └─ delete chat_outbox row

Field yang ditulis ke chat_outbox

chat_submit.go:820-844 — ChatOutboxStruct:

FieldSumberCatatan
IDuuidPrimary key
PesanpayloadKonten pesan
Nohpnormalized phoneFormat +62…
Waktutime.Now() atau payloadTimestamp
Jenis"keluar"Outbound marker
Instancedevice instance
TipemessageTypetext/image/video/document/audio/link/location
CaptionpayloadUntuk media
StatusAck"pending"Akan diupdate Node engine
IdUser, IdCscontext
Jalur"API"Via API (vs manual)
InboxIDchatInbox.IDLink ke chat_inbox row
PesanId, Noted, TempIdconditionalOverride dari payload

Dua mode pengiriman

ModeTriggerMechanism
Beta deviceschat_outbox insertMongoDB change stream → dispatcher.js → BullMQ → worker_message.js
Legacy devicespollingcron_mdv2.js poll chat_outbox per tick → POST :5002

Tenant variants

Pola yang sama berlaku untuk _admin dan _crm:

TenantHandler inboundHandler outboundOutbox write
defaultchat_callback.go:CallbackMessagechat_submit.go:saveMessageMongoCreateChatOutbox
adminchat_callback_admin.go:CallbackMessageAdminchat_submit_admin.go:saveMessageMongoAdminCreateChatInboxAdmin (admin_chat_inbox)
crmchat_callback_crm.go:CallbackMessageCrmchat_submit_crm.go:saveMessageMongoCrmCreateChatInboxCrm (CRM chat_inbox)

Status ACK lifecycle

Field chat_inbox.status_ack melacak status pengiriman:

NilaiArtiSetter
pendingMenunggu dikirimHandler Go (saat insert)
sentBerhasil terkirimNode engine (setelah WA gateway confirm)
errorGagalHandler Go (express mode) atau Node engine

Shared collections — kontrak implisit

Go API dan Node engine berbagi koleksi tanpa schema validation:

KoleksiGo menulisNode membacaNode menulis
chat_outbox✅ insert✅ poll/change stream❌
chat_inbox✅ insert (inbound + outbound row)—✅ update status_ack
chat_list✅—✅ update-chatlist via WA gateway

Langkah berikutnya