0
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
1 import email
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
2 import os.path
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
3 import re
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
4 import smtplib
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
5 import sqlite3
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
6 import subprocess
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
7 import textwrap
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
8
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
9 from collections import namedtuple, defaultdict
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
10 from contextlib import contextmanager
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
11 from datetime import datetime
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
12 from decimal import Decimal
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
13 from email.mime.text import MIMEText
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
14 from email.utils import format_datetime, localtime, parseaddr
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
15 from io import BytesIO
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
16 from itertools import product, islice
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
17 from urllib.parse import urljoin
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
18
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
19 import bs4
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
20 import numpy as np
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
21 import requests
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
22
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
23 from PIL import Image
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
24
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
25 DB = 'bnpparibas.sqlite'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
26
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
27 # message template
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
28 MESSAGE = """\
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
29 From: {sender:}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
30 Subject: {subject:}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
31 Date: {date:}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
32 Message-Id: {id:}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
33
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
34 {body:}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
35 """
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
36
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
37 # transaction template
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
38 HEADER = '{:14s} {:10s} {:59s} {:>8s}'.format('Id', 'Date', 'Description', 'Amount')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
39 TRANSACTION = '{id:} {date:%d/%m/%Y} {descr:59s} {amount:>8s}'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
40
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
41 # as defined in bnpbaribas web app
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
42 CATEGORIES = {
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
43 '1': 'Alimentation',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
44 '7': 'Logement',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
45 '8': 'Loisirs',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
46 '9': 'Transport',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
47 '12': 'Opérations bancaires',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
48 '13': 'Non défini',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
49 '14': 'Multimédia',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
50 '20': 'Energies',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
51 '22': 'Retrait',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
52 '23': 'Sorties',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
53 'R58': 'Non défini',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
54 }
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
55
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
56 # euro symbol
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
57 EURO = b'\xe2\x82\xac'.decode('utf-8')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
58
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
59
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
60 # load configuration
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
61 from config import *
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
62
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
63
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
64 # GPG encrypted text is ascii and as such does not require encoding
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
65 # but its decrypted form is utf-8 and therefore the charset header
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
66 # must be set accordingly. define an appropriate charset object
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
67 email.charset.add_charset('utf8 7bit', header_enc=email.charset.SHORTEST,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
68 body_enc=None, output_charset='utf-8')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
69
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
70
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
71 Message = namedtuple('Message', 'id read icon sender subject date validity'.split())
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
72
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
73
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
74 class Transaction:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
75 def __init__(self, tid, date, descr, debit, credit, category):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
76 self.id = tid
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
77 self.date = date
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
78 self.descr = descr
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
79 self.debit = debit
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
80 self.credit = credit
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
81 self.category = category
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
82
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
83 def __str__(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
84 # there does not seem to be an easy way to format Decimal
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
85 # objects with a leading sign in both the positive and
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
86 # negative value cases so do it manually
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
87 d = vars(self)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
88 if d['debit']:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
89 d['amount'] = '-' + str(d['debit'])
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
90 if d['credit']:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
91 d['amount'] = '+' + str(d['credit'])
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
92 return TRANSACTION.format(**d)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
93
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
94
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
95 def imslice(image):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
96 for x, y in product(range(0, 5), range(0, 5)):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
97 islice = image[27*x+1:27*(x+1), 27*y+1:27*(y+1), 0]
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
98 yield islice
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
99
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
100
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
101 def imdecode(image):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
102 keypad = np.load(os.path.join(os.path.dirname(__file__), 'keypad.npy'))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
103 immap = {}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
104 for n, islice in enumerate(imslice(image)):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
105 # skip empty tiles
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
106 if np.mean(islice) > 248.0:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
107 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
108 # compare to reference tiles
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
109 for d in range(0, 10):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
110 delta = np.sum(islice - keypad[d])
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
111 if delta < 100:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
112 print(delta)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
113 immap[d] = n + 1
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
114 return immap
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
115
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
116
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
117 def amountparse(value):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
118 # empty
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
119 if value == '\xa0':
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
120 return None
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
121 m = re.match(r'\s+((?:\d+\.)?\d+,\d+)\s+([^\s]+)\s+$', value, re.U|re.S)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
122 if m is None:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
123 raise ValueError(repr(value))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
124 # euro
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
125 currency = m.group(2)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
126 if currency != EURO:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
127 raise ValueError(repr(currency))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
128 return Decimal(m.group(1).replace('.', '').replace(',', '.'))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
129
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
130
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
131 class Site:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
132 def __init__(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
133 self.url = 'https://www.secure.bnpparibas.net'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
134 self.req = requests.Session()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
135
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
136 def login(self, user, passwd):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
137 # login page
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
138 url = urljoin(self.url, '/banque/portail/particulier/HomeConnexion')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
139 r = self.req.get(url, params={'type': 'homeconnex'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
140 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
141 # login form
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
142 soup = bs4.BeautifulSoup(r.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
143 form = soup.find('form', attrs={'name': 'logincanalnet'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
144 # extract relevant data
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
145 action = form['action']
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
146 data = { field['name']: field['value'] for field in form('input') }
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
147
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
148 # keyboard image url
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
149 src = ''
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
150 tag = soup.find(attrs={'id': 'secret-nbr-keyboard'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
151 for prop in tag['style'].split(';'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
152 match = re.match(r'background-image:\s+url\(\'(.*)\'\)\s*', prop)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
153 if match:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
154 src = match.group(1)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
155 break
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
156 # download keyboard image
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
157 r = self.req.get(urljoin(self.url, src))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
158 image = np.array(Image.open(BytesIO(r.content)).convert('RGB'))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
159 # decode digits position
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
160 passwdmap = imdecode(image)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
161
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
162 # encode password
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
163 passwdenc = ''.join('%02d' % passwdmap[d] for d in map(int, passwd))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
164
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
165 # username and password
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
166 data['ch1'] = user
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
167 data['ch5'] = passwdenc
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
168
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
169 # post
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
170 r = self.req.post(urljoin(self.url, action), data=data)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
171 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
172 # redirection
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
173 m = re.search(r'document\.location\.replace\(\"(.+)\"\)', r.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
174 dest = m.group(1)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
175 r = self.req.get(dest)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
176 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
177
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
178 # check for errors
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
179 soup = bs4.BeautifulSoup(r.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
180 err = soup.find(attrs={'class': 'TitreErreur'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
181 if err:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
182 raise ValueError(err.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
183
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
184
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
185 def recent(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
186 data = {
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
187 'BeginDate': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
188 'Categs': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
189 'Contracts': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
190 'EndDate': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
191 'OpTypes': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
192 'cboFlowName': 'flow/iastatement',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
193 'contractId': CONTRACT,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
194 'contractIds': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
195 'entryDashboard': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
196 'execution': 'e6s1',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
197 'externalIAId': 'IAStatements',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
198 'g1Style': 'expand',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
199 'g1Type': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
200 'g2Style': 'collapse',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
201 'g2Type': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
202 'g3Style': 'collapse',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
203 'g3Type': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
204 'g4Style': 'collapse',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
205 'g4Type': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
206 'groupId': '-2',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
207 'groupSelected': '-2',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
208 'gt': 'homepage:basic-theme',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
209 'pageId': 'releveoperations',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
210 'pastOrPendingOperations': '1',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
211 'sendEUD': 'true',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
212 'step': 'STAMENTS', }
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
213
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
214 url = urljoin(self.url, '/banque/portail/particulier/FicheA')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
215 r = self.req.post(url, data=data)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
216 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
217 text = r.text
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
218
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
219 # the html is so broken beautifulsoup does not understand it
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
220 text = text.replace(
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
221 '<th class="thTitre" style="width:7%">Pointage </td>',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
222 '<th class="thTitre" style="width:7%">Pointage </th>')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
223 s = bs4.BeautifulSoup(text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
224
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
225 # extract transactions
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
226 table = s.find('table', id='tableCompte')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
227 rows = table.find_all('tr')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
228 for row in rows:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
229 fields = row.find_all('td')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
230 if not fields:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
231 # skip headers row
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
232 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
233 id = int(fields[0].input['id'].lstrip('_'))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
234 date = datetime.strptime(fields[1].text, '%d/%m/%Y')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
235 descr = fields[2].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
236 debit = amountparse(fields[3].text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
237 credit = amountparse(fields[4].text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
238 category = fields[5].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
239 categoryid = fields[6].span['class'][2][4:]
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
240 yield Transaction(id, date, descr, debit, credit, categoryid)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
241
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
242
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
243 def messages(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
244 data = {
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
245 'identifiant': 'BmmFicheListerMessagesRecus_20100607022434',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
246 'type': 'fiche', }
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
247
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
248 url = urljoin(self.url, '/banque/portail/particulier/Fiche')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
249 r = self.req.post(url, data=data)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
250 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
251 s = bs4.BeautifulSoup(r.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
252
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
253 # messages list
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
254 table = s.find('table', id='listeMessages')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
255 for row in table.find_all('tr', recursive=False):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
256 # skip headers and separators
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
257 if 'entete' in row['class']:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
258 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
259 # skip separators
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
260 if 'sep' in row['class']:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
261 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
262 # skip footer
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
263 if 'actions_bas' in row['class']:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
264 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
265 fields = row.find_all('td')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
266 icon = fields[1].img['src']
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
267 sender = fields[2].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
268 subject = fields[4].a.text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
269 date = datetime.strptime(fields[5]['data'], '%Y/%m/%d:%Hh%Mmin%Ssec')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
270 validity = datetime.strptime(fields[6]['data'], '%Y/%m/%d:%Hh%Mmin%Ssec')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
271 m = re.match(r'''validerFormulaire\('BmmFicheLireMessage_20100607022346','(.+)','(true|false)'\);$''', fields[4].a['onclick'])
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
272 mid = m.group(1)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
273 read = m.group(2) == 'false'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
274 yield Message(mid, read, icon, sender, subject, date, validity)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
275
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
276
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
277 def message(self, mid):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
278 data = {
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
279 'etape': 'boiteReception',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
280 'idMessage': mid,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
281 'identifiant': 'BmmFicheLireMessage_20100607022346',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
282 'maxPagination': 2,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
283 'minPagination': 1,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
284 'nbElementParPage': 20,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
285 'nbEltPagination': 5,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
286 'nbPages': 2,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
287 'newMsg': 'false',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
288 'pagination': 1,
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
289 'type': 'fiche',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
290 'typeAction': '', }
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
291
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
292 url = urljoin(self.url, '/banque/portail/particulier/Fiche')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
293 r = self.req.post(url, data=data)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
294 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
295 # fix badly broken html
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
296 text = r.text.replace('<br>', '<br/>').replace('</br>', '')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
297 s = bs4.BeautifulSoup(text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
298
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
299 envelope = s.find('div', attrs={'class': 'enveloppe'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
300 rows = envelope.find_all('tr')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
301 fields = rows[1].find_all('td')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
302 # the messages list present a truncated sender
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
303 sender = fields[0].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
304 # not used
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
305 subject = fields[1].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
306 date = fields[2].text.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
307
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
308 content = s.find('div', attrs={'class': 'txtMessage'})
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
309 # clean up text
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
310 for t in content.find_all('style'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
311 t.extract()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
312 for t in content.find_all('script'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
313 t.extract()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
314 for t in content.find_all(id='info_pro'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
315 t.extract()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
316 for t in content.find_all('br'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
317 t.replace_with('\n\n')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
318 for t in content.find_all('b'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
319 if t.string:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
320 t.replace_with('*%s*' % t.string.strip())
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
321 for t in content.find_all('li'):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
322 t.replace_with('- %s\n\n' % t.text.strip())
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
323 # format nicely
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
324 text = re.sub(' +', ' ', content.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
325 text = re.sub(r'\s+([\.:])', r'\1', text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
326 pars = []
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
327 for p in re.split('\n\n+', text):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
328 p = p.strip()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
329 if p:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
330 pars.append('\n'.join(textwrap.wrap(p, 72)))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
331 body = '\n\n'.join(pars)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
332 return sender, body
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
333
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
334
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
335 def transactions(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
336 data = {'ch_memo': 'NON',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
337 'ch_rop_cpt_0': 'FR7630004001640000242975804',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
338 'ch_rop_dat': 'tous',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
339 'ch_rop_dat_deb': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
340 'ch_rop_dat_fin': '',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
341 'ch_rop_fmt_dat': 'JJMMAAAA',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
342 'ch_rop_fmt_fic': 'RTEXC',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
343 'ch_rop_fmt_sep': 'PT',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
344 'ch_rop_mon': 'EUR',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
345 'x': '55',
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
346 'y': '7'}
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
347 r = self.req.post(urljoin(self.url, '/SAF_TLC_CNF'), data=data)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
348 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
349 s = bs4.BeautifulSoup(r.text)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
350 path = s.find('a')['href']
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
351 r = self.req.get(urljoin(self.url, path))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
352 r.raise_for_status()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
353 return r.text
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
354
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
355
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
356 class Mailer:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
357 def __init__(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
358 self.server = SMTPSERVER
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
359 self.port = SMTPPORT
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
360 self.starttls = SMTPSTARTTLS
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
361 self.username = SMTPUSER
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
362 self.password = SMTPPASSWD
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
363
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
364 @contextmanager
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
365 def connect(self):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
366 smtp = smtplib.SMTP(self.server, self.port)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
367 if self.starttls:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
368 smtp.starttls()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
369 if self.username:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
370 smtp.login(self.username, self.password)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
371 yield smtp
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
372 smtp.quit()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
373
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
374 def send(self, message, fromaddr=None, toaddr=None):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
375 if not fromaddr:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
376 fromaddr = message['From']
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
377 if not toaddr:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
378 toaddr = message['To']
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
379 with self.connect() as conn:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
380 conn.sendmail(fromaddr, toaddr, str(message))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
381
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
382
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
383 def encrypt(message, sender, recipient):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
384 sender = parseaddr(sender)[1]
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
385 recipient = parseaddr(recipient)[1]
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
386 cmd = [ "gpg", "--homedir", GNUPGHOME, "--batch", "--yes", "--no-options", "--armor",
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
387 "--local-user", sender, "--recipient", recipient, "--sign", "--encrypt"]
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
388 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
389 encdata = p.communicate(input=message.encode('utf-8'))[0].decode('ascii')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
390 return encdata
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
391
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
392
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
393 def main():
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
394 bnp = Site()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
395 bnp.login(USERNAME, PASSWORD)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
396
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
397 db = sqlite3.connect(DB)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
398 db.execute('''CREATE TABLE IF NOT EXISTS messages (id TEXT PRIMARY KEY)''')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
399 db.execute('''CREATE TABLE IF NOT EXISTS transactions (id INTEGER PRIMARY KEY)''')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
400
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
401 mailer = Mailer()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
402
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
403 ## unread messages
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
404 messages = filter(lambda x: not x.read, bnp.messages())
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
405 for m in sorted(messages, key=lambda x: x.date):
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
406 curs = db.cursor()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
407 curs.execute('''SELECT IFNULL((SELECT id FROM messages WHERE id = ?), 0)''', (m.id, ))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
408 if curs.fetchone()[0]:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
409 # already handled
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
410 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
411
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
412 # retrieve complete sender and message body
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
413 sender, body = bnp.message(m.id)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
414
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
415 # compose and send message
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
416 body = MESSAGE.format(id=m.id, sender=sender, date=m.date, subject=m.subject, body=body)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
417 message = MIMEText(encrypt(body, MAILFROM, MAILTO), _charset='utf8 7bit')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
418 message['Subject'] = 'BNP Paribas message'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
419 message['From'] = MAILFROM
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
420 message['To'] = MAILTO
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
421 message['Date'] = format_datetime(localtime(m.date))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
422 mailer.send(message)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
423
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
424 curs.execute('''INSERT INTO messages (id) VALUES (?)''', (m.id, ))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
425 db.commit()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
426
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
427
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
428 ## transactions
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
429 transactions = bnp.recent()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
430 curs = db.cursor()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
431 lines = []
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
432 for t in transactions:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
433 curs.execute('''SELECT IFNULL((SELECT id FROM transactions WHERE id = ?), 0)''', (t.id, ))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
434 if curs.fetchone()[0]:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
435 # already handled
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
436 continue
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
437 lines.append(str(t))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
438 curs.execute('''INSERT INTO transactions (id) VALUES (?)''', (t.id, ))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
439
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
440 if lines:
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
441 lines.insert(0, HEADER)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
442 lines.insert(1, '-' * len(HEADER))
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
443 body = '\n'.join(lines)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
444 message = MIMEText(encrypt(body, MAILFROM, MAILTO), _charset='utf8 7bit')
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
445 message['Subject'] = 'BNP Paribas update'
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
446 message['From'] = MAILFROM
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
447 message['To'] = MAILTO
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
448 message['Date'] = format_datetime(localtime())
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
449 mailer.send(message)
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
450
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
451 db.commit()
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
452
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
453
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
454 if __name__ == '__main__':
|
Daniele Nicolodi <daniele.nicolodi@obspm.fr>
parents:
diff
changeset
|
455 main()
|