Using PythonAnywhere, Flask and smsgateway.me to make a free text bot
I put together a project a while back and this article is intended to share my experience with the tools and to provide a base for others to build on.
Here is what it does:
Ali sends an SMS to my phone, which is forwarded to my web server. My web server processes it the way I specify and sends a response back to the phone. This response is forwarded to Ali.
Stuff you’ll need:
1. Android phone
2. Extra SIM
3. Data connection for the device (WiFi or mobile data)
4. A web server
- I installed the SMS Gateway app to my device. This will send SMSs received on my phone to the app servers and then those servers will forward the message to my web server.
- You can use any server and language you are comfortable with. You should be able to access it from the public internet. This works without https being set but I think it helps with security to get a secure certificate. I set up a Flask server on PythonAnywhere because it’s free and I have relatively more experience coding in python.
- Now log in to the SMS Gateway site and go to the callbacks page. Add a new callback and write your servers public address in the ‘Action’ field. A few example are:
http://hanthaaloo.com/chicken_club. Also fill the ‘Secret’ field with a password of your choosing.
Now every time a message is received on your mobile phone, it should be sent to your webpage as a web request (This sometimes takes long). The format for the request is on the SMS Gateway website and I’ll elaborate on some of the problems below. Whatever your webpage responds to the request gets sent back to the phone and the phone forwards that to the sender.
This is the basic code below:
from flask import Flask,request
app = Flask(__name__)
## Your code goes here
The description for this is given below. Open the code in another window to follow along. If you just need it to run copy paste this in your PythonAnywhere application and if you can port this over to a different framework, this might not be of any use to you :)
The Flask library is to run the server and request library helps us see the request sent to our server. In our case, the request module will help us see what the SMS Gateway server sent us the message sent to our phone.
The sys module allows us some access to information to the server machine. I’ve used it elsewhere for the time and date of the server etc.
JSON is used by a number of web applications to exchange data. In our case though the request from SMS Gateway is not JSON.
requests is a module that makes sending requests to other sites very simple. If you do need to query some website for information, you can use requests.
def helper_function is a placeholder for any helper function you may need. This is where they worked for me.
@app.route(‘/’, methods=[‘POST’])says a lot. Every web server has a base name e.g. medium.com. But we could also write : medium.com/@the_jennitaur. Now things after the slash (in this case @the_jennitaur) require the web page to do different stuff. So both the urls mentioned require different actions from the server hosting them. In case the url is the base url,
@app.route(‘/’) is the path taken because there is nothing other than the base url. In case the other example is entered, we would need a different route. Simplistically, it would be
@app.route(‘/@the_jennitaur') and a method/function under it that would say look up our user directory for this user name etc.
Beneath all administrative stuff, we have our first function
def hello() . Notice that this is called when the base url is typed. Now the base url can have a bunch of data with it. And we need to extract this data using the request module imported earlier. The data sent from SMS Gateway looks like this originally:
"message": "hello world!",
"name": "Phyllis Turner",
The documentation isn’t very clear regarding what they send in a callback and I haven’t recorded it myself but this is the general format. Now during conversion to a url to be served to our web server, the data is put in a straight line. Now if you access
request.form[‘secret’], the secret is stored in the variable. But accessing contact id is a little different. The url sends it as ‘contact[id]’.
This wasn’t obvious to me. I debugged this by obtaining the raw url sent to my server using
a=request.get_data().decode(“utf-8”) then I returned variable ‘a’, containing the raw url (If you can print this somewhere that would be much less hassle, but I wasn’t able to). This sent me a message back with 140 characters of the url. I repeated this by splitting and discarding the part of the url I had sent to myself piecewise. Now I had the url but it had a strange encoding with %D marks etc. I found a url decoder and finally found how the data was sent.
Back to the code. Whatever you return something, it is sent as an SMS to the first sender in this chain via your Android phone. Now anyone with your number can send a message to your app and receive a reply. You can search for a city in the input message and send back its weather. You can find the latitude and longitude of a city. You can text yourself notes. You can get sunrise, sunset timings.
A few problems with this setup:
- If you use your personal phone, it will reply to every message. I don’t believe there is a good work around to this.
- It usually takes more time than we’re used to with instant messaging. There are times when it doesn’t forward messages from the phone but this is rare.
- Messaging cost may be a problem
- Everything has to be set up for it to work like applications we’re used to such as persistent memory etc
- The receiving phone has to be connected to the internet at all times for reliability
The advantages of this approach are the advantages of SMS:
- Works almost everywhere. Much better coverage than mobile data.
- Uses less power
- If messaging is free thats a plus
In conclusion, this can be used for applications where a wait can be tolerated, the input and output can be satisfactorily be displayed as text and where coverage at most times is required.
If there’s a problem anywhere, ask away. Thanks!