Telegram SMS-bridge via twilio

And here we go again. Even though my old project is still running nicely, the disappointing part it the need to regularly reboot the system as usb umts sticks seem to freeze after a week or so.

There is also the problem of running the system at home or whereever, but not on my servers in the computing center, so I had a look into various sms-gateway provider and decided to build a solution around twilio.

Even though twilio is nicely documented for the REST-API and provides you with many code examples anything handling responses is basically aimed at forwarding incomming replies to a webservice you would set up. No, I don’t want that ūüėČ .

Luckily, the API allows exactly what is needed – it is just not the kind of code example popping up if you search for ‘receiving SMS’.

Thus: here is the modification of the telepot-bot to use twilio instead of local hardware:

 

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import telepot
import re
from twilio.rest import TwilioRestClient 

class twsmsbot(telepot.Bot):
    
    def __init__ (self, uid, token, twacc, twauth, twsndr):
        super().__init__(token)
        self._mode = 0
        self._recipient = ""
        self._active = 1
        self._user = uid
        self.message_loop(self.handle)
        self._twclient = TwilioRestClient(twacc, twauth) 
        self._txsndr = twsndr
    
    def handle (self, msg):
        chat_id = msg['chat']['id']
        command = msg['text']
        if chat_id != self._user:
            self.sendMessage( chat_id, "unauthorized user")
            return 0
        res = re.match("^/(\w+)(\s[\+\w\d]+)*\Z", command, flags=0)
        if res:
            if      res.group(1) == "status":
                self.status()
            elif    res.group(1) == "setrcpt":
                self._recipient = res.group(2)
                self.sendMessage( chat_id, "recipient set to: %s" % self._recipient)
            elif    res.group(1) == "open":
                if self._recipient != "":
                    self._mode = 1
                    self.sendMessage( chat_id, "relay active")
                else:
                    self.sendMessage( chat_id, "set recipient first!")
            elif    res.group(1) == "close":
                self.sendMessage( chat_id, "relay deactived")
                self._mode = 0
            elif    res.group(1) == "terminate":
                self._active = 0
            else:
                self.sendMessage( chat_id, "unrecognized command!")
        else:
            if self._mode == 1 and self._recipient != "":
                self.sendsm ( command )
            else:
                self.sendMessage( chat_id, "relay not enabled")

    def status (self):
        self.sendMessage( self._user , "recipient: %s, relay status: %d" % (self._recipient, self._mode))
    
    def sendsm (self, msg):
        self._twclient.messages.create(to=self._recipient, from_=self._txsndr, 
        body=msg)

    def recvsm (self ):
        l = self._twclient.messages.list()
        for i in l:
            m = self._twclient.messages.get(i.name)
            self.sendMessage(self._user, "From: %s \n at: %s \n %s" % (m.from_,m.date_created,m.body))
            m.delete()

    
    def run (self ):
        while self._active == 1:
            self.recvsm ()
            time.sleep(5)
    
    
mybot = twsmsbot (<adminuid>, "bot-token", 
        "twilio-acc-token", 
        "twilio-auth-token", "sender")
mybot.run()

 

 

Telegram SMS bridge/gateway

Once in a while I am just considering doing things just because ¬†they can be done ūüėČ

Actually, the topic of this article might be helpful for others if they depend on SMS to control devices or refrain from carrying more than one phone around.

So, for this new weekend project, what do we need?

  • a UMTS stick (available on ebay starting at 5 EUR ūüėČ ), old phone with USB or serial connection or similar
  • a telegram bot (telepot)
  • a simcard which is active and can send SMs

Expected outcome: a bot that will relay SMs to a given cellphone number and print incoming SMs to a private telegram channel.

right now there are five commands:

  1. \setrcpt <number> (to set a receipient number we want to chat to obviously)
  2. \open (enable relay)
  3. \close (disable relay)
  4. \status  (if you are not certain which number is set or to retrieve other information)
  5. \terminate (terminate the script)

setting up smstools on ubuntu can be done as described here

so, as soon as you got sms tools running, all we need is some python script-foo ūüėÄ

required software:

apt install python-pip
pip install telepot
import sys
import time
import telepot
import re
import string
import random
import os

class smsbot(telepot.Bot):
    
    def __init__ (self, uid, token, spooldir):
        super(smsbot, self).__init__(token)
        self._mode = 0
        self._recipient = ""
        self._active = 1
        self._user = uid
        self._spooldir = spooldir
        self.notifyOnMessage(self.handle)
    
    def handle (self, msg):
        chat_id = msg['chat']['id']
        command = msg['text']
        if chat_id != self._user:
            self.sendMessage( chat_id, "unauthorized user")
            return 0
        res = re.match(r'^\\(\w+)\s?([\w\d]*)$', command, flags=0)
        if res:
            if      res.group(1) == "status":
                self.status()
            elif    res.group(1) == "setrcpt":
                self._receipient = res.group(2)
                self.sendMessage( chat_id, "recipient set to: %s" % self._recipient)
            elif    res.group(1) == "open":
                if self._recipient != "":
                    self._mode = 1
                    self.sendMessage( chat_id, "relay active")
                else:
                    self.sendMessage( chat_id, "set recipient first!")
            elif    res.group(1) == "close":
                self.sendMessage( chat_id, "relay deactived")
                self._mode = 0
            elif    res.group(1) == "terminate":
                self._active = 0
            else:
                self.sendMessage( chat_id, "unrecognized command!")
        else:
            if self._mode == 1 and self._recipient != "":
                self.sendsm ( command )
            else:
                self.sendMessage( chat_id, "relay not enabled")
    
    def id_generator(self, size=8, chars=string.ascii_uppercase + string.digits):
        return ''.join(random.choice(chars) for _ in range(size))
    
    def status (self): # needs some improvement
        self.sendMessage( self._user , "status")
    
    def sendsm (self, msg):
        output = "To: %s \n\n%s" % (self._recipient, msg)
        filename = os.path.join (self._spooldir, "outgoing", self.id_generator())
        fh = open (filename, "w")
        fh.write (output)
        fh.close()
    
    def recvsm (self ):
        for x in os.listdir(self._spooldir+"incoming/"):
            f = open(self._spooldir+"incoming/"+x, 'r')
            cont = []
            for line in f:
                cont.append(line)
            self.sendMessage(self._user, cont[0]+"\n"+cont[3]+"\n"+cont[12])
            f.close()
            os.remove(self._spooldir+"incoming/"+x)

    
    def checkerr ( self ): #some more work here
        for x in os.listdir(self._spooldir+"failed/"):
            self.sendMessage(self._user, "FAILED:")
            f = open(self._spooldir+"failed/"+x, 'r')
            ln=0
            for line in f:
                self.sendMessage(self._user, str(ln)+line)
                ln=ln+1
            f.close()
            os.remove(self._spooldir+"failed/"+x)
    
    def run (self ):
        while self._active == 1:
            self.recvsm ()
            self.checkerr ()
            time.sleep(5)
    
    
mybot = smsbot (<your_uid_here>, "<telepot_token_here", "/var/spool/sms/")
mybot.run()

run that script and enjoy ūüėČ

smsrelay