|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net> |
|
4 # |
|
5 # This file is part of Iro. |
|
6 # |
|
7 # Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
8 # this software and associated documentation files (the "Software"), to deal in |
|
9 # the Software without restriction, including without limitation the rights to use, |
|
10 # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
|
11 # #Software, and to permit persons to whom the Software is furnished to do so, |
|
12 # subject to the following conditions: |
|
13 # |
|
14 # The above copyright notice and this permission notice shall be included in |
|
15 # all copies or substantial portions of the Software. |
|
16 # |
|
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
18 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
|
19 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
20 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
23 |
|
24 |
|
25 import urllib |
|
26 from functools import partial |
|
27 from decimal import Decimal |
|
28 #import copy |
|
29 |
|
30 from ..config import Option |
|
31 from ..model.status import Status |
|
32 from .provider import Provider, providers |
|
33 from ..error import RejectRecipient, ExternalException |
|
34 |
|
35 #import logging |
|
36 #logger=logging.getLogger("smstrade") |
|
37 |
|
38 statusCodes = {10 : "Empfaengernummer nicht korrekt.", |
|
39 20 : "Absenderkennung nicht korrekt.", |
|
40 30 : "Nachrichtentext nicht korrekt.", |
|
41 31 : "Messagetyp nicht korrekt.", |
|
42 40 : "SMS Route nicht korrekt.", |
|
43 50 : "Identifikation fehlgeschlagen.", |
|
44 60 : "nicht genuegend Guthaben.", |
|
45 70 : "Netz wird von Route nicht abgedeckt.", |
|
46 71 : "Feature nicht ueber diese Route moeglich.", |
|
47 80 : "Uebergabe an SMS-C fehlgeschlagen.", |
|
48 90 : "Versand nicht moeglich.", |
|
49 100 : "SMS wurde versendet.", |
|
50 } |
|
51 """statuscodes of external smstrade API""" |
|
52 |
|
53 |
|
54 class SmstradeException(ExternalException): |
|
55 """An excetion that connects the status code with the excetion string (see :attr:`statusCodes`)""" |
|
56 def __init__(self,status): |
|
57 ExternalException.__init__(self) |
|
58 self.status = status |
|
59 self.str_=str(status) |
|
60 |
|
61 def __str__(self): |
|
62 return "%s\n%s"%(ExternalException.__str__(self),self.str_) |
|
63 |
|
64 |
|
65 class StatusCode: |
|
66 """Class that represents the output of one smstrade request.""" |
|
67 def __init__(self,code, exID=None, costs=Decimal("0.0"), count=0): |
|
68 self.code = code |
|
69 |
|
70 self.exID = exID |
|
71 self.costs = Decimal(costs) |
|
72 self.count = int(count) |
|
73 |
|
74 def __str__(self): |
|
75 if self.code in statusCodes.keys(): |
|
76 return "%i: %s"%(self.code, statusCodes[self.code]) |
|
77 |
|
78 return "%i: unknown statuscode."%self.code |
|
79 |
|
80 def __int__(self): |
|
81 return self.code |
|
82 |
|
83 class Smstrade(Provider): |
|
84 """A Provider to send SMS to recipients using smstrade. |
|
85 Smstrade only supports to send SMS and four diffrent routes: ``["basic","economy","gold","direct"]``. |
|
86 |
|
87 It needs a smstrade Gateway Key https://login.smstrade.de/index.php?gateway in configuration file. |
|
88 |
|
89 smstrade API documentation: http://kundencenter.smstrade.de/sites/smstrade.de.kundencenter/__pdf/SMS-Gateway_HTTP_API_v2.pdf |
|
90 |
|
91 The smstrade API supports a debug mode, that can be set with :attr:`~iro.offer.provider.Provider.testmode`. |
|
92 """ |
|
93 _params= {"debug":("boolean",False), |
|
94 "concat_sms":('boolean',False), |
|
95 "message_id":('boolean',False), |
|
96 "count":('boolean',False), |
|
97 "cost":('boolean',False), |
|
98 } |
|
99 '''dict for standrd values of the smstrade api, it is used to get the right values to the API.''' |
|
100 |
|
101 def __init__(self, name): |
|
102 self.url = "https://gateway.smstrade.de" |
|
103 options =[("key", Option(lambda x,y:x,long="smstrade Gateway Key https://login.smstrade.de/index.php?gateway", must=True)),] |
|
104 Provider.__init__(self, name, {"sms":["basic","economy","gold","direct"]},options) |
|
105 |
|
106 def send(self, route, recipient, sms): |
|
107 """send one SMS to recipient via route |
|
108 |
|
109 :param string route: A valid route ``["basic", "economy", "gold", "direct"]`` |
|
110 :param `iro.telnumber.Telnumber` recipient: Mobilenumber of recipient |
|
111 :param `iro.model.message.SMS` sms: the sms to send |
|
112 :return: |
|
113 - All went ok -- :class:`iro.model.status.Status` object |
|
114 - otherwise -- an exception |
|
115 """ |
|
116 #logger.debug('smstrade.sendSMS(%s,%s)'%(sms, recipient)) |
|
117 |
|
118 route = unicode(route) |
|
119 |
|
120 if recipient.land != '49' and route == "basic": |
|
121 raise RejectRecipient(recipient) |
|
122 |
|
123 to ='00'+recipient.land+recipient.number |
|
124 |
|
125 s = self.__send(route, to, sms) |
|
126 if int(s) in (100,): |
|
127 return Status(self,route, exID=s.exID, costs=s.costs, count=s.count) |
|
128 elif int(s) in (70,71,): |
|
129 raise RejectRecipient(recipient, status=s) |
|
130 else: |
|
131 raise SmstradeException(s) |
|
132 |
|
133 def __send(self, route, to, sms): |
|
134 """ This is the main function to request to the sms service. |
|
135 |
|
136 :param string route: A valid route ``["basic", "economy", "gold", "direct"] |
|
137 :param string recipient: Mobilenumber of recipient |
|
138 :param `iro.model.message.sms` sms: the sms to send |
|
139 :return: a :class:`.StatusCode` object |
|
140 """ |
|
141 |
|
142 #logger.debug('smstrade._send(%s,%s,%s)'%( route, to, sms)) |
|
143 parameters= {"key": self.key, |
|
144 "route": route, |
|
145 "to": to, |
|
146 "message": sms.content, |
|
147 "charset":"utf-8", |
|
148 "debug": self.testmode, |
|
149 "message_id":True, |
|
150 "count":True, |
|
151 "cost":True, |
|
152 } |
|
153 |
|
154 doubleChar="€[]{}|\\^~" #these charactar need two GSM Chars |
|
155 |
|
156 if sms.from_ is not None: |
|
157 parameters["from"] = sms.from_ |
|
158 |
|
159 length=len(sms.content) |
|
160 for s in doubleChar: |
|
161 length += sms.content.count(s) |
|
162 parameters["concat_sms"] = True if length > 160 else False |
|
163 |
|
164 ps={} |
|
165 for p in parameters: |
|
166 if p in self._params.keys(): |
|
167 if self._params[p][0] == "boolean": |
|
168 if parameters[p] != self._params[p][1]: |
|
169 ps[p]=int(bool(parameters[p])) |
|
170 else: |
|
171 ps[p] = parameters[p] |
|
172 |
|
173 params = urllib.urlencode(ps) |
|
174 #dp=copy.deepcopy(ps) |
|
175 #dp["key"]="<KEY>" |
|
176 #print 'smstrade._send-parameters:%s\n\t->%s'%(str(dp), urllib.urlencode(dp)) |
|
177 |
|
178 response = urllib.urlopen(self.url, params) |
|
179 data = response.readlines() |
|
180 if len(data) == 1: |
|
181 return StatusCode(int(data[0])) |
|
182 return StatusCode(int(data[0]),exID=data[1],costs=data[2],count=data[3]) |
|
183 |
|
184 def getSendFunc(self, typ, route): |
|
185 """returns a partial :meth:`send` methed with bounded route, if typ and route is valid.""" |
|
186 |
|
187 Provider.getSendFunc(self, typ, route) |
|
188 return partial(self.send,route) |
|
189 |
|
190 providers["smstrade"]=Smstrade |