# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2011 Nexedi SA and Contributors. All Rights Reserved. # Francois-Xavier Algrain <fxalgrain@tiolive.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## """Receive or send SMS""" #Import python module import urllib from DateTime import DateTime #Import Zope module from AccessControl import ClassSecurityInfo, \ Unauthorized from AccessControl.SecurityManagement import getSecurityManager, \ setSecurityManager, \ newSecurityManager import zope.interface from zLOG import LOG, INFO from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Security.ERP5UserManager import SUPER_USER #Product Module from Products.ERP5ShortMessage.Errors import SMSGatewayError class MobytGateway(XMLObject): """Mobyt SMS Gateway Implementation""" meta_type='Mobyt Gateway' portal_type = 'Mobyt Gateway' security = ClassSecurityInfo() add_permission = Permissions.AddPortalContent zope.interface.implements(interfaces.ISmsGateway) # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properi ties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.Reference , PropertySheet.SMSGateway ) api_url = "http://multilevel.mobyt.fr/sms" security.declarePublic('getAllowedMessageType') def getAllowedMessageType(self): """List of all message type""" return ['text','multitext', 'wappush', 'ucs2', 'multiucs2'] security.declarePrivate("_fetchSendResponseAsDict") def _fetchSendResponseAsDict(self,page): """Page result is like Key=value in text format. We transform it to a more powerfull dictionnary""" result = dict() lines = page.readlines() assert len(lines) == 1, "Multi lines response is not managed %s" % lines line = lines[0] parts = line.split(' ') #Format is 'Status Message' result['status'] = parts[0] result['status_info'] = ' '.join(parts[1:]) return result security.declarePrivate("_fetchStatusResponseAsDict") def _fetchStatusResponseAsDict(self,page): """Page result is like Key=value in text format. We transform it to a more powerfull dictionnary""" result = dict() lines = page.readlines() #First line is special : CSV column title or error inform line = lines[0] if line[0:1] == "KO": result['status'] = "KO" result['status_info'] = line[2:] return result def _cleanText(s): return s.replace('\r','').replace('\n','') column_name_list = line.split(',') column_count = len(column_name_list) #Clean last colum column_name_list[-1] = _cleanText(column_name_list[-1]) result['status'] = "OK" row_list = [] #Batch other line to get all status for line in lines[1:]: row = {} column_value_list = line.split(',') column_value_list[-1] = _cleanText(column_value_list[-1]) for i in range(0,column_count): row[column_name_list[i]] = column_value_list[i] row_list.append(row) result['status_info'] = row_list return result security.declarePrivate("_transformPhoneUrlToGatewayNumber") def _transformPhoneUrlToGatewayNumber(self,phone): """Transform url of phone number to a valid phone number (gateway side)""" phone = phone.replace('tel:', '').replace('(0)','').replace('-','') # Check that phone number can not be something not existing assert not(phone.startswith('99000')) return phone security.declareProtected(Permissions.ManagePortal, 'send') def send(self, text,recipient,sender=None, sender_title=None, message_type="text",test=False,**kw): """Send a message. Parameters: text -- message recipient -- phone url of destination_reference. Could be a list sender -- phone url of source sender_title -- Use it as source if the gateway has title mode enable message_type -- Only 'text' is available today test -- Force the test mode Kw Parameters: quality -- Quality of the SMS (default,n) Return message id """ #Check messsage type if message_type not in self.getAllowedMessageType(): raise ValueError, "Type of message in not allowed" #Check message qualit quality = kw.get('quality','n') #Allow sender personalization and status of SMS assert quality in ['n','l','ll'], "Unknown quality : '%s'" % quality #Recipients if not isinstance(recipient, str): recipient_count = len(recipient) recipient = ",".join([self._transformPhoneUrlToGatewayNumber(x) for x in recipient]) else: recipient = self._transformPhoneUrlToGatewayNumber(recipient) recipient_count = 1 if recipient_count > 1: base_url = self.api_url + "/batch.php" #Multi recipient else: base_url = self.api_url + "/send.php" #Common params params = { "user" : self.getGatewayUser(), "pass" : self.getGatewayPassword(), "rcpt" : recipient, "data" : text, "qty" : quality, "return_id": 1} #Define sender if sender_title and self.isTitleMode() and quality == 'n': params['sender'] = sender_title elif sender: params['sender'] = self._transformPhoneUrlToGatewayNumber(sender) elif self.getDefaultSender(): params['sender'] = self.getDefaultSender() #Define type of message if message_type != "text": assert quality == 'n', "This type of message require top level messsage quality" assert message_type in self.getAllowedMessageType(), "Unknown message type" param['operation'] = message_type.capitalize() #Send message (or test) if test or self.isSimulationMode(): LOG("MobytGateway", INFO, params) result = {'status': "Test"} else: LOG('MobytGateway, sending to with params', 0, (base_url, params)) params = urllib.urlencode(params) page = urllib.urlopen(base_url, params) result = self._fetchSendResponseAsDict(page) #Check result and return if result['status'] == "OK": return [result.get('status_info', "")] #return message id (gateway side) elif result['status'] == "KO": #we get an error when call the gateway raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to send the SMS")) elif result['status'] == "Test": #just a test, no message id return None else: raise ValueError("Unknown result", 0, result) security.declareProtected(Permissions.ManagePortal, 'getMessageStatus') def getMessageStatus(self, message_id): """Retrive the status of a message""" base_url = self.api_url + "/batch-status.php" params = { "user" : self.getGatewayUser(), "pass" : self.getGatewayPassword(), "id" : message_id, "type" : 'notify', "schema" : 1 } params = urllib.urlencode(params) page = urllib.urlopen(base_url, params) result = self._fetchStatusResponseAsDict(page) if result['status'] == "OK": row_list = result.get('status_info') #return only status_text list if len(row_list) == 1: return row_list[0].get('status_text').lower() else: status_list = [] for row in row_list: status_list.append(row.get('status_text').lower()) return status_list elif result['status'] == "KO": #we get an error when call the gateway raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to get the message status")) security.declarePublic('receive') def receive(self,REQUEST): """Receive push notification from the gateway""" #Get current user sm = getSecurityManager() try: #Use SUPER_USER portal_membership = self.getPortalObject().portal_membership newSecurityManager(None, portal_membership.getMemberById(SUPER_USER)) #Mobyt notify only new SMS self.notifyReception(REQUEST.get("orig"), REQUEST.get("text"), REQUEST.get("ticket")) finally: #Restore orinal user setSecurityManager(sm) security.declareProtected(Permissions.ManagePortal, 'notifyReception') def notifyReception(self, sender, text, message_id): """The gateway inform what we ha a new message. """ #Convert phone as erp5 compliant def parsePhoneNumber(number): #XXX: Should register well formatted number or brut number ? #return number return "+%s(%s)-%s" % (number[0:2],0,number[2:]) #Create the new sms in activities self.activate(activity='SQLQueue').SMSTool_pushNewSMS( message_id=message_id, sender=parsePhoneNumber(sender), recipient=None, text_content=text, message_type='text/plain', reception_date=DateTime())