Skip to content

Instantly share code, notes, and snippets.

@rgaudin
Created October 21, 2009 09:23
Show Gist options
  • Save rgaudin/214985 to your computer and use it in GitHub Desktop.
Save rgaudin/214985 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
import re
import urllib
from datetime import datetime
from select import select
from SocketServer import ThreadingMixIn
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import rapidsms
def _uni(str):
''' Make inbound a unicode str Decoding from utf-8 if needed '''
try:
return unicode(str)
except:
return unicode(str,'utf-8')
def _str(uni):
''' Make inbound a string Encoding to utf-8 if needed '''
try:
return str(uni)
except:
return uni.encode('utf-8')
class HttpServer(HTTPServer, ThreadingMixIn):
def handle_request (self, timeout=1.0):
# don't block on handle_request
reads, writes, errors = (self,), (), ()
reads, writes, errors = select(reads, writes, errors, timeout)
if reads:
HTTPServer.handle_request(self)
class Backend(rapidsms.backends.Backend):
''' Kannel backend for rapidSMS
Should work with most Kannel configuration
Although only tested with SMPP to SMS Aggregator.
Check if Kannel is UP on configuration by testing sendsms HTTP server'''
kannel_host = 'localhost'
kannel_port = 13013
kannel_username = 'kannel'
kannel_password = 'kannel'
def configure(self, host="localhost", port=8080, kannel_host='localhost', kannel_port=13013, kannel_username='kannel', kannel_password="kannel"):
self.kannel_host = kannel_host
self.kannel_port = int(kannel_port)
self.kannel_username = kannel_username
self.kannel_password = kannel_password
KannelHTTPHandler.kannel_host = self.kannel_host
KannelHTTPHandler.kannel_port = self.kannel_port
KannelHTTPHandler.kannel_username = self.kannel_username
KannelHTTPHandler.kannel_password = self.kannel_password
self.server = HttpServer((host, int(port)), KannelHTTPHandler)
self.type = "KANNEL"
# set this backend in the server instance so it
# can callback when a message is received
self.server.backend = self
KannelHTTPHandler.backend = self
self._slug = "kannel"
try:
# connect to Kannel sendsms server to test if Kannel is up.
url = "http://%s:%d" % (self.kannel_host, self.kannel_port)
res = urllib.urlopen(url)
except Exception, err:
raise Exception("Unable to connect to Kannel: %s" % err)
if not res.code in (200, 202, 400, 404):
raise Exception("Unable to connect to Kannel: %s" % res.code)
def run (self):
while self.running:
if self.message_waiting:
msg = self.next_message()
KannelHTTPHandler.outgoing(msg)
self.server.handle_request()
class KannelHTTPHandler(BaseHTTPRequestHandler):
kannel_host = 'localhost'
kannel_port = 13013
kannel_username = 'kannel'
kannel_password = 'kannel'
def respond(self, code, msg):
self.send_response(code)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(_str(msg))
def log_message(self, format, *args):
self.__class__.debug(format, *args)
def log_debug(self, format, *args):
self.__class__.debug(format, *args)
def log_error(self, format, *args):
self.__class__.error(format, *args)
def do_GET(self):
''' called on SMS arrival by Kannel
sender and message as parameter
http://server:port/+number/mymessage'''
def _plus(str):
return str.replace('%2B', '+')
# answer to / for testing purpose
if self.path == "/":
self.respond(200, "working")
return
# accepts /+digits/message
request_regex = re.compile(r"^/([\%B0-9]+)/(.*)")
match = request_regex.match(self.path)
if match:
# build the message
sender_id = _plus(match.group(1))
text = _str(match.group(2))
text = _plus(text.replace('+', ' '))
# get time
received = datetime.utcnow()
msg = self.server.backend.message(
sender_id,
urllib.unquote(text),
date=received
)
# send the message to router
self.server.backend.route(msg)
# send HTTP response
self.respond(200, '')
return
return
@classmethod
def debug(cls, format, *args):
cls.backend.debug(format, *args)
@classmethod
def error(cls, format, *args):
cls.backend.error(format, *args)
@classmethod
def outgoing(cls, msg):
'''Used to send outgoing messages through this interface.'''
cls.debug("sending message: %s to %s. %s chars" % (msg.text, msg.connection.identity, msg.text.__len__()))
# remove non digit from number
target = re.compile('\D').sub("", msg.connection.identity)
# urlencode for HTTP get
message = _str(msg.text)
msg_enc = urllib.quote(message)
# send HTTP GET request to Kannel
try:
url = "http://%s:%d/cgi-bin/sendsms?username=%s&password=%s&to=%s&from=&text=%s"\
% (cls.kannel_host, cls.kannel_port, cls.kannel_username, cls.kannel_password, target, msg_enc)
res = urllib.urlopen(url)
ans = res.read()
except Exception, err:
cls.error("Error sending message: %s" % err)
return False
if res.code == 202:
if ans.startswith('0: Accepted'):
kw = 'sent'
elif ans.startswith('3: Queued'):
kw = 'queued'
else:
kw = 'sent'
cls.debug("message %s: %s" % (kw, message))
elif res.code == 503:
cls.error("message failed to send (temporary error): %s" % ans)
else:
cls.error("message failed to send: %s" % ans)
@asuraphel
Copy link

Hi @rgaudin any specific reasons for implementing this instead of using a web framework like Flask?

@rgaudin
Copy link
Author

rgaudin commented May 11, 2022

@asuraphel I wouldn't remember since this was 13 years ago. I bet it was for simplicity and the fact that there is no additional dependency. Maybe it was a proof-of-concept that proved to be good-enough for my use case…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment