iro/offer/smstrade.py
author hefee
Sat, 27 Jul 2019 13:37:23 +0200
changeset 310 352850d4fb4b
parent 307 6acae4210716
child 312 42fd5075a5d1
permissions -rw-r--r--
remove spaces on lineendings.

# -*- 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
        try:
            self.costs = Decimal(costs)
        except:
            if not costs.strip():
                self.costs = Decimal("0.0")
            else:
                raise
        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)
        s.costs = Decimal('0.0728')
        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()
        print "result:%s"%(data)
        if len(data) == 1:
            return StatusCode(int(data[0]))
        return StatusCode(int(data[0]),exID=data[1].strip(),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