iro/offer/smstrade.py
changeset 302 3f4bdea2abbf
parent 295 dc3cc61c7f6f
child 307 6acae4210716
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/offer/smstrade.py	Thu Sep 27 17:15:46 2012 +0200
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net>
+# 
+# This file is part of Iro.
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+# #Software, and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+import urllib
+from functools import partial
+from decimal import Decimal
+#import copy
+
+from ..config import Option
+from ..model.status import Status
+from .provider import Provider, providers
+from ..error import RejectRecipient, ExternalException
+
+#import logging
+#logger=logging.getLogger("smstrade")
+
+statusCodes = {10 : "Empfaengernummer nicht korrekt.",
+    20 : "Absenderkennung nicht korrekt.",
+    30 : "Nachrichtentext nicht korrekt.",
+    31 : "Messagetyp nicht korrekt.",
+    40 : "SMS Route nicht korrekt.",
+    50 : "Identifikation fehlgeschlagen.",
+    60 : "nicht genuegend Guthaben.",
+    70 : "Netz wird von Route nicht abgedeckt.",
+    71 : "Feature nicht ueber diese Route moeglich.",
+    80 : "Uebergabe an SMS-C fehlgeschlagen.",
+    90 : "Versand nicht moeglich.",
+    100 : "SMS wurde versendet.",
+    }
+"""statuscodes of external smstrade API"""
+
+
+class SmstradeException(ExternalException):
+    """An excetion that connects the status code with the excetion string (see :attr:`statusCodes`)"""
+    def __init__(self,status):
+        ExternalException.__init__(self)
+        self.status = status
+        self.str_=str(status)
+    
+    def __str__(self):
+        return "%s\n%s"%(ExternalException.__str__(self),self.str_)
+
+
+class StatusCode:
+    """Class that represents the output of one smstrade request."""
+    def __init__(self,code, exID=None, costs=Decimal("0.0"), count=0):
+        self.code = code
+        
+        self.exID = exID
+        self.costs = Decimal(costs)
+        self.count = int(count)
+     
+    def __str__(self):
+        if self.code in statusCodes.keys():
+            return "%i: %s"%(self.code, statusCodes[self.code])
+        
+        return "%i: unknown statuscode."%self.code
+
+    def __int__(self):
+        return self.code
+
+class Smstrade(Provider):
+    """A Provider to send SMS to recipients using smstrade.
+    Smstrade only supports to send SMS  and four diffrent routes: ``["basic","economy","gold","direct"]``.
+
+    It needs a smstrade Gateway Key https://login.smstrade.de/index.php?gateway in configuration file.
+
+    smstrade API documentation:  http://kundencenter.smstrade.de/sites/smstrade.de.kundencenter/__pdf/SMS-Gateway_HTTP_API_v2.pdf
+
+    The smstrade API supports a debug mode, that can be set  with :attr:`~iro.offer.provider.Provider.testmode`.
+    """
+    _params= {"debug":("boolean",False),
+            "concat_sms":('boolean',False),
+            "message_id":('boolean',False),
+            "count":('boolean',False),
+            "cost":('boolean',False),
+           }
+    '''dict for standrd values of the smstrade api, it is used to get the right values to the API.'''
+
+    def __init__(self, name):       
+        self.url = "https://gateway.smstrade.de"
+        options =[("key", Option(lambda x,y:x,long="smstrade Gateway Key https://login.smstrade.de/index.php?gateway", must=True)),]
+        Provider.__init__(self, name, {"sms":["basic","economy","gold","direct"]},options)
+
+    def send(self, route, recipient, sms):
+        """send one SMS to recipient via route
+        
+        :param string route: A valid route ``["basic", "economy", "gold", "direct"]``
+        :param `iro.telnumber.Telnumber` recipient: Mobilenumber of recipient
+        :param `iro.model.message.SMS` sms: the sms to send
+        :return:
+            - All went ok -- :class:`iro.model.status.Status` object
+            - otherwise -- an exception
+        """
+        #logger.debug('smstrade.sendSMS(%s,%s)'%(sms,  recipient))
+
+        route = unicode(route)
+
+        if recipient.land != '49' and route == "basic":
+            raise RejectRecipient(recipient)
+        
+        to ='00'+recipient.land+recipient.number
+        
+        s = self.__send(route, to, sms)	
+        if int(s) in (100,):
+            return Status(self,route, exID=s.exID, costs=s.costs, count=s.count)
+        elif int(s) in (70,71,):
+            raise RejectRecipient(recipient, status=s)
+        else:
+            raise SmstradeException(s)
+
+    def __send(self, route, to, sms):
+        """ This is the main function to request to the sms service.    
+
+        :param string route: A valid route ``["basic", "economy", "gold", "direct"]
+        :param string recipient: Mobilenumber of recipient
+        :param `iro.model.message.sms` sms: the sms to send
+        :return: a :class:`.StatusCode` object
+        """
+        
+        #logger.debug('smstrade._send(%s,%s,%s)'%( route, to, sms))
+        parameters= {"key": self.key,
+                "route": route,
+                "to": to,
+                "message": sms.content,
+                "charset":"utf-8", 
+                "debug": self.testmode,
+                "message_id":True,
+                "count":True,
+                "cost":True,
+                }
+        
+        doubleChar="€[]{}|\\^~"    #these charactar need two GSM Chars
+
+        if sms.from_ is not None:
+            parameters["from"] = sms.from_
+       
+        length=len(sms.content)
+        for s in doubleChar:
+            length += sms.content.count(s)
+        parameters["concat_sms"] = True if length > 160 else False
+
+        ps={}
+        for p in parameters:
+            if p in self._params.keys():
+                if self._params[p][0] == "boolean":
+                    if parameters[p] != self._params[p][1]:
+                        ps[p]=int(bool(parameters[p]))
+            else:
+                ps[p] = parameters[p]
+
+        params = urllib.urlencode(ps)
+        #dp=copy.deepcopy(ps)
+        #dp["key"]="<KEY>"
+        #print 'smstrade._send-parameters:%s\n\t->%s'%(str(dp), urllib.urlencode(dp))
+        
+        response = urllib.urlopen(self.url, params)
+        data = response.readlines()
+        if len(data) == 1:
+            return StatusCode(int(data[0]))
+        return StatusCode(int(data[0]),exID=data[1],costs=data[2],count=data[3])
+
+    def getSendFunc(self, typ, route):
+        """returns  a partial :meth:`send` methed with bounded route, if typ and route is valid."""
+
+        Provider.getSendFunc(self, typ, route)
+        return partial(self.send,route)
+
+providers["smstrade"]=Smstrade