3
3
This package allows you to communicate with your Revolut accounts
4
4
"""
5
5
6
- import requests
7
- import json
8
6
import base64
9
- from urllib .parse import urljoin
10
7
from datetime import datetime
8
+ import json
9
+ import requests
10
+ from urllib .parse import urljoin
11
11
12
12
__version__ = '0.0.6' # Should be the same in setup.py
13
13
41
41
}
42
42
43
43
44
- class Amount ( object ) :
44
+ class Amount :
45
45
""" Class to handle the Revolut amount with currencies """
46
46
def __init__ (self , currency , revolut_amount = None , real_amount = None ):
47
47
if currency not in _AVAILABLE_CURRENCIES :
@@ -53,6 +53,7 @@ def __init__(self, currency, revolut_amount=None, real_amount=None):
53
53
raise TypeError (type (revolut_amount ))
54
54
self .revolut_amount = revolut_amount
55
55
self .real_amount = self .get_real_amount ()
56
+
56
57
elif real_amount is not None :
57
58
if type (real_amount ) not in [float , int ]:
58
59
raise TypeError (type (real_amount ))
@@ -100,7 +101,7 @@ def get_revolut_amount(self):
100
101
return int (self .real_amount * scale )
101
102
102
103
103
- class Transaction ( object ) :
104
+ class Transaction :
104
105
""" Class to handle an exchange transaction """
105
106
def __init__ (self , from_amount , to_amount , date ):
106
107
if type (from_amount ) != Amount :
@@ -119,7 +120,7 @@ def __str__(self):
119
120
self .to_amount ))
120
121
121
122
122
- class Client ( object ) :
123
+ class Client :
123
124
""" Do the requests with the Revolut servers """
124
125
def __init__ (self , token , device_id ):
125
126
self .session = requests .session ()
@@ -133,7 +134,7 @@ def __init__(self, token, device_id):
133
134
134
135
def _get (self , url , expected_status_code = 200 ):
135
136
ret = self .session .get (url = url , headers = self .headers )
136
- if ( ret .status_code != expected_status_code ) :
137
+ if ret .status_code != expected_status_code :
137
138
raise ConnectionError (
138
139
'Status code {status} for url {url}\n {content}' .format (
139
140
status = ret .status_code , url = url , content = ret .text ))
@@ -142,15 +143,15 @@ def _get(self, url, expected_status_code=200):
142
143
def _post (self , url , post_data , expected_status_code = 200 ):
143
144
ret = self .session .post (url = url ,
144
145
headers = self .headers ,
145
- data = post_data )
146
- if ( ret .status_code != expected_status_code ) :
146
+ json = post_data )
147
+ if ret .status_code != expected_status_code :
147
148
raise ConnectionError (
148
149
'Status code {status} for url {url}\n {content}' .format (
149
150
status = ret .status_code , url = url , content = ret .text ))
150
151
return ret
151
152
152
153
153
- class Revolut ( object ) :
154
+ class Revolut :
154
155
def __init__ (self , token , device_id ):
155
156
self .client = Client (token = token , device_id = device_id )
156
157
@@ -162,14 +163,14 @@ def get_account_balances(self):
162
163
163
164
account_balances = []
164
165
for raw_account in raw_accounts .get ("pockets" ):
165
- account_balance = {}
166
- account_balance [ "balance" ] = raw_account .get ("balance" )
167
- account_balance [ "currency" ] = raw_account .get ("currency" )
168
- account_balance [ "type" ] = raw_account .get ("type" )
169
- account_balance [ "state" ] = raw_account .get ("state" )
170
- # name is present when the account is a vault (type = SAVINGS)
171
- account_balance [ "vault_name" ] = raw_account .get ("name" , "" )
172
- account_balances . append ( account_balance )
166
+ account_balances . append ({
167
+ "balance" : raw_account .get ("balance" ),
168
+ "currency" : raw_account .get ("currency" ),
169
+ "type" : raw_account .get ("type" ),
170
+ "state" : raw_account .get ("state" ),
171
+ # name is present when the account is a vault (type = SAVINGS)
172
+ "vault_name" : raw_account .get ("name" , "" ),
173
+ } )
173
174
self .account_balances = Accounts (account_balances )
174
175
return self .account_balances
175
176
@@ -178,7 +179,7 @@ def quote(self, from_amount, to_currency):
178
179
raise TypeError ("from_amount must be with the Amount type" )
179
180
180
181
if to_currency not in _AVAILABLE_CURRENCIES :
181
- raise KeyError (to_currency )
182
+ raise KeyError (to_currency )
182
183
183
184
url_quote = urljoin (_URL_QUOTE , '{}{}?amount={}&side=SELL' .format (
184
185
from_amount .currency ,
@@ -195,12 +196,14 @@ def exchange(self, from_amount, to_currency, simulate=False):
195
196
raise TypeError ("from_amount must be with the Amount type" )
196
197
197
198
if to_currency not in _AVAILABLE_CURRENCIES :
198
- raise KeyError (to_currency )
199
+ raise KeyError (to_currency )
199
200
200
- data = '{"fromCcy":"%s","fromAmount":%d,"toCcy":"%s","toAmount":null}' \
201
- % (from_amount .currency ,
202
- from_amount .revolut_amount ,
203
- to_currency )
201
+ data = {
202
+ "fromCcy" : from_amount .currency ,
203
+ "fromAmount" : from_amount .revolut_amount ,
204
+ "toCcy" : to_currency ,
205
+ "toAmount" : None ,
206
+ }
204
207
205
208
if simulate :
206
209
# Because we don't want to exchange currencies
@@ -244,7 +247,7 @@ def exchange(self, from_amount, to_currency, simulate=False):
244
247
return exchange_transaction
245
248
246
249
247
- class Account ( object ) :
250
+ class Account :
248
251
""" Class to handle an account """
249
252
def __init__ (self , account_type , balance , state , vault_name ):
250
253
self .account_type = account_type # CURRENT, SAVINGS
@@ -270,65 +273,69 @@ def __str__(self):
270
273
balance = str (self .balance ))
271
274
272
275
273
- class Accounts ( object ) :
276
+ class Accounts :
274
277
""" Class to handle the account balances """
278
+
275
279
def __init__ (self , account_balances ):
276
280
self .raw_list = account_balances
277
- self .list = []
278
- for account in self .raw_list :
279
- self .list .append (Account (account_type = account .get ("type" ),
280
- balance = Amount (
281
- currency = account .get ("currency" ),
282
- revolut_amount = account .get ("balance" )),
283
- state = account .get ("state" ),
284
- vault_name = account .get ("vault_name" )))
281
+ self .list = [
282
+ Account (
283
+ account_type = account .get ("type" ),
284
+ balance = Amount (
285
+ currency = account .get ("currency" ),
286
+ revolut_amount = account .get ("balance" ),
287
+ ),
288
+ state = account .get ("state" ),
289
+ vault_name = account .get ("vault_name" ),
290
+ )
291
+ for account in self .raw_list
292
+ ]
285
293
286
294
def get_account_by_name (self , account_name ):
287
295
""" Get an account by its name """
288
- found_account = None
289
- for account in self :
296
+ for account in self .list :
290
297
if account .name == account_name :
291
- found_account = account
292
- break
293
- return found_account
298
+ return account
294
299
295
300
def __len__ (self ):
296
301
return len (self .list )
297
302
298
303
def __getitem__ (self , key ):
299
- """ Méthod to access the object as a list
304
+ """ Method to access the object as a list
300
305
(ex : accounts[1]) """
301
306
return self .list [key ]
302
307
303
308
def csv (self , lang = "fr" ):
304
- if lang == "fr" :
309
+ lang_is_fr = lang == "fr"
310
+ if lang_is_fr :
305
311
csv_str = "Nom du compte;Solde;Devise"
306
312
else :
307
313
csv_str = "Account name,Balance,Currency"
308
314
315
+ # Europe uses 'comma' as decimal separator,
316
+ # so it can't be used as delimiter:
317
+ delimiter = ";" if lang_is_fr else ","
318
+
309
319
for account in self .list :
310
320
if account .state == _ACTIVE_ACCOUNT : # Do not print INACTIVE
311
- csv_str += ('\n {account_name},{balance},{currency}' .format (
312
- account_name = account .name ,
313
- currency = account .balance .currency ,
314
- balance = account .balance .real_amount_str ))
315
- if lang == "fr" :
316
- # In the French Excel, csv are like "pi;3,14" (not "pi,3.14")
317
- csv_str = csv_str .replace ("," , ";" ).replace ("." , "," )
318
- return csv_str
321
+ csv_str += "\n " + delimiter .join ((
322
+ account .name ,
323
+ account .balance .real_amount_str ,
324
+ account .balance .currency ,
325
+ ))
326
+
327
+ return csv_str .replace ("." , "," ) if lang_is_fr else csv_str
319
328
320
329
321
330
def get_token_step1 (device_id , phone , password , simulate = False ):
322
331
""" Function to obtain a Revolut token (step 1 : send a code by sms) """
323
- if simulate :
324
- ret = ""
325
- else :
332
+ if not simulate :
326
333
c = Client (device_id = device_id , token = _DEFAULT_TOKEN_FOR_SIGNIN )
327
- data = ' {"phone":"%s", "password": "%s"}' % ( phone , password )
328
- ret = c ._post (url = _URL_GET_TOKEN_STEP1 ,
329
- post_data = data ,
330
- expected_status_code = 204 )
331
- return ret
334
+ data = {"phone" : phone , "password" : password }
335
+ return c ._post (url = _URL_GET_TOKEN_STEP1 ,
336
+ json = data ,
337
+ expected_status_code = 204 )
338
+ return ""
332
339
333
340
334
341
def get_token_step2 (device_id , phone , sms_code , simulate = False ):
@@ -353,7 +360,7 @@ def get_token_step2(device_id, phone, sms_code, simulate=False):
353
360
else :
354
361
c = Client (device_id = device_id , token = _DEFAULT_TOKEN_FOR_SIGNIN )
355
362
sms_code = sms_code .replace ("-" , "" ) # If the user would put -
356
- data = ' {"phone":"%s", "code": "%s"}' % ( phone , sms_code )
363
+ data = {"phone" : phone , "code" : sms_code }
357
364
ret = c ._post (url = _URL_GET_TOKEN_STEP2 , post_data = data )
358
365
raw_get_token = json .loads (ret .text )
359
366
0 commit comments