This project aims to provide a way to receive and send SMS messages from an application. SMS aggregators use HTTP endpoints to send and receive messages. This allows you to receive messages from a variety of aggregators and to send those messages back in a synchronous or asynchronous way. It also provides logging and the ability to replay messages from an administrative console. It uses MySQL for logging and Beanstalkd for queueing.
The currently supported aggregators are
-
CellTrust – www.celltrust.com
-
Clickatell – www.clickatell.com
-
mBlox – www.mblox.com
-
MX Telecom – www.mxtelecom.com/us
-
txtNation – www.txtnation.com
-
Textmarks – www.textmarks.com
-
Unwired Appeal – www.unwiredappeal.com
Receiving messages and sending replies happens over synchronous HTTP. All messages are normalized down to four fields: from
, to
, source
and body
. Optionally, there is a carrier_id
for aggregators that are carrier aware.
Message Gateway supports interacting with these aggregators in an synchronous or asynchronous way. In the synchronous model, here is how a message is processed.
Aggregator Message Gateway Your site ========== =============== ========= Send msg from mobile ====\ \===> Normalize Message =====\ Do something with \====> the message. Respond normally /===/ Denormalize <=====/ /===== message and Receive <===/ reply response
In this model, replying with non-empty body to the request from the message gateway will send the message back to the client.
In asynchronous processing, the message comes in and uses queues for processing. Here is how it looks:
Aggregator Q Message Gateway Q Your site ========== =============== ========= Send msg from mobile =====>O | | Normalize =============> Do something with Respond w/ <-----O======> Message the message. Respond 200 O==== normally | | Denormalize <========O Receive <============ message and response send to aggregator
In the asynchronous model, a separate sending processor has to be setup, separate from the endpoint for incoming messages. The currently supported asynchronous senders are:
-
CellTrust – www.celltrust.com
-
Clickatell – www.clickatell.com
-
mBlox – www.mblox.com
-
MX Telecom – www.mxtelecom.com/us
-
txtNation – www.txtnation.com
-
Unwired Appeal – www.unwiredappeal.com
As well, there is another means of sending, SMTP. Many carrier support (as well, all major US carriers) sending messages by email. You can specify SMTP as a message source provided you configure it with a valid SMTP server.
When you add an outbound message sender, Message Gateway assumes that you want to process messages asynchronously.
Outbound message =======> Outbound SMS Sending Endpoint (SmsSendingEndpoint) ====> Beanstalk Queue for later processing
| | Appropriate Sender Subclass < ==== 0
Message Gateway is informed of outbound messages by an HTTP post from your app to the sending interface URL (set up with HttpRouter - see below)
You can configure an admin interface which will give you a high level view of messages processed, and the success or failure of every message. If a message fails, you can attempt to replay it (either to the aggregator or to your site). As well, you can simulate a message from this panel.
Configuration right now takes place in a rackup file. Here is an example configuration file with annotation explaining what everything does:
require 'message_gateway' gateway = MessageGateway.new('my gateway', "http://127.0.0.1:3000/interpret") # This takes a name for display purposes and an endpoint to send messages to. gateway.beanstalk('127.0.0.1') # This sets the beanstalk host (and port) for use as a queue gateway.logger = MessageGateway::MessageLogger.new(:adapter => "mysql", :host => "127.0.0.1", :database => "message_gateway", :username => "root", :password => "") # this configures mysql for use as a logger. gateway.outbound(:unwired_appeal, '99999') { |out| # This configures an outbound message sender. The first parameter is the adapter to use out.default_from = '99999' # and the second parameter is an identifier used to identify it. out.event_id = "1111" # Every aggregator has its own configuration parameters. See the rdocs for out.password = "12345678" # each adapter for all its options. } end_point = gateway.sms_sending_endpoint # This creates a sending endpoint for initiating sms messages end_point.default_source = '99999' # without having to receive one first. run HttpRouter.new { # This creates your endpoints: add('admin*').to gateway.admin # For the admin interface post('send').to end_point # For the sending interface add('sms/incoming.sms').to gateway.inbound(:sync, '99999').parser(:unwired_appeal) # this creates a receiving endpoint } # The endpoint using the synchronous processing model and the # unwired appeal parser.
Once you’ve configured this, you can use thin to run the subsequent rackup file.
Once you’ve set up an SMS sending endpoint, you can send messages to it. Simply post data with the following fields:
-
to – The number you’re sending to
-
body – The contents of the message
-
source – The identifier of the outbound processor to use. This is the name you’ve assigned the source when you set it up. (This is optional, you can configure a default source)
“Post data where?”, one might ask: To the route that calls the SMS sending end point… in the above rackup example, it’s the
post('send').to end_point
line
Once you’ve set up an inbound endpoint (the HttpRouter block above adds(‘sms/incoming.sms’), as an example), the mobile agreegator will POST to that URL. See the Processor rdocs for more information here.
We’ll call this the “message received application logic callback”, for lack of a better name.
Your “message received application logic callback” will recieve the following parameters:
1. to - reciever of the SMS message 2. from - sender of the SMS message 3. body - the body of the message
In the “Synchronous processing” section of this document, the flow of this was described, but to put this into a little more perspective:
** The response your "message received application callback" returns will be returned to the Mobile Aggregator. As such, it should conform to their guidelines about the format of this message. (For example, Twilio wants an XML-like document returned, and from this document you can send a text message back in reply to your user **
There are some limitations. For one, using a rackup file is a bit awkward for configuration. For another, there is no way to try several aggregators to see which can send a message. As well, there is currently no support for delivery receipts.
You are running the tests before trying any of this, right?
spec/spec_helper.rb defines MessageGateway.default_logger. This object also keeps the database connection information.
By default the test suite uses mysql, with username root and no password, to connect to a database named “message_gateway_test”
Because of the high dependance on EventMachine, Message Gateway assumes your server is an EventMachine compliant server (aka: thin), AND has already started the EventMachine reactor.
If you want, I suggest setting up Airbrake for this app.
In your rackup file, configure Airbrake as the documentation says, then
use Airbrake::Rack
This can go outside the HttpRouter.new block.
This Airbrake middleware will catch errors going into processing. With the Asynchronous processor, this includes breaking the message apart into Message Gateway’s normalized message format. However, sending messages to the message received application callback is not part of this request/response cycle.
Errors from the message received application callback will be logged as an event (thus allowing the incoming SMS message to be replayed).
This gem is managed via Bundler, which has created the skeleton for us in addition to providing Rake tasks related to installing and releasing the gem.
This software is distributed under GNU General Public License (www.gnu.org/licenses/gpl.html).