iro/offer/smstrade.py
author Sandro Knauß <knauss@netzguerilla.net>
Wed, 25 Apr 2012 00:05:53 +0200
branchdevel
changeset 275 88d45c846f2b
parent 269 0d134b173cb1
child 291 84eb5a7a715a
permissions -rw-r--r--
docu fixes

# -*- coding: utf-8 -*-
#Copyright (C) 2009  Georg Bischoff

#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 3 of the License, or 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, see <http://www.gnu.org/licenses/>.


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