Integrating Africa's Talking (SMS) in your Rails Application
Tags: africastalking, sms, rails, tutorial
Published: 2024-04-12 10:18:49

#### Pre-requisites
- Knowledge of Ruby on Rails

#### Africa's Talking

- If you don't have an account, you can sign up [here](https://account.africastalking.com/auth/register)
- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gt3l2z76q2advjoqv2ht.png)

- 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:

```yml
  africas_talking:
    sandbox_username: 'sandbox'
    sandbox_api_key: 'your_sandbox_api_key'
    prod_username: 'your_prod_username'
    prod_api_key: 'your_prod_api_key'
```

- 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:

```ruby
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')
```
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](https://developers.africastalking.com/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
```ruby
[#<StatusReport:0x00007fe8c9d47358
  @cost="KES 0.8000",
  @messageId="ATXid_e5b768b6d7bb4b5f45c4b40399fd1bab",
  @number="07xxxxxxxx",
  @status="Success"
>]
```
- A failure response might look like this
```ruby
[#<StatusReport:0x00007fe8c9e9b880
  @cost="0",
  @messageId="None",
  @number="7xxxxxxxx",
  @status="InvalidPhoneNumber"
>]
```
- 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:

```ruby
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
```
- `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](https://github.com/AfricasTalkingLtd/africastalking-ruby)
- 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.

```ruby
def send_sms
  BulkSendSms.new(broadcast_message_id: @broadcast_message.id).call
end
```
- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c3nfdf32155n3j5c2cke.png)

- 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:

```ruby
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
```
- 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](https://github.com/Annastacia-dev/africas_talking_rails)

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](mailto:annetotoh@gmail.com) or [LinkedIn](https://www.linkedin.com/in/annastacia-mumbua)