Integrating Africa's Talking (SMS) in your Rails Application

anne46

Annastacia Kioko

Posted on April 12, 2024

Integrating Africa's Talking (SMS) in your Rails Application

Pre-requisites

  • Knowledge of Ruby on Rails

Africa's Talking

  • If you don't have an account, you can sign up here
  • Follow the registration process and activate your account.
  • By default AT( Africa's Talking) provides you with a sandbox account, you can use this to test your application.
  • On the menu on your left, click on settings and click on API Key, enter your password and click on generate API key.
  • This will generate an API key for your sandbox account, copy this key and keep it safe.
  • Default username for sandbox account is sandbox

Image description

  • For production, you need to first create a team, and within the team, you can create a new app. Give your app a name, username, select your country and currency then save.
  • To get your API key, click on the app you just created, and click on settings, then click on API Key, enter your password and click on generate API key.

Rails Application

  • In your Rails application, add the africastalking-ruby gem to your Gemfile and run bundle install
  • You can add the key and username to your environment variables or to your credentials file.
  • For this tutorial I'll be using the credentials file, to create a new credentials file, run EDITOR="nano" rails credentials:edit in your terminal.
  • Add the following to your credentials file:
  africas_talking:
    sandbox_username: 'sandbox'
    sandbox_api_key: 'your_sandbox_api_key'
    prod_username: 'your_prod_username'
    prod_api_key: 'your_prod_api_key'
Enter fullscreen mode Exit fullscreen mode
  • Make sure to substitute your_sandbox_api_key, your_prod_username and your_prod_api_key with your actual keys.
  • At this point, you can test if your credentials from the console, open your console and run the following:
require 'AfricasTalking'

username = Rails.application.credentials.africas_talking[:sandbox_username]
api_key = Rails.application.credentials.africas_talking[:sandbox_api_key]

@at=AfricasTalking::Initialize.new(username, api_key)

@at.sms.send('to' => '07xxxxxxxx', 'message' => 'message')
Enter fullscreen mode Exit fullscreen mode

NOTE

  • If you use sandbox credentials, the message isn't actually sent to the phone number, it's just a simulation.You can use the Launch a Simulator feature on the AT dashboard to see the message. Enter the phone number you want to send the message to, connect then send the message from your console.

  • A successful response might look like this

[#<StatusReport:0x00007fe8c9d47358
  @cost="KES 0.8000",
  @messageId="ATXid_e5b768b6d7bb4b5f45c4b40399fd1bab",
  @number="07xxxxxxxx",
  @status="Success"
>]
Enter fullscreen mode Exit fullscreen mode
  • A failure response might look like this
[#<StatusReport:0x00007fe8c9e9b880
  @cost="0",
  @messageId="None",
  @number="7xxxxxxxx",
  @status="InvalidPhoneNumber"
>]
Enter fullscreen mode Exit fullscreen mode
  • For Kenyan numbers, the valid formats are 2547xxxxxxxx or 07xxxxxxxx or +2547xxxxxxx

Implementation

  • Let's create an AT module which will handle all Africas Talking related tasks. Create a file in lib/modules/africastalking.rb and add the following:
require 'AfricasTalking'

class Africastalking
  def initialize
    if Rails.env.production?
      username = Rails.application.credentials.africas_talking[:prod_username]
      api_key = Rails.application.credentials.africas_talking[:prod_api_key]
    else
      username = Rails.application.credentials.africas_talking[:sandbox_username]
      api_key = Rails.application.credentials.africas_talking[:sandbox_api_key]
    end

    at = AfricasTalking::Initialize.new(username, api_key)
    @sms = at.sms
  end

  def self.send_sms(**options)
    new.send_sms(**options)
  end

  def send_sms(**options)
    message = options[:message]
    to = options[:to]

    @sms.send(
      'to' => to,
      'message' => message,
      'retryDurationInHours' => 1
    )
  end
end
Enter fullscreen mode Exit fullscreen mode
  • message and to are REQUIRED options while retryDurationInHours is optional, it specifies the number of hours for our message to be retried incase it's not delivered.
  • Other optional arguments are from which is the senders ID ( by default it is 'AFRICASTKNG') that you can register with Africa's Talking. To see what other options you can pass, check AT's Github Documentation
  • We initialize the Africastalking class with the username and api key from our credentials file, we then create a new instance of the Africastalking class and call the send_sms method with the message and to options.

Usage

  • In this example, I have set up a simple User model that only takes in a name and phone_number and a model Broadcast Message that has a message and a status. We will create a broadcast message and send it to all the users in our database.
  • In my broadcast_messages_controller.rb (or wherever you want to send the message from), I'll define an action send_sms that will send the message to all the users in our database.This action will in turn call a service object that will handle sending the message to all the users in our database.
def send_sms
  BulkSendSms.new(broadcast_message_id: @broadcast_message.id).call
end
Enter fullscreen mode Exit fullscreen mode
  • On the index page I list all the broadcast messages and have a button to send the message to all the users in the database.

Image description

  • I have created a service object BulkSendSms that will handle sending the message to all the users in our database. Create a new file in app/services and name it bulk_send_sms.rb and add the following:
require './lib/modules/africastalking'

class BulkSendSms

  attr_reader :broadcast_message_id, :broadcast_message, :users

  def initialize(params={})
    @broadcast_message_id = params[:broadcast_message_id] || params['broadcast_message_id']
    @success_count = 0
    @failure_count = 0
    @failure_reasons = []
  end

  def call
    puts '[Service] Bulk sending broadcast sms called..'

    load_broadcast_message
    load_users
    send_sms
    results
  end

  private

  def load_broadcast_message
    @broadcast_message = BroadcastMessage.find(broadcast_message_id)

    if broadcast_message.blank?
      puts 'No broadcast message found'
      return false
    end

    puts "Loaded broadcast message - #{broadcast_message.message}"
  end

  def load_users
    return if broadcast_message.blank?
    @users = User.all

    if users.empty?
      puts 'No users found'
      return false
    end

    puts "Loaded #{users.size} users"
  end

  def send_sms
    return if users.empty?

    users.each do |user|
      puts "Sending SMS to #{user.name} with phone - #{user.phone_number}"
      request = Africastalking.send_sms(
        message: Liquid::Template.parse(broadcast_message.message).render(
          "name" => user.name
        ),
        to: user.phone_number
      )

      puts request

      if request[0].status == 'Success'
        @success_count += 1
        puts 'SMS sent successfully'
      else
        @failure_count += 1
        puts 'Failed to send SMS'
        @failure_reasons << "#{request[0].status} - #{request[0].number} for #{user.name}"
      end
    end
  end

  def results
    @broadcast_message.update(status: :sent)

    total_count = users.size
    puts "Successfully sent to #{@success_count} out of #{total_count} users"

    if failure_count > 0
      puts "Failed to send to #{@failure_count} users, reasons: #{@failure_reasons.join(', ')}"
    end
  end

end
Enter fullscreen mode Exit fullscreen mode
  • I initiate success_count, failure_count and failure_reasons to keep track of the number of messages sent successfully, the number of messages that failed to send and the reasons for the failure respectively.
  • We then load the broadcast message and users from the database, if the broadcast message is not found or there are no users in the database, we return false.
  • In the send_sms method, we loop through all the users in the database and send the message to each user. We use the Liquid gem to parse the message and render the user's name in the message.We then call the Africastalking send_sms method we defined earlier in the Africastalking module and pass in the message and the user's phone number.
  • In the results method, we update the broadcast message status to sent and print out the number of messages sent successfully and the number of messages that failed to send and the reasons for the failure.Instead of logging you can create a report and send it to your email or the notifcation model or any other way you prefer.

Conclusion

  • You can now send SMS to all the users in your database using Africas Talking API. You can also extend this to send SMS to a specific group of users or to send SMS to a single user. You can also add more options to the send_sms method to allow for more customization of the message.
  • For improvements, we can use a worker to send the messages in the background to avoid blocking the main thread. We can also set up webhooks to get delivery reports and handle failed messages.

You can find the full code here

Thank you for reading this tutorial, I hope you found it helpful. If you have any questions or suggestions, feel free to reach out to me on Email or LinkedIn

💖 💪 🙅 🚩
anne46
Annastacia Kioko

Posted on April 12, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related