From 4b56f568120ad9a38c6c074619f690970148fc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20F=C3=A9lizard?= Date: Sat, 13 Jun 2015 22:44:06 +0000 Subject: [PATCH] new module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an OpenPGP/MIME signed message (RFC 4880 and 3156) Signed-off-by: Cédric Félizard Signed-off-by: Romain Bignon --- modules/kiwibank/README.md | 4 ++ modules/kiwibank/__init__.py | 23 +++++++++ modules/kiwibank/browser.py | 62 ++++++++++++++++++++++++ modules/kiwibank/favicon.png | Bin 0 -> 1634 bytes modules/kiwibank/module.py | 55 +++++++++++++++++++++ modules/kiwibank/pages.py | 91 +++++++++++++++++++++++++++++++++++ modules/kiwibank/test.py | 34 +++++++++++++ 7 files changed, 269 insertions(+) create mode 100644 modules/kiwibank/README.md create mode 100644 modules/kiwibank/__init__.py create mode 100644 modules/kiwibank/browser.py create mode 100644 modules/kiwibank/favicon.png create mode 100644 modules/kiwibank/module.py create mode 100644 modules/kiwibank/pages.py create mode 100644 modules/kiwibank/test.py diff --git a/modules/kiwibank/README.md b/modules/kiwibank/README.md new file mode 100644 index 00000000..21f5acd8 --- /dev/null +++ b/modules/kiwibank/README.md @@ -0,0 +1,4 @@ +# [weboob-kiwibank](https://github.com/infertux/weboob-kiwibank) + +[Kiwibank](http://www.kiwibank.co.nz/) module for the [Weboob](http://weboob.org/) project + diff --git a/modules/kiwibank/__init__.py b/modules/kiwibank/__init__.py new file mode 100644 index 00000000..f12139c5 --- /dev/null +++ b/modules/kiwibank/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2015 Cédric Félizard +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from .module import KiwibankModule + +__all__ = ['KiwibankModule'] diff --git a/modules/kiwibank/browser.py b/modules/kiwibank/browser.py new file mode 100644 index 00000000..5fdea61b --- /dev/null +++ b/modules/kiwibank/browser.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2015 Cédric Félizard +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.browser import LoginBrowser, URL, need_login +from weboob.exceptions import BrowserIncorrectPassword +from .pages import LoginPage, AccountPage, HistoryPage + + +__all__ = ['Kiwibank'] + + +class HistoryUnavailable(Exception): + pass + + +class Kiwibank(LoginBrowser): + BASEURL = 'https://www.ib.kiwibank.co.nz/mobile/' + CERTHASH = ['5dc8be7430a2e37fab4dbfe232038ec60feed827d7ce0f68613532676962c197'] + TIMEOUT = 30 + + login = URL('login/', LoginPage) + login_error = URL('login-error/', LoginPage) + accounts = URL('accounts/$', AccountPage) + account = URL('/accounts/view/[0-9A-F]+$', HistoryPage) + + def do_login(self): + self.login.stay_or_go() + self.page.login(self.username, self.password) + + if self.login.is_here() or self.login_error.is_here(): + raise BrowserIncorrectPassword() + + @need_login + def get_accounts(self): + self.accounts.stay_or_go() + return self.page.get_accounts() + + @need_login + def get_history(self, account): + if account._link is None: + raise HistoryUnavailable() + + self.location(account._link) + + return self.page.get_history() diff --git a/modules/kiwibank/favicon.png b/modules/kiwibank/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1fab88619c912b75e06d0dee35cfc11eef64eeda GIT binary patch literal 1634 zcmV-o2A%ndP)WFU8GbZ8()Nlj2>E@cM*00qWLL_t(|+U=TqOjB1J z$G`X9K5ieBUdyYt0u~}ALMIa)GUo#%L8H^L%w?GxG}E!T&247W&24TbduMTp%aUyx zvrJvK7-VsAxM*}45{IG)3QJTFTR;$cd6e4+ee4fM7e-qy1(GG4-+w*ld(OG%bAI>t z`<2mk_r03ZMe00MvjAOHve0)PM@0H!m5-Nom@ zDUG5AW*dr;Z7BZVY(y#JCJsQSN)|O}k?C2i%z7QJA;*JBC~{&HrZdUV&^wG4f2$KrN38w#Bds$kZ1=%E&{GS^LntwPth;G>7cfDw1H(&J_p1>-U5`#o^5_OkEFNXh)s(ByM(91wDYFrSSQ^ zA;&q~-I%S*9Txzf-vdg-=>tHfz5r@g7gnW~>n832Yb<-PY|b{s$C&VQ-9D6GeOvTq zJYE4R`3$VF>=8XiLd)R`c*op+T6`WJP1^+5&;WSBHu+hAQyNjWLh>R?Ws^hH4gK*m(SOdDrBp!9}JGc&+FZ z6jBYk9W6L;t|aPERohw)tzu>nYB-ZJ3aHt5XxKz(*n|i%LW)$~!${T5N3YNx)#(F( zUP@ z<1BgxTSMwJY$66+J+K&7;`f^eC#MdOOH?Q{Z%0w)4oGM@JU%BXuDw5L3G0{o58)5^ zupsrRh`K9xE07eM0ULiktR^AGgvZiL0g6Ix>xsz$Ab5I_7N3VcX9o^eZN||*Uq(Y) z<)i^<-(X7N3XIxcR7Q`7vtu9|fS_)djm?MW()B$||-q zO$X5L?8Ng+j)c{ila`{h|0Wn(7B(>FB(Fu+z-?6B`Yz&nF>C_7KDYQF`0XE?K!L)} zqF<-1)IDDNCXCwbu)%QYoUJ%mwHXvBEHZ5v)nAgc4;nTR_Cf1}PCslG*m*n!5=Mq~ z^Y%^&gqEJmxYJXMlAL`b+F7Xvi_^EjIn)ncu;Fm^Ruq_DzzXwABXqG!Jtzujix49w zoCbH7XFw_Auqu1kn7KO|J9J?Sl43KkG4IO}?emk@!Qtu-{{HIx(3N+a7rUM z#Y{BcuR(S5k*U27e|mZa;#8?9&e|E#6Nby@@w%hOE5H=@NbpryW7z|mW^m}jmY{|g zY&cf)3YKMT4aNtGqH)(=Cu)rZO&VNYM|1#q!G;r8-$7HyIXt)c@VH7@F;;owbM(79 zu|D?`a7rVL++3V#IuJ5@^gBE8(G!1R-Mqc9_1*wOOGT~0DGd-jeDtMo)4lU(?YV+W zcgnFmqby|LT9EoAXlnf5RvOwWp;a27l5tp`Q3k=?7dGvF_FEC$UMJjM0jxwlX7nF6 zQ`|!6R4HQv;FJaoy6vOfm=|oY^7Xja*94koP@4bss7Y*m19+i5>WmPp)FUVPaa1>d zA5nX;`6xK05l&B^=+~{eyC!TkIieKyc%4wmXM{be1_1eK8)4AS1uNCSHPj#MpRLP{ zc;$>_HvT$%{vovYHI7+ywe>b)zeT|~$pT$`s!lM8Etl-C>+a7VUW0zjV#pWNd zqp)H``{~9n5I`W}w}eP>=5}W_?mJq>)LQv#IDKP3PS$@QdVYVv2c0SfG{uCzROOb) z-+OSkrr>b*plje(*vagJRy1~;MJ$_$Zbxg-3s`9kbn0Z(+fE^XDE@az0rtVW5!W6T zQq+Uz5DmcZg^W?4)m{g8h-^TSe~M0XBLD~h0)PM@00;mAfB+x>2mk_r03ZMe00Mvj gAOHve0$_UC-=^s0%usVDf&c&j07*qoM6N<$f;i0I^8f$< literal 0 HcmV?d00001 diff --git a/modules/kiwibank/module.py b/modules/kiwibank/module.py new file mode 100644 index 00000000..f9ca1589 --- /dev/null +++ b/modules/kiwibank/module.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2015 Cédric Félizard +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.capabilities.bank import CapBank, AccountNotFound +from weboob.capabilities.base import find_object +from weboob.tools.backend import Module, BackendConfig +from weboob.tools.value import ValueBackendPassword +from .browser import Kiwibank + + +__all__ = ['KiwibankModule'] + + +class KiwibankModule(Module, CapBank): + NAME = 'kiwibank' + MAINTAINER = u'Cédric Félizard' + EMAIL = 'cedric@felizard.fr' + VERSION = '1.0.0' + LICENSE = 'AGPLv3+' + DESCRIPTION = u'Kiwibank' + CONFIG = BackendConfig( + ValueBackendPassword('username', label='Username', masked=False), + ValueBackendPassword('password', label='Password'), + ) + BROWSER = Kiwibank + + def create_default_browser(self): + return self.create_browser(self.config['username'].get(), self.config['password'].get()) + + def iter_accounts(self): + return self.browser.get_accounts() + + def get_account(self, _id): + return find_object(self.browser.get_accounts(), id=_id, error=AccountNotFound) + + def iter_history(self, account): + for transaction in self.browser.get_history(account): + yield transaction diff --git a/modules/kiwibank/pages.py b/modules/kiwibank/pages.py new file mode 100644 index 00000000..6a1f00d4 --- /dev/null +++ b/modules/kiwibank/pages.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2015 Cédric Félizard +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +import datetime +import re +from decimal import Decimal +from weboob.browser.pages import HTMLPage, LoggedPage +from weboob.capabilities.bank import Account +from weboob.tools.capabilities.bank.transactions import AmericanTransaction as EnglishTransaction + + +__all__ = ['LoginPage', 'AccountPage', 'HistoryPage'] + + +class LoginPage(HTMLPage): + def login(self, username, password): + form = self.get_form(name='aspnetForm') + form['ctl00$chi$txtUserName'] = username + form['ctl00$chi$txtPassword'] = password + form.submit() + + +class AccountPage(LoggedPage, HTMLPage): + def get_accounts(self): + for el in self.doc.getroot().cssselect('div#content tr.row'): + account = Account() + + balance = el.cssselect('td.Balance')[0].text + account.balance = Decimal(Transaction.clean_amount(balance)) + account.id = el.cssselect('span')[0].text.strip() + account.currency = u'NZD' # TODO: handle other currencies + + if el.cssselect('td.AccountName > a'): + label_el = el.cssselect('td.AccountName > a')[0] + account._link = label_el.get('href') + else: + label_el = el.cssselect('td.AccountName')[0] + account._link = None + + account.label = unicode(label_el.text.strip()) + + yield account + + +class HistoryPage(LoggedPage, HTMLPage): + def get_history(self): + # TODO: get more results from "next" page, only 15 transactions per page + for el in self.doc.getroot().cssselect('div#content tr.row'): + transaction = Transaction() + + label = unicode(el.cssselect('td.tranDesc')[0].text) + transaction.label = label + + for pattern, _type in Transaction.PATTERNS: + match = pattern.match(label) + if match: + transaction.type = _type + break + + date = el.cssselect('td.tranDate')[0].text + transaction.date = datetime.datetime.strptime(date, '%d %b \'%y') + + amount = el.cssselect('td.tranAmnt')[0].text + transaction.amount = Decimal(Transaction.clean_amount(amount)) + + yield transaction + + +class Transaction(EnglishTransaction): + PATTERNS = [ + (re.compile(r'^POS W/D (?P.*)'), EnglishTransaction.TYPE_CARD), + (re.compile(r'^ATM W/D (?P.*)'), EnglishTransaction.TYPE_WITHDRAWAL), + (re.compile(r'^(PAY|FROM) (?P.*)'), EnglishTransaction.TYPE_TRANSFER), + ] diff --git a/modules/kiwibank/test.py b/modules/kiwibank/test.py new file mode 100644 index 00000000..fcb428ad --- /dev/null +++ b/modules/kiwibank/test.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2015 Cédric Félizard +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.tools.test import BackendTest + + +__all__ = ['KiwibankTest'] + + +class KiwibankTest(BackendTest): + MODULE = 'kiwibank' + + def test_kiwibank(self): + l = list(self.backend.iter_accounts()) + if len(l) > 0: + a = l[0] + list(self.backend.iter_history(a))