SMS Gateway - Kompletní dokumentace

Modul SMS Gateway nahrazuje Odoo IAP pro odesílání SMS. Místo cloudové služby používá fyzické Android telefony jako SMS brány. Každý telefon se spáruje s Odoo pomocí QR kódu a automaticky odesílající SMS z fronty.

Obsah

1. Architektura systému

┌────────────────────────┐ REST API ┌────────────────────────┐ │ Odoo Server │ pending / confirm │ SMS Gateway App │ │ │ ◀─────────────────▶ │ (Android telefon) │ │ • mailing.mailing │ heartbeat (60s) │ │ │ • sms.sms │ │ • SMS Queue Polling │ ┌──────────────┐ │ • sms.gateway.phone │ │ • DirectSms (nativní) │ ───▶ │ Příjemce SMS │ │ • phone.blacklist │ ◀───────────────── │ • Inbound SMS (STOP) │ │ +420 777... │ │ • link.tracker │ inbound / STOP │ │ └──────┬───────┘ └──────────┬─────────────┘ └─────────────────────────┘ │ │ /r/{code}/s/{sms_id} (UTM tracking + počítání kliknutí) │ └────────────────────────────── Klikne na link ◀───────────────────────────┘

Komponenty

Komponenta Popis
sms_gateway Odoo modul - spravuje telefony, frontu, API endpointy
sms-gateway-app React Native (Expo) aplikace pro Android
sms.gateway.phone Model reprezentující jeden registrovaný telefon
sms.sms Rozšířený o gateway_phone_id a gateway_state

2. Instalace Odoo modulu

Předpoklady

Postup

  1. Nainstalujte Python závislost:
    pip install qrcode[pil]
  2. Zkopírujte modul sms_gateway do adresáře Odoo addons:
    cp -r extra/sms/sms_modules/sms_gateway /path/to/odoo/addons/
  3. Restartujte Odoo server a aktualizujte seznam modulů
  4. Nainstalujte modul SMS Gateway přes Aplikace
Důležité: Modul sms_gateway je vzájemně exkluzivní s sms_httpsms. Pokud používáte sms_httpsms, odinstalujte ho před instalací sms_gateway.

3. Instalace mobilní aplikace

Stažení APK (doporučeno)

Stáhněte nejnovější release APK z GitHub Releases. Na telefonu otevřete stažený .apk soubor a povolte instalaci z neznámých zdrojů.

Poznámka: Aplikace není na Google Play. Používá oprávnění SEND_SMS a READ_SMS, která vyžadují speciální review. Sideloading APK z GitHub Releases je zamýšlený způsob distribuce.

Instalace nejnovějšího release přes ADB (skript)

Připojte telefon přes USB (se zapnutým USB laděním). Z kořene modulu sms-gateway (extra/sms):

# Nejnovější release
./scripts/install-release.sh

# Konkrétní tag
./scripts/install-release.sh v1.3.0

# Stáhnout znovu i když je v cache
./scripts/install-release.sh --force

Stáhne app-release.apk z GitHub release (cache podle tagu ve složce .release-cache/), nainstaluje přes adb install -r a udělí oprávnění pro SMS limit. Vyžaduje gh (přihlášený) a adb — žádný build toolchain není potřeba.

Předpoklady (build ze zdrojáků)

Build & instalace (jeden skript)

Připojte telefon přes USB. Z kořene modulu sms-gateway (extra/sms):

./scripts/setup.sh              # celý flow: závislosti, prebuild, build, instalace
./scripts/setup.sh --clean      # prebuild s --clean (po změně app.json / nativního kódu)
./scripts/setup.sh --no-install # jen build, přeskočit ADB instalaci

Skript bootstrapuje nvm + Node 20 (nainstaluje, pokud chybí), inicializuje submodul sms-gateway-app, najde Android SDK, nainstaluje závislosti, provede prebuild, sestaví release APK, nainstaluje ho přes ADB a udělí WRITE_SECURE_SETTINGS.

Build ručně

git submodule update --init --recursive   # stáhnout zdroje sms-gateway-app
cd sms-gateway-app

# Instalace závislostí
npm install

# Prebuild nativního projektu (nutné pro nativní moduly)
npx expo prebuild

# Spuštění na připojeném telefonu
npx expo run:android

# NEBO build release APK přes EAS
eas build --profile production-apk --platform android

Upload jako GitHub Release

