RapidSMS Applications

 

RapidSMS applications are Django apps which contain custom logic for processing incoming and outgoing messages. When the router receives an incoming or outgoing message, it triggers a series of phases through which its associated applications can process the message. Any number of RapidSMS applications can be used in a project.

Each RapidSMS application defines a class that extends from rapidsms.apps.base.AppBase, kept in the app.py submodule of a Django app. The Django app also contains models, views, and methods required by the application.

As an example, we might create a simple application that replies ‘pong’ after receiving the message ‘ping’:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 # In pingpongapp/app.py
from rapidsms.apps.base import AppBase
class PingPong(AppBase):
    def handle(self, msg):
        if msg.text == 'ping':
            msg.respond('pong')
            return True
        return False

After associating the PingPong application with the router, new incoming and outgoing messages received by the router are passed through the application for processing. All incoming ‘ping’ messages will receive a ‘pong’ reply. In general, the send and receive methods in the messaging api abstract the logic needed for passing messages to the router.

Application and router behavior in RapidSMS are intertwined. In this section, we focus on the behavior specific to applications, with references to some key areas where this behavior is tied to the router. For more information about routing messages through applications, see the router documentation.

 

Application Structure

A RapidSMS application is contained in a Django app. Each application defines a class that extends from rapidsms.apps.base.AppBase, kept in the app.py submodule of the Django app.

The router maintains a collection of associated applications through which to route incoming and outgoing messages. Application discovery is managed through the BaseRouter.add_app method. The default router, BlockingRouter, loads applications upon initialization by calling BaseRouter.add_app on each app listed in the optional apps argument or in INSTALLED_APPS.

 

Incoming Message Processing

The router receives each incoming message through its incoming method. In BaseRouter.receive_incoming, the message is passed sequentially to the router’s associated applications in each of five processing phases. Applications provide the code to execute each phase. The router provides hooks which allow an application to filter out a message, skip phases, or stop further processing.

Important

The order in which the router chooses applications to process messages is extremely important, because each application will have the opportunity to block subsequent applications from processing a message.

The logic for each phase is defined in a method of the same name in the AppBase class. By default, no action is taken at any phase. Each subclass may choose to override any of the default methods to use custom logic on incoming messages.

  1. filterOptionally abort further processing of the incoming message. The filter phase is executed before any other processing or modification of the incoming message. If an application returns True from this phase, the message is filtered out and no further processing will be done by any application (not even cleanup).

    Example: An application that filters out spam messages:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 from rapidsms.apps.base import AppBase
class SpamFilter(AppBase):
    def filter(self, msg):
        """Filter out spam messages."""
        if msg.text == "Congratulations, you've won a free iPod!":
            return True  # This message is probably spam and should not be
                         # processed any more.
        return False
  1. parseModify message in a way that is globally useful. This phase is used to modify the incoming message in a way that could be useful to other applications. All messages that aren’t filtered go through the parse phase of every application. No INSERTs or UPDATEs should be done during this phase.

    Example: An application adds metadata about phone number registration to each message.

  1. handleRespond to the incoming message. The router passes incoming messages through the handle phase of each application until one of them returns True. All subsequent apps will not handle the message.

    It is considered best practice to return True during the handle phase if the application responds to or otherwise alters the message. Although an application may return False in order to allow other applications to handle the message, remember that the default phase will execute if no application returns True during handle.

    As mentioned above, the order in which the router chooses to send messages to applications is very important. For example, you may wish to have ‘keyword’ applications (which look for a specific trigger word) handle a message before more general applications that use a regex to match possible text.

  1. defaultExecute a default action if no application returns True during the handle phase. For example, an application might want to provide additional help text or a generic response if no other application has handled the message. The application can return True from this method in order to prevent the remaining applications from executing their default stage.
  1. cleanupClean up work from previous phases.
  

Outgoing Message Processing

The router receives each outgoing message through its outgoing method. Messages are processed in a manner similar to incoming messages, except only one phase, outgoing, is defined. In BaseRouter.send_outgoing, the message is processed sequentially by the router’s associated applications. However, the applications are called in reverse order with respect to the order they are called in BaseRouter.receive_incoming, so the first application called to process an incoming message is the last application that is called to process an outgoing message. If any application returns False during the outgoing phase, all further processing of the message will be aborted.

The logic for the outgoing phase is defined in a method of the same name in the AppBase class. By default, no action is taken during this phase. Each subclass may choose to override the default method to use custom logic on outgoing messages.

 

Router Events: start and stop

For historical reasons, each application can provide start-up and shut-down logic in the start and stop methods, respectively. These methods are called from BaseRouter when the router is started or stopped. However, this behavior has never been enforced. A “stopped” router can still receive messages and will route them to applications, even “stopped” applications. As we move toward v1.0, we expect to remove these methods from BaseApp.

 

Scheduling tasks

If your application needs to run tasks asynchronously, either on-demand or on a schedule, you can of course use any mechanism that works in Django. The RapidSMS project recommends using Celery, and there are some advantages to using Celery in RapidSMS applications compared to other schedulers. See Using Celery for Scheduling Tasks

 

Contrib and Community Applications

There are many existing RapidSMS applications. The applications in rapidsms.contrib are maintained by core developers and provide broad-reaching functionality that will be useful to many developers. We also provide a directory of community-maintained RapidSMS applications that may be useful in your project.