D
Engineering

Broadcast Scheduler

Penjadwalan dan eksekusi broadcast WhatsApp. dazoapp hanya menyimpan konfigurasi & template — eksekusi sebenarnya di backend Go. Mencakup target audience, rate limiting, dan tracking.

Broadcast mengirim pesan WhatsApp massal ke kontak. dazoapp hanya menyimpan konfigurasi & template — eksekusi sebenarnya ditangani backend Go (url_api). dazoapp tidak punya job/cron untuk broadcast.

Ringkasan alur

text
CONFIGURE (di dazoapp):
  1. Buat BroadcastMessage (template: text + optional 1 file)
  2. Buat BroadcastSceduler:
     - pilih device (device.token = autentikasi ke engine WA)
     - pilih message (BroadcastMessage.id)
     - pilih target audience (all / select / order_product / order_status / tag / segment)
     - set schedule (once / daily / range) + time
     - set send_mode (slow / medium / fast)

EXECUTE (di backend Go):
  Frontend POST {url_api}send_broadcast
    → payload: id_sceduler, token (device.token), numbers, message, send_mode, ...
    → Go call engine WhatsApp untuk kirim per nomor
    → Go return {result: true}

TRACK (di dazoapp):
  BroadcastLog.status (bool: true=sukses, false=gagal)
  → Hanya 2 status. Tidak ada pending/delivered/read.
  → Tidak ada retry otomatis.

1. Broadcast model

Tiga model terkait (semua MongoDB, UUID string PK):

BroadcastMessage — template pesan

app/Models/BroadcastMessage.php

FieldTipeCatatan
titlestringmax 30 char
messagearray of objectEntry [0] = text, entry [1] = file (hanya 1 file per pesan)
media_type / file_typeenumtext, images, video, file, default
team_idstring
store_idstringScope multi-tenant

Struktur message entry:

json
[
  { "type": "text", "message": "Halo...", "original_name": null, "stored_name": null, "size": 0, "timestamp": "..." },
  { "type": "images", "message": "...", "original_name": "foto.jpg", "stored_name": "s3-key", "size": 102400, "timestamp": "..." }
]

Validasi upload (BroadcastMessageStoreRequest.php:35-48):

  • Image: max:10240 (10MB)
  • Video: mimes:3gp,mp4 max:20480 (20MB)
  • File: mimes:txt,xls,xlsx,doc,docx,ppt,pptx,pdf max:20480 (20MB)

Relasi: scheduler() hasMany BroadcastSceduler, log() hasMany BroadcastLog.

Ada duplicateBroadcast (BroadcastchatController.php:736-884) untuk duplikasi template (termasuk copy file S3).

BroadcastSceduler — jadwal broadcast

app/Models/BroadcastSceduler.php (catatan: nama model & controller typo “Sceduler”/“Scedular” — jangan perbaiki tanpa diminta).

FieldTipeCatatan
devicestring (FK Device.id)
message_idstring (FK BroadcastMessage.id)
typeenum (cast SchedulerTypeCast)once, everiday (daily), once-range, everidate (specific date), every-range
modeenum (BroadcastTarget)Target audience — lihat §3
statusbooleanActive/inactive
send_modeenum (SendMode)slow/medium/fast — lihat §4
is_filterbooleanFilter tag tambahan untuk segment
criteriastringand/or/exclude — kombinasi tag
broadcast_typestringlangsung (kirim segera) / jadwal
start_date, end_datestring (Y-m-d)Rentang tanggal
status_start_date, status_end_datestringFilter order status
timestring (HH:mm:ss)Waktu kirim
contact_list, product_list, status_list, tag_list, segment_listarrayTarget filter
team_id, store_idstringScope
onprogressbooleanSedang berjalan?
last_execute_date, last_manualrun_datedatetimeTracking

use SoftDeletes aktif. retrieved event (:28-35): auto-set status=false jika data_device.sender atau message.id kosong — disable otomatis saat data tidak lengkap.

BroadcastLog — log pengiriman

app/Models/BroadcastLog.php. Table fisik: broadcast_submit_log (:19).

FieldTipeCatatan
store_idstringScope
broadcast_idstringFK BroadcastMessage
id_scedulerstringFK BroadcastSceduler
customer_idstringFK Customer
statusboolean (cast)true = sukses, false = gagal
batch_idstringUUID untuk group batch
created_atdatetime

2. Scheduling

Endpoint

RouteHandlerFungsi
POST /api/broadcast/sceduler/save (:643)storeScheduler (:898)Create
PUT /api/broadcast/sceduler/update/{id} (:645)updateSceduler (:1005)Update
PATCH /api/broadcast/sceduler/change-status/{id} (:644)changeStatus (:653)Toggle active
DELETE /api/broadcast/sceduler/delete/{id} (:651)destroyDelete

Atur schedule

Tidak pakai cron expression. Pakai kombinasi field enum:

  • type (app/Enums/SchedulerType.php): once, everiday (daily), once-range, everidate (specific date), every-range
  • start_date + end_date (string Y-m-d) + time (HH:mm:ss)
  • broadcast_type: langsung (kirim segera) vs jadwal/lainnya

Job/cron di dazoapp

Dampak QUEUE_CONNECTION=sync