gh release create v1.x.x ./build/*.apk \
  --repo Varyshop/sms-gateway-app \
  --title "v1.x.x" \
  --notes "Release notes"

Požadovaná oprávnění Android

Oprávnění Účel
SEND_SMS Odesílání SMS bez otevření aplikace
RECEIVE_SMS Čtení příchozích SMS (detekce STOP)
READ_PHONE_STATE Čtení čísel SIM karet
READ_PHONE_NUMBERS Čtení telefonního čísla
CAMERA Skenování QR kódu

4. Nastavení modulu v Odoo

Vytvoření gateway telefonu

  1. Přejděte do Email Marketing > SMS Gateway > Gateway Phones
  2. Klikněte Nový
  3. Vyplňte:
    Pole Popis Příklad
    Name Název telefonu Telefon 1 - O2
    Phone Number Hlavní číslo (mezinárodní formát) +420777123456
    Phone Number 2 Druhé číslo (dual SIM, volitelné) +420608987654
    Daily Limit Max SMS za den 500
    SMS per Minute Max SMS za minutu (rate limit) 100
    Heartbeat Timeout Po kolika minutách bez heartbeatu = offline 5
  4. Klikněte Generate API Key - vygeneruje se API klíč a QR kód
Tip: Můžete registrovat více telefonů. Systém automaticky rozděluje zátěž (least-loaded algoritmus).

5. Párování telefonu

  1. V Odoo otevřete formulář gateway telefonu a ujistěte se, že je vygenerovaný QR kód
  2. Na Android telefonu otevřete aplikaci SMS Gateway
  3. Přejděte na tab Nastavení
  4. Klikněte Naskenovat QR kód
  5. Nasměrujte kameru na QR kód zobrazený v Odoo
  6. Aplikace se automaticky spojí se serverem a začne posílat heartbeaty

QR kód obsahuje

{
    "type": "sms_gateway",
    "url": "https://vas-odoo-server.cz",
    "api_key": "nahodny_bezpecnostni_token"
}
Ověření: Po spárování by se měl stav telefonu v Odoo změnit na Online. Pokud ne, zkontrolujte síťové připojení a čas heartbeat timeout.

6. Nastavení SMS kampaně

Vytvoření kampaně

  1. Přejděte do Email Marketing > SMS Marketing
  2. Klikněte Nový
  3. Nastavte:

Příklad SMS textu

Ahoj {{ object.name }}! Mate 20% slevu na vse.
Nakupte na https://www.example.cz/akce
STOP pro odhlaseni
Automatická náhrada: Modul automaticky nahradí všechny URL v SMS za krátké trackovací odkazy (např. https://vas-server.cz/r/ABC123/s/42). Původní odhlašovací odkazy se nahradí textem STOP pro odhlášení.

Filtrování příjemců

Použijte Odoo domain filtr pro výběr příjemců:

Příklad filtru Popis
[("category_id", "in", [10])] Kontakty s kategorií ID 10
[("country_id.code", "=", "CZ")] České kontakty
[("customer_rank", ">", 0)] Pouze zákazníky
[("opt_out", "=", False)] Pouze ti, co se neodhlásili

7. Filtrování domény pro telefony

Každý gateway telefon může mít nastaven Partner Domain Filter. Tento filtr určuje, které SMS se budou přiřazovat danému telefonu na základě partnera příjemce.

Jak to funguje

  1. Když sms.sms._send() přiřazuje SMS do fronty, hledá dostupné telefony
  2. Pro každý telefon s nastaveným domain_filter se ověří, zda partner příjemce odpovídá filtru
  3. Telefon bez filtru přijímá SMS pro všechny partnery
  4. Telefon s filtrem přijímá pouze SMS kde partner odpovídá doméně

Příklady použití

Scénář Domain Filter Vysvětlení
Telefon pro VIP zákazníky [("category_id", "in", [10])] Pouze zákazníci s kategorií "VIP" (ID 10)
Telefon pro Prahu [("state_id.name", "=", "Praha")] Pouze zákazníci z Prahy
Telefon pro všechny (prázdné) Přijímá SMS pro všechny partnery
Telefon pro firmy [("is_company", "=", True)] Pouze firmy, ne fyzické osoby
Pozor: Pokud žádný telefon neodpovídá filtru pro daného partnera, SMS se označí jako chyba (failure_type='sms_server'). Doporučujeme mít alespoň jeden telefon bez filtru jako fallback.

8. Spuštění odesílání

Automatické odesílání (kampaň)

  1. Vytvořte a nastavte kampaň (viz sekce 6)
  2. Klikněte Send nebo Schedule
  3. Odoo vytvoří sms.sms záznamy ve stavu outgoing
  4. Cron job (SMS: SMS Queue Manager) pickuje outgoing SMS
  5. _send() přiřadí každé SMS gateway telefonu a změní stav na pending
  6. Mobilní aplikace si vyzvedne pending SMS přes API
  7. Aplikace odešle SMS a potvrdí stav (sent/error)

Manuální odesílání (Force Create SMS Queue)

Pokud se SMS nevytvořily automaticky:

  1. Otevřete kampaň
  2. Klikněte Force Create SMS Queue
  3. Systém vytvoří SMS záznamy pro všechny zbývající příjemce

Pozastavení kampaně

Kampaň lze pozastavit bez zrušení:

  1. Otevřete kampaň v režimu Sending
  2. Přepněte přepínač Paused na aktivní
  3. SMS ve frontě se nebudou odesílat, ale zůstanou přiřazeny
  4. Pro obnovení vypněte přepínač Paused
outgoing ──▶ pending ──▶ processing ──▶ sending ──┬──▶ sent (úspěšně odesláno) ✓ (přiřazeno (telefon (telefon │ telefonu) vyzvedl) odesílá) └──▶ error (chyba odeslání) ✗ Pokud je kampaň pozastavená: outgoing ──▶ (přeskočeno, zůstává outgoing)

9. Jak fungují odkazy a tracking

Automatická konverze odkazů

Všechny URL v SMS těle jsou automaticky převáděny na krátké trackovací odkazy:

Fáze Příklad
Původní text Nakupte na https://www.example.cz/akce
Po konverzi Nakupte na https://vas-server.cz/r/ABC123
Po přiřazení SMS ID Nakupte na https://vas-server.cz/r/ABC123/s/42

Jak tracking funguje

  1. Vytvoření odkazu: Odoo vytvoří link.tracker záznam s krátkým kódem a UTM parametry
  2. Přidání SMS ID: Ke každému odkazu se přidá /s/{sms_id} pro identifikaci konkrétního příjemce
  3. Kliknutí příjemce: Příjemce klikne na link ve SMS zprávě
  4. Server zaznamenává:
  5. Přesměrování: Server přesměruje na původní URL s UTM parametry:
    https://www.example.cz/akce?utm_campaign=Q1_2025&utm_medium=sms&utm_source=kampan

UTM parametry

Automaticky přidávané při přesměrování:

Parametr Hodnota Zdroj
utm_campaign Název kampaně mailing.mailing.campaign_id
utm_medium "sms" mailing.mailing.medium_id
utm_source Název zdroje mailing.mailing.source_id
Google Analytics: Díky UTM parametrům můžete sledovat návštěvnost z SMS kampaní přímo v Google Analytics (Acquisition > Campaigns).

10. Odhlášení a blacklist (GDPR)

Jak funguje odhlášení

Systém podporuje dva způsoby odhlášení z SMS kampaní:

Způsob 1: STOP zpráva (doporučený)

  1. Na konci každé SMS je text: STOP pro odhlášení
  2. Příjemce odpoví SMS zprávou obsahující slovo STOP
  3. Mobilní aplikace detekuje příchozí SMS se slovem STOP
  4. Aplikace pošle informaci na Odoo endpoint /sms-gateway/inbound
  5. Odoo přidá číslo příjemce na phone.blacklist
  6. Všechny budoucí kampaně automaticky přeskočí blacklistovaná čísla

Způsob 2: Webový odkaz (standardní Odoo)

Odoo standardně generuje odhlašovací URL ve formátu /sms/{mailing_id}/{trace_code}. Náš modul tyto URL nahrazuje textem "STOP pro odhlášení", protože:

GDPR povinnost: Podle GDPR musí být každá marketingová SMS zpráva opatřena možností odhlášení. Modul automaticky přidává "STOP pro odhlášení" na konec zprávy. Neodstranujte tuto informaci!

Blacklist model

Odoo používá model phone.blacklist z modulu phone_validation:

Kde najít blacklist

SMS Marketing > Configuration > Phone Blacklist

11. Statistiky a výkon kampaně

Sledované metriky

Metrika Popis Jak se měří
Sent Počet úspěšně odeslaných SMS sms.sms ve stavu sent
Delivered Doručené SMS = sent + opened + replied
Clicked Příjemci co klikli na odkaz mailing.trace s links_click_datetime
Click Rate Procento kliknutí (unikátní kliknutí / celkem odesláno) × 100
Bounced Neplatná čísla mailing.trace ve stavu bounce
Failed Chyba odeslání mailing.trace ve stavu error

Kde najít statistiky

  1. Přehled kampaně: Otevřete kampaň - v záhlaví jsou počítadla Sent/Clicked/Bounced
  2. Detailní statistiky: Klikněte na počítadlo pro zobrazení seznamu záznamů
  3. Link Tracker: Email Marketing > Link Tracker - přehled všech trackovacích odkazů s počtem kliknutí
  4. Gateway telefony: SMS Gateway > Gateway Phones - sent_today, sent_total, pending, error počítadla

Link Tracker - detailní analýza

Každý odkaz v SMS má vlastní link.tracker záznam:

12. Limity a rate limiting

Typy limitu

Limit Kde se nastavuje Popis
Daily Limit sms.gateway.phone Max SMS za den na telefon. Počítadlo sent_today se resetuje o půlnoci.
SMS per Minute sms.gateway.phone Max SMS za minutu. Aplikace počítá delay mezi SMS: 60000 / rate_limit ms.
Android SMS limit OS Android Android standardně omezuje na ~30 SMS za 30 minut. Může být změněn v nastavení.

Jak funguje rate limiting

  1. Server nastavuje rate_limit na každém gateway telefonu (např. 100 SMS/min)
  2. Mobilní aplikace získá rate_limit z heartbeat odpovědi
  3. Mezi každou SMS aplikace počká 60000 / rate_limit milisekund
  4. Pokud telefon dosáhne daily_limit, není mu přiřazena žádná další SMS
Android omezení: Na některých Android zařízeních je hardwarový limit ~30 SMS za 30 minut. Pro větší objemy doporučujeme použít více telefonů nebo změnit nastavení ADB:
adb shell settings put global sms_outgoing_check_max_count 10000

13. Dual SIM podpora

Systém podporuje telefony se dvěma SIM kartami:

Poznámka: Ne všechny Android telefony umožňují výběr SIM slotu pro odchozí SMS programově. Na některých zařízeních se použije výchozí SIM karta.

14. API reference

Všechny endpointy vyžadují hlavičku X-API-Key s platným API klíčem gateway telefonu.

POST /sms-gateway/heartbeat

Odeslání heartbeatu a získání stavu fronty.

// Request
{
    "phone_numbers": ["+420777123456", "+420608987654"],
    "battery_level": 85,
    "signal_strength": -70
}

// Response
{
    "success": true,
    "pending_count": {"+420777123456": 12, "+420608987654": 5},
    "rate_limit": 100
}

POST /sms-gateway/pending

Získání SMS čekajících na odeslání.

// Request
{"phone_numbers": ["+420777123456"], "limit": 20}

// Response
{
    "success": true,
    "sms_list": [
        {
            "id": 123,
            "phone_number": "+420999888777",
            "message": "Text SMS zpravy...",
            "uuid": "abc-123-def",
            "gateway_phone_number": "+420777123456"
        }
    ]
}

POST /sms-gateway/confirm/{sms_id}

Potvrzení stavu odeslání SMS.

// Request - uspech
{"status": "sent"}

// Request - chyba
{"status": "error", "error_message": "Permission denied"}

// Request - probihajici
{"status": "sending"}

// Response
{"success": true}

POST /sms-gateway/inbound

Nahlášení příchozí SMS (pro STOP detekci). Zpráva se uloží do modelu sms.gateway.inbound a pokud obsahuje STOP, číslo se přidá na blacklist.

// Request
{
    "from_number": "+420999888777",
    "message": "STOP",
    "to_number": "+420777123456"
}

// Response
{
    "success": true,
    "blacklisted": true,
    "partner_found": true
}

POST /sms-gateway/inbound-batch

Hromadné nahlášení více příchozích SMS najednou. Od v1.2.0 aplikace posílá všechny přijaté SMS (nejen STOP) na server. Server provádí deduplikaci podle kombinace from_number + to_number + message + received_at, takže opakované odeslání stejných zpráv (např. při rescan inboxu) nevytvoří duplicity.

// Request
{
    "messages": [
        {
            "from_number": "+420999888777",
            "message": "STOP",
            "to_number": "+420777123456",
            "received_at": "2026-03-22T10:30:00Z"
        },
        {
            "from_number": "+420111222333",
            "message": "Dekuji za informaci",
            "to_number": "+420777123456",
            "received_at": "2026-03-22T11:15:00Z"
        }
    ]
}

// Response
{
    "success": true,
    "processed": 2,
    "duplicates_skipped": 0
}
Chatter integrace: Každá příchozí SMS se automaticky zapíše do chatteru partnera v Odoo jako formátovaná HTML poznámka (blockquote s textem zprávy). Pokud zpráva obsahuje STOP, přidá se vizuální štítek STOP.

Model: sms.gateway.inbound

Všechny příchozí SMS se ukládají do modelu sms.gateway.inbound:

Pole Typ Popis
from_number Char Číslo odesílatele (E.164)
to_number Char Číslo gateway telefonu, na které SMS přišla
message Text Obsah SMS zprávy
received_at Datetime Čas příjmu zprávy na telefonu
partner_id Many2one Nalezený partner podle čísla odesílatele (může být prázdné)
is_stop Boolean Zda zpráva obsahuje klíčové slovo STOP
is_blacklisted Boolean Zda je číslo odesílatele na blacklistu
gateway_phone_id Many2one Odkaz na sms.gateway.phone

POST /sms-gateway/stats

Statistiky gateway telefonu.

// Response
{
    "success": true,
    "phones": [
        {
            "id": 1,
            "name": "Telefon 1 - O2",
            "phone_number": "+420777123456",
            "phone_number_2": null,
            "state": "online",
            "sent_today": 142,
            "daily_limit": 500,
            "sent_total": 4582,
            "pending_count": 23,
            "rate_limit": 100
        }
    ]
}

POST /sms-gateway/register-fcm

Registrace FCM tokenu pro push notifikace. Telefon pošle svůj FCM token po spárování nebo když se token obnoví.

// Request
{
    "fcm_token": "dK3xP9...dlouhy_firebase_token"
}

// Response - uspech
{
    "success": true,
    "message": "FCM token registered"
}

// Response - FCM neni povoleno
{
    "success": true,
    "message": "FCM not enabled, token ignored"
}

POST /sms-gateway/confirm-batch

Hromadné potvrzení stavu více SMS najednou. Efektivnější než jednotlivé /confirm/{sms_id} volání.

// Request
{
    "results": [
        {"sms_id": 123, "status": "sent"},
        {"sms_id": 124, "status": "sent"},
        {"sms_id": 125, "status": "error", "error_message": "Number unreachable"}
    ]
}

// Response
{
    "success": true,
    "processed": 3
}

14b. Marketing modely (segmenty a šablony)

Od verze v18.0.2.2.0 modul obsahuje modely pro SMS marketing přímo z mobilní aplikace. Admin definuje segmenty a šablony v Odoo, operátor je používá z appky bez přístupu do backendu.

Model: sms.marketing.segment

Definuje skupinu příjemců (např. „Neobjednali 3 měsíce“).

PoleTypPopis
nameCharNázev segmentu (překladatelný)
codeChar, uniqueSlug (no_order_3m, one_order_only, new_customers_30d)
domain_filterCharDeklarativní Odoo doména, např. [("country_id.code", "=", "CZ")]. Pokud je nastavena, použije se místo code-based logiky.
sequenceIntegerPořadí v seznamu
activeBooleanAktivní/neaktivní
descriptionTextPopis zobrazený v appce

Výchozí segmenty (data)

CodeNázevLogika
no_order_3mNeobjednali 3 měsíceSQL: partneři s objednávkou, ale žádnou za 90 dní
one_order_onlyJediná objednávkaSQL: partneři s přesně 1 potvrzenou objednávkou
new_customers_30dNoví zákazníci (30 dní)Domain: [("create_date", ">=", today - 30d)]

Klíčové metody

MetodaPopis
_get_domain() Vrátí základní Odoo domain segmentu. Použije domain_filter nebo dispatchuje na _domain_{code}().
_get_full_domain(phone, exclude_contacted_days) Single source of truth — skládá kompletní domain: segment + blacklist + phone filter + exclusion kontaktovaných.
_get_storable_domain(phone, exclude_contacted_days) Vrací domain vhodnou pro uložení do mailing_domain. Deklarativní segmenty se uloží jako čitelná doména; SQL segmenty se pre-resolví na ('id', 'in', [...]).
_resolve_recipient_ids(phone, exclude_days, limit) Resolví domain na seznam partner ID. Používá se pro počty a preview.
_get_recipient_count(phone, exclude_days) Spočítá počet odpovídajících partnerů.
_get_exclusion_domain(days) Vrátí deklarativní domain leaf ('stats_last_sms_days', '>', days) vylučující partnery kontaktované v posledních N dnech. Pokud je days <= 0, vrací prázdný seznam (filtr ignorován). Od v18.0.2.3.0 je plně deklarativní — žádný SQL na mailing_trace.
Domain compositing (v18.0.2.3.0+): _get_storable_domain() kombinuje 3 deklarativní filtry do jedné uložené domény:
  1. segment domain_filter
  2. phone domain_filter z sms.gateway.phone
  3. ('stats_last_sms_days', '>', exclude_contacted_days) ze šablony
Všechny 3 podmínky musí příjemce splnit současně. Doména je uložena v mailing.mailing.mailing_domain jako pure declarative Odoo domain — Odoo ji znovu vyhodnocuje při každém odeslání, bez runtime SQL dotazů. Exclusion leaf využívá computed field res.partner.stats_last_sms_days s vlastním _search_stats_last_sms_days() handlerem (v18.0.2.4.0+), který pro operátory >/>= vrací OR větev: partneři se stats row splňující datum nebo partneři bez stats row (('stats_id', '=', False)). Tím je zajištěno, že brand-new partneři bez jakékoliv SMS historie splňují podmínku „nekontaktován v posledních N dnech“. Předchozí fields.Integer(related='stats_id.last_sms_sent_days') varianta NEfungovala, protože Odoo převáděl search na LEFT JOIN přes O2m, který NULL řádky filtroval ven (viz changelog v18.0.2.4.0).

Model: sms.marketing.template

SMS šablona přiřazena ke gateway telefonu.

PoleTypPopis
nameCharNázev šablony
bodyTextText SMS (může obsahovat {{object.name}})
phone_idMany2one → sms.gateway.phonePřiřazený telefon
segment_idsMany2many → sms.marketing.segmentPovolené segmenty
default_limitInteger (100)Výchozí návrh počtu příjemců
max_limitInteger (500)Hard cap na počet příjemců
exclude_contacted_daysInteger (0)Vyloučit partnery kontaktované v posledních N dnech

Nová pole na mailing.mailing

PoleTypPopis
gateway_phone_forced_idMany2one → sms.gateway.phoneVynucený telefon pro všechny SMS v kampani
recipient_limitIntegerMax počet příjemců
marketing_template_idMany2one → sms.marketing.templateZpětný odkaz na šablonu
created_from_appBooleanVytvořeno z mobilní appky
pausedBooleanPozastavená kampaň (neodesílá se, ale SMS jsou ve frontě)

14c. Campaign API (mobilní appka)

Endpointy pro mobilní appku umožňující vytvořit a sledovat SMS kampaně bez přístupu do Odoo backendu. Všechny endpointy: POST, auth přes X-API-Key, JSON body/response.

POST /sms-gateway/campaign/templates

Vrátí šablony přiřazené k telefonu identifikovanému API klíčem.

// Response
{
    "success": true,
    "templates": [
        {
            "id": 1,
            "name": "Akce tento tyden",
            "body": "Ahoj {{object.name}}! Mate slevu 20%...",
            "segments": [
                {"id": 1, "name": "Neobjednali 3 mesice", "code": "no_order_3m"},
                {"id": 2, "name": "Jedina objednavka", "code": "one_order_only"}
            ],
            "default_limit": 100,
            "max_limit": 500,
            "exclude_contacted_days": 14
        }
    ]
}

POST /sms-gateway/campaign/filters

Pro každý segment spočítá počet odpovídajících příjemců (včetně phone domain filtru a exclusion).

// Request
{"template_id": 1}

// Response
{
    "success": true,
    "filters": [
        {
            "id": 1,
            "code": "no_order_3m",
            "name": "Neobjednali 3 mesice",
            "description": "Partneri s objednavkou ale zadnou za 90 dni",
            "recipient_count": 342
        }
    ]
}

POST /sms-gateway/campaign/preview

Náhled kampaně — počet příjemců a renderovaný text s ukázkovým partnerem.

// Request
{"template_id": 1, "segment_id": 1, "limit": 200}

// Response
{
    "success": true,
    "recipient_count": 200,
    "preview_text": "Ahoj Jan Novak! Mate slevu 20%...",
    "template_name": "Akce tento tyden",
    "segment_name": "Neobjednali 3 mesice"
}

POST /sms-gateway/campaign/create

Vytvoří mailing.mailing s plným trackingem. Přijímá volitelný custom_body (úprava textu z appky) a send_now přepínač.

// Request
{
    "template_id": 1,
    "segment_id": 1,
    "limit": 200,
    "custom_body": "Upraveny text SMS...",
    "send_now": true
}

// Response
{
    "success": true,
    "campaign_id": 42,
    "recipient_count": 200,
    "state": "sending"
}
send_now: Pokud send_now=false, kampaň se vytvoří ve stavu in_queue s paused=true. SMS jsou ve frontě, ale neodesílají se dokud operátor nestiskne „Odeslat ihned“ v appce (endpoint assign-sim).
Domain storage: Pro segmenty s deklarativním domain_filter se do mailing_domain uloží čitelná doména (např. [("country_id.code", "=", "CZ"), ...]). Pro SQL segmenty se pre-resolví na kompaktní [("id", "in", [1, 2, 3, ...])].

POST /sms-gateway/campaign/assign-sim

Přiřadí gateway telefon a SIM k SMS v kampani. Používá stejnou logiku jako akce „Send via Gateway“ v Odoo.

// Request — jedina SIM (nebo auto-prirazeni bez SIM cisla)
{
    "campaign_id": 42,
    "mode": "single",
    "sim_number": "+420777123456"
}

// Request — rozdeleni mezi vice SIM
{
    "campaign_id": 42,
    "mode": "split",
    "sim_numbers": ["+420777123456", "+420608987654"]
}

// Response
{
    "success": true,
    "assigned": 200,
    "message": "Assigned 200 SMS to phone"
}
Co assign-sim dělá:

POST /sms-gateway/campaign/list

Seznam kampaní vytvořených z appky pro daný telefon.

// Response
{
    "success": true,
    "campaigns": [
        {
            "id": 42,
            "name": "Akce tento tyden - 04.04.2026 10:30",
            "state": "done",
            "date_created": "2026-04-04 10:30:00",
            "total": 200,
            "sent": 195,
            "pending": 0,
            "error": 5,
            "clicked": 42,
            "total_clicks": 67,
            "order_count": 8,
            "revenue": 12400.0,
            "optout": 3
        }
    ]
}

POST /sms-gateway/campaign/status/{mailing_id}

Detail kampaně s marketingovými statistikami.

// Response
{
    "success": true,
    "id": 42,
    "name": "Akce tento tyden - 04.04.2026 10:30",
    "state": "done",
    "total": 200,
    "sent": 195,
    "pending": 0,
    "error": 5,
    "clicked": 42,
    "total_clicks": 67,
    "order_count": 8,
    "revenue": 12400.0,
    "optout": 3,
    "created_at": "2026-04-04 10:30:00"
}

Vysvětlení statistik

PolePopisZdroj
clickedUnikátní příjemci co kliklimailing.trace s links_click_datetime
total_clicksCelkový počet kliknutí (včetně opakovaných)link.tracker.click pres UTM campaign
order_countPočet objednávek z kampaněsale.order s campaign_id = UTM kampan
revenueCelkový příjem z objednávekSoučet amount_total z matchujících objednávek
optoutPočet odhlášených (STOP)mailing.trace ve stavu cancel

15. Řešení problémů

Problém Příčina Řešení
Telefon je stále Offline Heartbeat nedochází na server
  • Zkontrolujte síťové připojení telefonu
  • Ověřte správnost API klíče (znovu naskenujte QR)
  • Zkontrolujte URL serveru (musí být HTTPS)
  • Zvyšte heartbeat_timeout na gateway telefonu
SMS se neodesílají Aplikace nemá oprávnění SEND_SMS Otevře nastavení Android > Aplikace > SMS Gateway > Oprávnění > povolit SMS
SMS ve frontě ale žádný telefon nepřijímá Žádný telefon neodpovídá domain_filter Zkontrolujte domain_filter na gateway telefonech. Alespoň jeden by měl být bez filtru.
Všechny SMS mají stav error Žádný gateway telefon není online Ujistěte se že aplikace běží, je spárovaná a posílá heartbeaty
Daily limit dosažený příliš brzo Počítadlo sent_today Zvyšte daily_limit nebo přidejte další telefony. Reset je o půlnoci (cron).
Android blokuje SMS po 30 zprávách Hardwarový limit Androidu Použijte ADB příkaz: adb shell settings put global sms_outgoing_check_max_count 10000
STOP nepřidá číslo na blacklist Aplikace nemá oprávnění RECEIVE_SMS Povolte oprávnění RECEIVE_SMS v nastavení Android
Odkazy v SMS nefungují Špatná konfigurace web.base.url V Odoo nastavte správně System Parameters > web.base.url (musí být veřejně dostupné)
Kampaň se neoznačí jako Done Některé SMS stále pending Zkontrolujte, že všechny SMS jsou ve stavu sent nebo error. Možná je kampaň pozastavená.
SMS se neodesílají při zamknuté obrazovce Android ukončí aplikaci kvůli optimalizaci baterie
  • Vypněte Battery Optimization pro SMS Gateway (Nastavení > Aplikace > SMS Gateway > Baterie > Neomezovat)
  • Na MIUI/HarmonyOS: přidejte do Autostart a zakažte DuraSpeed / Battery saver
  • Ověřte, že AlarmManager má oprávnění SCHEDULE_EXACT_ALARM
STOP SMS chybí v blacklistu InboundSmsWorker selhal nebo není síť
  • Zkontrolujte, že WorkManager má přístup k síti (Nastavení > Datové připojení)
  • Ověřte oprávnění RECEIVE_SMS
  • V logu aplikace hledejte chyby InboundSmsWorker
  • Zkontrolujte, že endpoint /sms-gateway/inbound je dostupný z telefonu
  • Použijte tlačítko Znovu prohledat přijaté SMS v Nastavení > Služba pro retroaktivní odeslání všech přijatých SMS za posledních 30 dní
Příchozí SMS nedochází na server InboundSmsWorker používal špatnou autentifikaci (Authorization: Bearer místo X-API-Key)
  • Fix v1.2.0: InboundSmsWorker nyní používá správně hlavičku X-API-Key (stejně jako ostatní endpointy)
  • Aktualizujte aplikaci na v1.2.0+
  • Po aktualizaci použijte Znovu prohledat přijaté SMS pro odeslání chybějících zpráv z minulosti
FCM push nedochází FCM není správně nakonfigurováno
  • Ověřte sms_gateway.fcm_enabled = True v System Parameters
  • Zkontrolujte, že sms_gateway.fcm_credentials_json nebo sms_gateway.fcm_credentials_path je nastaveno
  • Ověřte instalaci: pip list | grep firebase-admin
  • Zkontrolujte, že telefon má platný FCM token (endpoint /sms-gateway/register-fcm)

Diagnostické příkazy (curl)

# Test heartbeat
curl -X POST https://vas-server.cz/sms-gateway/heartbeat \
  -H "Content-Type: application/json" \
  -H "X-API-Key: VAS_API_KLIC" \
  -d '{"phone_numbers": ["+420777123456"]}'

# Overeni pending SMS
curl -X POST https://vas-server.cz/sms-gateway/pending \
  -H "Content-Type: application/json" \
  -H "X-API-Key: VAS_API_KLIC" \
  -d '{"phone_numbers": ["+420777123456"], "limit": 5}'

# Test inbound STOP
curl -X POST https://vas-server.cz/sms-gateway/inbound \
  -H "Content-Type: application/json" \
  -H "X-API-Key: VAS_API_KLIC" \
  -d '{"from_number": "+420999888777", "message": "STOP", "to_number": "+420777123456"}'

16. FCM Push Notifications

Od verze v18.0.2.0.0 modul podporuje Firebase Cloud Messaging (FCM) pro okamžité probuzení telefonu při nových SMS ve frontě. Místo čekání na další polling interval telefon obdrží push notifikaci a okamžitě si vyzvedne čekající SMS.

Architektura

Odoo Server Google FCM Android App │ │ │ │ 1. Nová SMS ve frontě │ │ │──────────────────────────────▶ │ │ send_fcm_wake() │ │ │ (data message) │ 2. Push notification │ │ │───────────────────────────▶│ │ │ │ 3. FcmMessageHandler │ │ │ probudí aplikaci │ 4. GET /sms-gateway/pending │ │ │◀──────────────────────────────────────────────────────────│ │ 5. SMS data │ │ │──────────────────────────────────────────────────────────▶│ │ │ │ 6. Odešle SMS

Konfigurace na strane Odoo

System Parameter Typ Popis
sms_gateway.fcm_enabled Boolean Zapne/vypne FCM push notifikace. Výchozí: False
sms_gateway.fcm_credentials_json Text (JSON) Inline obsah Firebase service account JSON souboru. Má přednost před fcm_credentials_path.
sms_gateway.fcm_credentials_path Text (cesta) Absolutní cesta k Firebase service account JSON souboru na serveru. Použije se pokud fcm_credentials_json není nastaven.

Python závislost

pip install firebase-admin

Knihovna firebase-admin je automaticky nainstalována při instalaci modulu (deklarována v external_dependencies manifestu). Pokud instalace selže (např. chybí systémové knihovny), FCM funkce se tiše deaktivuje a telefony pokračují v polling režimu.

Implementace: tools/fcm_service.py

Hlavní funkce send_fcm_wake(phone) odešle FCM data message na registrovaný telefon. Data message (na rozdíl od notification message) probudí aplikaci i na pozadí bez zobrazení notifikace uživateli.

# Format FCM data message
{
    "type": "sms_pending",
    "phone_id": "7",
    "timestamp": "1710751200"
}
Pole Popis
type Vždy "sms_pending" — aplikace podle typu rozhodne akci
phone_id ID záznamu sms.gateway.phone v Odoo
timestamp Unix timestamp odeslání — pro deduplication na straně aplikace

Zpětná kompatibilita

Bezpečný upgrade: Telefony bez registrovaného FCM tokenu (pole fcm_token na sms.gateway.phone) pokračují v polling režimu bez jakékoliv změny chování. FCM je čistě aditivní vylepšení — žádný existující telefon nepřestane fungovat.

17. Pozadí a spolehlivost (Android)

Android agresivně omezuje pozadí aplikací kvůli úspoře baterie. Tato sekce dokumentuje strategie použité v SMS Gateway aplikaci pro spolehlivý provoz i při zamknuté obrazovce a Doze režimu.

AlarmManager vs ScheduledExecutorService

Pro pravidelný polling (heartbeat + vyzvedávání SMS) aplikace používá AlarmManager s přesným buzením namísto ScheduledExecutorService nebo Handler.postDelayed().

Přístup Chování v Doze MIUI/HarmonyOS
ScheduledExecutorService Zastaven po ~1 min v Doze Agresivně ukončen
Handler.postDelayed Zastaven po ~1 min v Doze Agresivně ukončen
AlarmManager.setExactAndAllowWhileIdle Probudí zařízení z Doze Funguje i na MIUI (s výjimkou baterie)
Oprávnění: Od Android 12 (API 31) je nutné oprávnění SCHEDULE_EXACT_ALARM nebo USE_EXACT_ALARM. Aplikace o něj žádá automaticky při prvním spuštění.

WorkManager pro příchozí SMS

Zpracování příchozích SMS (detekce STOP) používá WorkManager (InboundSmsWorker) namísto přímých HTTP volání z BroadcastReceiveru. Důvody:

WakeLock strategie

Aplikace používá PowerManager.WakeLock na dvou místech pro zabránění uspání CPU během kritických operací:

Komponenta WakeLock tag Trvání Účel
FcmMessageHandler sms-gateway:fcm-wake Max 60 sekund Drží CPU vzhůru během poll + odeslání SMS po FCM wake
SmsBroadcastReceiver sms-gateway:inbound-sms Max 30 sekund Drží CPU během zpracování příchozí SMS a enqueue do WorkManager

IMPORTANCE_HIGH notifikační kanál

Foreground service notifikace používá kanál s IMPORTANCE_HIGH. To zajišťuje, že Android nezabije službu ani při nedostatku paměti. Uživatel vidí trvalou notifikaci ve stavovém řádku (požadavek Androidu pro foreground service).

Výjimka z optimalizace baterie

Při prvním spuštění aplikace žádá uživatele o výjimku z optimalizace baterie (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS). Bez této výjimky může Android:

Kontrola: V aplikaci v sekci Nastavení je indikátor stavu optimalizace baterie. Pokud svítí červeně, uživatel musí ručně přidat výjimku.

Rescan Inbox (nativní metoda)

Aplikace od v1.2.0 obsahuje nativní metodu SmsModule.rescanInbox(days), která přečte SMS inbox za posledních N dní (výchozí: 30) a všechny nalezené zprávy odešle na server přes endpoint /sms-gateway/inbound-batch.

Tato funkce je dostupná uživateli přes tlačítko Znovu prohledat přijaté SMS v Nastavení > Služba. Server provádí deduplikaci, takže je bezpečné spustit rescan opakovaně.

// React Native volani
import { NativeModules } from 'react-native';
const messages = await NativeModules.SmsModule.rescanInbox(30);
// messages: Array<{from: string, body: string, date: number}>

// Nasledne se zpravy odeslou pres inbound-batch endpoint

WRITE_SECURE_SETTINGS oprávnění

Pro zvýšení nativního Android limitu na počet odchozích SMS je potřeba udělit aplikaci oprávnění WRITE_SECURE_SETTINGS. Toto oprávnění nelze udělit z GUI telefonu — vyžaduje ADB připojení:

# Primo pres ADB
adb shell pm grant com.varyshop.smsgatewayapp android.permission.WRITE_SECURE_SETTINGS

# Nebo pri buildu ze zdrojaku
yarn grant-permission
Bez tohoto oprávnění Android může blokovat odesílání po překročení nativního limitu (~30 SMS za 30 minut). S oprávněním aplikace automaticky zvýší limit.

Heartbeat safety net

I s FCM může dojít k situaci, kdy push notifikace nedorazí (např. dočasný výpadek Google služeb). Proto aplikace udržuje heartbeat safety net:

  1. Každý heartbeat vrací pending_count pro každé číslo
  2. Pokud pending_count > 0, aplikace okamžitě spustí poll cyklus
  3. Heartbeat interval je typicky 60 sekund — maximální zpoždění při výpadku FCM je tedy 1 minuta
  4. Tento mechanismus funguje nezávisle na FCM a zajišťuje doručení i bez push notifikací
FCM push (okamzite) ──▶ Poll + Send │ │ (pokud FCM selže) │ Heartbeat (60s) ──▶ pending_count > 0? ──▶ Poll + Send │ └── pending_count == 0 ──▶ Žádná akce
V případě jakýchkoli dotazů nebo potřebné pomoci s nastavením kontaktujte info@varyshop.eu nebo přímo vývojáře info@michalvarys.eu

18. Changelog

v18.0.2.6.0 / App v1.5.0 (2026-04-06)

v18.0.2.5.0 (2026-04-05)

v18.0.2.4.0 (2026-04-05)

v18.0.2.3.0 / App v1.4.0 (2026-04-05)

v18.0.2.2.0 / App v1.3.0 (2026-04-04)

v18.0.2.1.0 / App v1.2.0


SMS Gateway v18.0.2.6.0 • VaryShop • 2026