annotate src/bnpparibas.py @ 2:ad577744dd8e

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