pyircbot/pyircbot/modules/SMS.py

151 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
.. module::SMS
:synopsis: SMS client script (requires Twilio account)
"""
from pyircbot.modulebase import ModuleBase, regex
import cherrypy
from threading import Thread
from twilio.rest import Client
class Api(object):
def __init__(self, mod):
self.mod = mod
@cherrypy.expose
def gotsms(self, *args, **kwargs):
"""
Twilio webhook listener
"""
"""
Example payload:
{'To': '+11234567890',
'ToCity': 'ALBERTVILLE',
'ToState': 'AL',
'ToZip': '35951',
'ToCountry': 'US,
'NumMedia': '1',
'MediaContentType0': 'image/jpeg',
'MediaUrl0': 'https://api.twilio.com/xxx',
'From': '+11234567890',
'FromCity': 'ROCHESTER',
'FromState': 'NY',
'FromZip': '14622',
'FromCountry': 'US',
'Body': 'Lol',
'NumSegments': '1',
'SmsStatus': 'received',
'SmsSid': 'xxx',
'SmsMessageSid': 'xxx',
'MessageSid': 'xxx',
'AccountSid': 'xxx',
'MessagingServiceSid': 'xxx',
'ApiVersion': '2010-04-01'}
"""
attachments = []
medias = int(kwargs["NumMedia"])
while medias > 0:
medias -= 1
attachments.append((kwargs["MediaContentType{}".format(medias)], kwargs["MediaUrl{}".format(medias)], ))
self.mod.got_text(kwargs["From"], kwargs["Body"], attachments=attachments)
yield ''
class SMS(ModuleBase):
def __init__(self, bot, moduleName):
ModuleBase.__init__(self, bot, moduleName)
self.apithread = None
self.twilio = Client(self.config["account_sid"], self.config["auth_token"])
def api(self):
"""
Run the webhook listener and block
"""
api = Api(self)
cherrypy.config.update({
# 'sessionFilter.on': True,
'tools.sessions.on': False,
'tools.sessions.locking': 'explicit',
# 'tools.sessions.timeout': 525600,
'request.show_tracebacks': True,
'server.socket_port': self.config.get("api_port"),
'server.thread_pool': 1,
'server.socket_host': '0.0.0.0',
'server.show_tracebacks': True,
'server.socket_timeout': 10,
'log.screen': False,
'engine.autoreload.on': False})
cherrypy.tree.mount(api, '/app/', {})
cherrypy.engine.start()
cherrypy.engine.block()
def onenable(self):
"""
If needed, create an API and run it
"""
if self.apithread is None and self.config.get("api_port") > 0:
self.apithread = Thread(target=self.api, daemon=True)
self.apithread.start()
def ondisable(self):
"""
Shut down the api
"""
cherrypy.engine.exit()
@regex(r'(?:^\.text\-([a-zA-Z0-9]+)(?:\s+(.+))?)', types=['PRIVMSG'])
def cmd_text(self, match, msg):
"""
Text somebody
"""
contact, message = match.groups()
contact = contact.lower()
if msg.args[0].lower() != self.config["channel"].lower():
return # invalid channel
if message is None:
return # TODO help text
if contact not in self.config["contacts"].keys():
return # TODO invalid contact
try:
self.twilio.api.account.messages.create(to=self.config["contacts"][contact],
from_=self.config["number"],
body=msg.trailing[7 + len(contact):].strip())
except Exception as e:
self.bot.act_PRIVMSG(msg.args[0], "Could not send message: {}".format(repr(e)))
else:
self.bot.act_PRIVMSG(msg.args[0], "Message sent.")
def got_text(self, sender, body, attachments=None):
"""
Webhook callback to react to a message
:param sender: number that sent the message, like +10000000000
:type sender: str
:param body: body text of the sms/mms
:type body: str
:param attachments: if mms, any attachments as a list of (mime, url) tuples
:type attachments: list
"""
name = None
for contact, number in self.config["contacts"].items():
if number == sender:
name = contact
if name is None:
name = sender
body = body.strip()
if body:
self.bot.act_PRIVMSG(self.config["channel"], "SMS from {}: {}".format(name, body))
if attachments:
for mime, url in attachments[0:3]:
self.bot.act_PRIVMSG(self.config["channel"], "MMS from {}: {} ({})".format(name, url, mime))