Background Job — Notifications (Slack + SMS, Rush alerting)
Background Job — Notifications (Slack + SMS)
Code: app/notify.py. Two transports (notify_slack, notify_sms) plus one assembler (notify_rush) and one written-but-unwired function (notify_release).
Trigger / schedule
Not scheduled — event-driven, in-request. The only live call site is app/rush.py (~line 289), at the end of the Rush RO create handler (POST /ro/rush), after the RO has committed:
notify.notify_rush(order_no=…, job_name=…, tech_name=…, lines_count=…, vendor_hint=…)
The whole call is wrapped in try/except in rush.py as well as inside notify.py, so a notification failure can never fail or roll back the RO.
notify_release() (text the requester "RO 799013 ordered - PO 1234 - Elliott") is complete but has no call site on purpose: IM2 never creates a real QB PO yet and po_buy_list.released_at is written nowhere, so there is no "PO released" event to hook.
Settings and inputs
Setting (app_settings, Settings screen) | Ships as | Read by |
|---|---|---|
rush_notify_slack | Yes | notify_rush → Slack |
rush_notify_sms | No (off until ClickSend auto-recharge is fixed) | notify_rush → SMS |
rush_notify_tech_sms | Yes | notify_release (unwired) |
Read via im2.setting_bool() (imported locally to avoid an im2↔notify import cycle).
Recipients: im2.rush_alert_contact — select phone from rush_alert_contact where active order by name, maintained on the Rush Alert Contacts admin screen. notify_release would read app_users.phone.
Message body: RUSH RO {order_no} - {job_name} - {n} line(s)[ - vendor: X] - from {tech}; the Slack copy appends IM2_PUBLIC_URL + /po/workbench?priority=R.
Environment (all in /opt/im2/env)
RUSH_SLACK_WEBHOOK_URL— Slack Incoming Webhook for #sa-logistics-mgmtCLICKSEND_USERNAME,CLICKSEND_API_KEY,CLICKSEND_SENDER_NUMBERIM2_PUBLIC_URL— used to build the workbench link
External calls
- Slack:
POSTthe webhook URL with{"text": …}, 10 s timeout, success = any 2xx. - ClickSend:
POST https://rest.clicksend.com/v3/sms/send, HTTP basic auth, 15 s timeout, one message object per number. - Phone normalisation: 10 digits →
+1XXXXXXXXXX, 11 digits starting with 1 →+…; anything else is counted failed without hitting the network.
Tables written
None. This module is write-free — no delivery log, no receipt table.
Failure behaviour / idempotency
- Every function degrades to a logged warning and returns
False/{"sent":…,"failed":…}/None. Nothing in this file may raise past its own boundary. - Missing credentials = a warning line ("message dropped") and a no-op; the screens behave identically either way.
notify_smsalways accounts for every number insentorfailed. If ClickSend's response shape can't be parsed, numbers are optimistically counted as sent rather than silently claimed either way for numbers that were confirmed.- Not idempotent and not retried — one attempt per event. A dropped alert is intentionally acceptable ("losing a text is fine; losing a request is not").
Logs to look at
journalctl -u im2 on the droplet, logger name im2.notify: notify_slack: RUSH_SLACK_WEBHOOK_URL not set — message dropped: …, notify_sms: ClickSend returned …, notify_rush: slack step failed for RO ….
Known limits / gaps
RUSH_SLACK_WEBHOOK_URLis NOT set in/opt/im2/env(as of 2026-09-05), so every Rush Slack alert currently logs "message dropped" and goes nowhere. The droplet has no Slack bot token; the fix is an Incoming Webhook from Jeff, or an interim Viktor cron pollingro_headerforentry_source='Rush'. Until then Rush ROs are only visible on/po/workbench?priority=R.- SMS is shipped off (
rush_notify_sms = No) pending ClickSend auto-recharge. - No delivery history is stored anywhere — you cannot prove after the fact that a text was sent, only that the process logged an attempt.
- Rush is the only event wired; nothing notifies on holds, posting failures, or intake errors.