Skip to content

developervariety/fedex-spotbuddy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FedEx AVR Telegram Bot

Automates FedEx AVR call workflows through Telegram using C#/.NET 10 and Twilio Programmable Voice.

What It Does

The bot runs predefined FedEx AVR DTMF flows so you spend less time in phone menus.

Telegram commands

Command Purpose
/start Welcome message and reply keyboard
/departing Terminal departure flow
/spot Customer/spot departure flow (needs pickup trailer saved from a prior /departing)
/arriving Terminal arrival flow
/transcript Last saved transcript (and call type / time) for this user
/recording Toggle capture + Gladia + Telegram WAV attachment for new calls (on, off, or status)
/setfedexid <id> Save or update FedEx ID

Current Behavior

  • Collects required values per flow in Telegram (ConversationService + prompts).
  • Persists per-user data to JSON (Storage:UsersFilePath, default Data/users.json).
  • Departing asks for: FedEx ID (if not already saved), truck number (or reuse), spot number, pickup trailer (stored for later /spot), then empty trailer. The DTMF sequence uses truck, FedEx ID, spot, and empty trailer; pickup trailer is persisted for the spot flow, not dialed in this flow.
  • Spot reuses truck when within the TTL and uses the saved pickup trailer from profile. If no pickup trailer exists, the bot tells you to run /departing first.
  • Truck reuse: if TruckNumberValidUntil is in the future, truck is reused for /departing, /spot, and /arriving. The 11-hour window is refreshed when a /spot call starts after you enter a truck number.
  • DTMF timing: Twilio sendDigits pauses use w (0.5s each). After a menu key (single IVR digit), the next segment is preceded by **w** (0.5s). After a data entry segment (truck/FedEx ID/spot/trailer/100#, etc.), the next segment is preceded by **ww** (1s). Steps are explicit in BuildSendDigits / JoinDtmfSteps in CallService. No leading pause before the first keypress. See examples/twiml-play-digits-example.xml for sample TwiML and digit strings.
  • Arriving flow end: /arriving now appends a final menu key 9 so the call exits cleanly after the arriving path.
  • Twilio Media Streams (wss/twilio/stream) are only started in TwiML when /recording is on; the WebSocket rejects streams for calls without capture. Inbound audio is captured as μ-law, transcribed via Gladia, and (when recording is on) sent to Telegram as a WAV after the outcome (skipped if the file would exceed Telegram’s size limit).
  • Telegram UX: While a call is active, one chat message is created and edited in place (editMessageText) as Twilio status (and stream connect) lines arrive—same mechanism as any bot “live” refresh, not a separate AI API. When the call finishes, that same message is edited again into the final HTML outcome; the WAV (if any) is a follow-up document. If the call ends before any non-terminal status (e.g. busy), the bot sends only the outcome message.
  • Gladia: with capture on, the app prefers live transcription (Gladia:UseLiveStreaming) via Gladia Live; if live setup fails, it falls back to buffering μ-law and uploading WAV for batch transcription. Stream auth uses Twilio:StreamAuthToken if set, otherwise Twilio:AuthToken.
  • Outcome is computed after the call ends (Twilio terminal status + stream stopped when capture is on): required phrase per flow (see Outcome:*RequiredPhrase), then failure keywords, then success keywords in the tail of the transcript, then status/duration heuristics (CallOutcomeEvaluator).
  • Recording defaults to on at process start; /recording only affects new calls.
  • **GET /health** returns JSON: Twilio config/callback URL, recording toggle, Gladia readiness (API key + base URL via batch transcriber), Telegram connection state.

Architecture

  • Telegram long polling (TelegramPollingService) handles commands and multi-step prompts.
  • UserStore reads/writes JSON profiles.
  • CallService creates outbound calls, serves TwiML, handles /twilio/status and the media WebSocket, runs transcription and outcome evaluation.
  • GladiaLiveTranscriber / GladiaTranscriber implement live vs upload transcription paths.

See PLAN.md for DTMF step tables and persistence detail.

Tech Stack

  • .NET 10 (ASP.NET Core minimal API)
  • Telegram.Bot (long polling)
  • Twilio Programmable Voice (REST + TwiML + Media Streams)
  • Serilog
  • JSON file persistence

Key Files

  • FedExAvrBot/Program.cs — bootstrap, DI, MapAppEndpoints
  • FedExAvrBot/Endpoints/AppEndpoints.cs/, /health, Twilio routes
  • FedExAvrBot/HostedServices/TelegramPollingService.cs — Telegram bot
  • FedExAvrBot/Services/CallService.cs — calls, TwiML, streams, DTMF BuildSendDigits / JoinDtmfSteps
  • examples/twiml-play-digits-example.xml — sample <Play digits="…"/> TwiML and pause rules
  • FedExAvrBot/Services/ConversationService.cs — in-memory prompt state
  • FedExAvrBot/Services/CallOutcomeEvaluator.cs — outcome rules
  • FedExAvrBot/Services/GladiaTranscriber.cs / GladiaLiveTranscriber.cs
  • FedExAvrBot/appsettings.json — defaults (override with user-secrets or environment variables)

Configuration

Use user-secrets or environment variables for secrets.

Twilio

  • Twilio:AccountSid
  • Twilio:AuthToken
  • Twilio:FromNumber
  • Twilio:FedExNumberrequired; destination number for the FedEx AVR IVR. Treat as a secret (do not commit); set via user-secrets or environment (e.g. Linux: Twilio__FedExNumber).
  • Twilio:PublicBaseUrl — public HTTPS base URL for webhooks (e.g. https://example.com)
  • Twilio:StreamAuthToken — optional; if set, must match the token parameter on the stream (otherwise AuthToken is used)

Gladia

  • Gladia:ApiKey
  • Gladia:ApiBaseUrl (default https://api.gladia.io)
  • Gladia:TimeoutSeconds
  • Gladia:PollIntervalMs
  • Gladia:UseLiveStreaming — use Gladia Live WebSocket when capture is enabled (default false in appsettings.json)
  • Gladia:EnableLiveAudioEnhancer — live session pre-processing
  • Gladia:LiveSpeechThreshold — 0–1, live pre-processing

Outcome

  • Outcome:MinCompletedDurationSeconds
  • Outcome:DepartingRequiredPhrase / SpotRequiredPhrase / ArrivingRequiredPhrase — if the normalized transcript contains this phrase, outcome is CERTAIN_SUCCESS
  • Outcome:SuccessKeywords — matched near the end of the transcript if no failure match
  • Outcome:FailureKeywords — any match → LIKELY_FAILED

Telegram

  • Bot:TelegramToken

Storage

  • Storage:UsersFilePath

Production (Linux / systemd)

Set secrets in the service environment (not in files committed to git). ASP.NET Core uses double underscores for nested keys, e.g. Twilio__FedExNumber, Twilio__AccountSid, Bot__TelegramToken. Example: EnvironmentFile=/etc/fedexavr.env with Twilio__FedExNumber=+1… in that file (mode 600, root-owned).

Run Locally

  1. Install .NET 10 SDK.
  2. Configure Twilio, Telegram, and (if using capture) Gladia.
  3. Run:
 dotnet run --project FedExAvrBot
  1. In Telegram, use /start or a flow command.

Health Endpoint

GET /health — JSON with twilio, recording, transcription (Gladia), and telegram sections.

Notes

  • Menu logic is tuned for the current FedEx AVR IVR (Mar 2026); if prompts change, update BuildSendDigits / JoinDtmfSteps in CallService.cs.
  • Twilio Media Streams error 31920 usually means the WebSocket handshake to /twilio/stream did not complete (101); check stream auth (401 / policy violation) and reverse-proxy WebSocket settings.
  • ops/fedexavr-site.conf and scripts/deploy-server.ps1 use placeholder hosts where appropriate; adjust for your domain.

About

Vibe-coded automated FedEx spot buddy

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors