A Ruby on Rails application that places automated outbound phone calls through Twilio and holds a short, AI-generated voice conversation with the person who answers, using Google Gemini for the script and Twilio's text-to-speech for playback.
The core of the project is an autodialer: it works through a list of stored phone numbers and calls them one at a time with a configurable delay between calls.
The calling flow is:
- Dial:
AutodialerServiceselects allpendingphone numbers and, for each one,TwilioServiceplaces an outbound call via the Twilio REST API. The dialing loop runs in the background through a Sidekiq job (AutodialerJob). - Speak: when the callee answers, Twilio requests the
/twilio/voicewebhook. That endpoint asks Google Gemini (gemini-2.0-flash) to generate a short greeting/script, and returns TwiML that speaks it using Twilio's built-in TTS (thePolly.Aditivoice, configured for Hindi). - Listen & respond: the TwiML uses Twilio
<Gather>to capture the callee's speech or keypad input. That input is posted to/twilio/gather, which sends it back to Gemini for a conversational reply, speaks the reply, and then ends the call. - Track: Twilio posts call lifecycle events to
/twilio/status, which updates the matchingCallrecord (answered, busy, failed, no-answer, duration, etc.).
There is also an AI command endpoint (POST /calls/ai_command): it takes free-text like "call 8888888888", extracts the number (regex-based parsing first, with a Gemini fallback), and triggers a single call.
A web dashboard (/ and /dashboard) shows phone numbers, recent calls, and aggregate statistics.
The repository also bundles a separate content module that is independent of the calling feature: it generates programming blog articles (via Gemini or OpenAI gpt-4o-mini), stores them, creates embeddings with Gemini's text-embedding-004, and answers questions over them using a simple retrieval-augmented-generation flow (RagService) plus semantic search. This is a self-contained demo and not required for the autodialer to work.
- Language / framework: Ruby (~> 3.0), Ruby on Rails 7.0
- Telephony: Twilio (
twilio-ruby) for outbound calls, TwiML, and TTS - AI: Google Gemini (
gemini-2.0-flashfor scripts/replies,text-embedding-004for embeddings) via HTTParty; OpenAIgpt-4o-minifor blog generation - Background jobs: Sidekiq + Redis
- Database: SQLite3 (via Active Record)
- Server / assets: Puma, Importmap, Turbo, Stimulus, Sprockets
- Local webhooks: ngrok (to expose the dev server to Twilio)
- Ruby 3.0+ and Bundler
- SQLite3
- Redis (for Sidekiq)
- A Twilio account with a voice-capable phone number
- A Google Gemini API key (an OpenAI key is optional, only for blog generation)
- ngrok, or another public tunnel, so Twilio can reach your webhooks in development
Run ./check-prerequisites.sh to verify the local tooling.
-
Configure environment variables
cp .env.example .env
Then fill in
.env:TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_PHONE_NUMBERGEMINI_API_KEYOPENAI_API_KEY(optional, blog generation only)NGROK_URL(your public HTTPS tunnel URL for dev webhooks)
-
Install dependencies and prepare the database
./setup.sh # bundle install + db:create, db:migrate, db:seedor run the steps manually with
bundle installandbundle exec rake db:create db:migrate db:seed.Note: the seed data creates sample/test phone numbers. Review
db/seeds.rbbefore running it against real credentials.
-
Start Redis (
redis-server). -
Start the app and worker together with
./start.sh, or run them separately:bundle exec sidekiq # background dialing jobs bundle exec rails server # web UI + webhooks
-
(For live calls) start a tunnel so Twilio can reach the app, and set
NGROK_URL:ngrok http 3000
-
Open
http://localhost:3000, add phone numbers, and start the autodialer from the dashboard. Calls, statuses, and statistics update as Twilio posts back to the webhooks.
This is a personal/experimental project. A few honest caveats:
- Language and persona are hardcoded. The generated greetings and replies are written for a specific Hindi-speaking persona, and the TTS voice/language (
Polly.Aditi,hi-IN/en-IN) are set directly in the controllers rather than configurable. - No authentication. The dashboard and API endpoints are unauthenticated and intended for local/single-user use.
- Webhook security is minimal. CSRF protection is skipped on the Twilio webhooks (as expected for callbacks), but Twilio request-signature validation is not implemented.
- The dialer is a simple sequential loop. It calls numbers one at a time using an in-process delay inside a single background job; it is not built for high-volume or concurrent campaigns.
- SQLite + development-oriented config. Fine for testing; not tuned for production deployment.
- The RAG module is a demo. It recomputes embeddings during retrieval and is not optimized for large article sets.
Live calling requires valid Twilio credentials, a real destination number, and a publicly reachable webhook URL. Always comply with local calling/consent regulations before dialing real numbers.
Released under the MIT License.