Tidak ada Job/Queue yang dipakai untuk broadcast di dazoapp. sync berarti job apapun (mis. UpdateChatlist, push-notif) berjalan sinkron dalam request. Untuk broadcast: tidak relevan karena eksekusi ada di sisi Go.

3. Execution

Job/Command yang memproses

TIDAK ADA di dazoapp. Frontend memanggil backend Go url_api + send_broadcast:

javascript
// resources/js/Pages/Broadcast/Schedular/SchedularCreate.vue:1342
const uri = `${livechatUri.value}send_broadcast`
// livechatUri = global.url_api (SchedularCreate.vue:541)

Payload ke engine Go

Dari SchedularCreate.vue:1314-1334 & PenjadwalanBroadcastTab.vue:778-800:

plaintext
id_broadcast, id_sceduler, token (device.token), numbers,
message (array), send_all (mode), is_filter, send_mode, criteria,
team_id, contact_list, product_list, status_list, tag_list,
segment_list, status_start_date, status_end_date,
running_mode: "broadcast_sceduler", batch_id (uuid)

Target audience

app/Enums/BroadcastTarget.php:

ModeDeskripsiFilter
allSemua customer
selectCustomer spesifikcontact_list individual
order_productCustomer yang beli produkproduct_list
order_statusCustomer dengan status orderstatus_list + status_start_date/status_end_date
tag_customerCustomer dengan tagtag_list + criteria (and/or/exclude)
segment_customerCustomer di segmensegment_list (+ optional filter tag via is_filter)

Hitung recipient: POST /api/broadcast/sceduler/count-recipients (routes/api.php:647). Query: Customer where store_id, category='private', status != archive + filter tag/segment.

Cara pesan dikirim

Backend Go yang call engine WhatsApp url_engine. dazoapp TIDAK langsung call url_engine untuk kirim broadcast — hanya untuk cek device info (DeviceService.php:33).

4. Rate limiting

Via send_mode (app/Enums/SendMode.php):

ModeDelay per nomor
slow60-120 detik
medium10-30 detik
fast3-8 detik

Rate limit dikirim ke backend Go di payload (send_mode field), dieksekusi di sisi Go.

5. Status & tracking

Status per-pesan

BroadcastLog.status — boolean. true = sukses, false = gagal. Hanya 2 status — tidak ada pending/delivered/read di model.

Per-scheduler: logSukses & logError relation. Di UI: total_success_log, total_error_log, total_log (BroadcastscedularController.php:585-612). Deduplikasi log per customer_id (:582-583).

Endpoint cek status

EndpointFungsi
GET /api/broadcast/scedulerList scheduler + device status realtime + count log
GET /api/broadcast/sceduler_paginateList paginated
GET /api/broadcast/show/{id}Detail pesan + scheduler terkait
GET /api/broadcast/log_statisticChart success/fail per tanggal (default 7 hari)
GET /api/broadcast/kuotaBroadcastLogStatus kuota harian

Retry untuk failed message

TIDAK DITEMUKAN mekanisme retry otomatis. BroadcastLog hanya catat status, tidak ada field retry_count/next_retry_at. Manual retry = user re-kirim lewat UI “Kirim Langsung” lagi.

6. Edge case

Cancel setelah start

Toggle status via PATCH /api/broadcast/sceduler/change-status/{id} — hanya enable/disable scheduler (tidak aktif = tidak dieksekusi cron di Go-side). TIDAK ADA cancel in-progress untuk batch yang sedang berjalan. Field onprogress ada di model, tapi tidak ada endpoint yang mengubahnya menjadi false untuk cancel. Kemungkinan onprogress diupdate oleh backend Go via callback (tidak ditemukan route callback-nya di routes/api.php).

Device disconnected saat broadcast berjalan

  • Cek realtime status device tiap request listSceduler (BroadcastscedularController.php:516-552) — call DeviceService::deviceInfo() ke engine url_engine + api/device-info?instance={id}
  • Auto-disable scheduler di retrieved event (BroadcastSceduler.php:28-35): jika data_device.sender atau message.id kosong, set status=false
  • Di listSceduler (:567-572): jika data_device.sender/message.id/deviceId kosong → auto-set status=false
  • Tidak ada handling untuk broadcast yang sudah mid-flight saat device disconnect — itu tanggung jawab backend Go

Limit jumlah pesan

  • Kuota harian per store lewat findAccessFeature($store->features, 'broadcast') (BroadcastchatController.php:900-928). Hitung BroadcastLog hari ini grouped by batch_id. Jika quantity ≤ current count → out_of_range
  • Limit ukuran file per pesan: image 10MB, video/file 20MB
  • Title max 30 char, message_text max 5000 char
  • Tidak ditemukan limit jumlah recipient per broadcast (selain kuota harian)

Referensi file

FilePeran
app/Models/BroadcastMessage.phpTemplate pesan
app/Models/BroadcastSceduler.phpJadwal + config (SoftDeletes)
app/Models/BroadcastLog.phpLog pengiriman (table broadcast_submit_log)
app/Http/Controllers/BroadcastscedularController.phpEndpoint scheduler
app/Http/Controllers/BroadcastchatController.phpEndpoint message template
app/Enums/SchedulerType.phpSchedule type enum
app/Enums/BroadcastTarget.phpTarget audience enum
app/Enums/SendMode.phpRate limiting enum
resources/js/Pages/Broadcast/Schedular/SchedularCreate.vueFrontend call ke Go