diff --git a/modules/hsbchk/__init__.py b/modules/hsbchk/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a34dd8936fd6677d665acb31bc42a749e2a4f434
--- /dev/null
+++ b/modules/hsbchk/__init__.py
@@ -0,0 +1,3 @@
+from .module import HSBCHKModule
+
+__all__ = ['HSBCHKModule']
diff --git a/modules/hsbchk/browser.py b/modules/hsbchk/browser.py
new file mode 100755
index 0000000000000000000000000000000000000000..bb3b017ddc406ebf50d835fb24ce20c339d797b0
--- /dev/null
+++ b/modules/hsbchk/browser.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012-2013 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+from datetime import timedelta, date, datetime
+from dateutil import parser
+
+from weboob.exceptions import NoAccountsException
+from weboob.browser import PagesBrowser, URL, need_login, StatesMixin
+from weboob.browser.selenium import SubSeleniumMixin
+from weboob.browser.exceptions import LoggedOut, ClientError
+
+from .pages.account_pages import (
+ OtherPage, JsonAccSum, JsonAccHist
+)
+
+from .sbrowser import LoginBrowser
+
+__all__ = ['HSBCHK']
+
+
+class HSBCHK(StatesMixin, SubSeleniumMixin, PagesBrowser):
+ BASEURL = 'https://www.services.online-banking.hsbc.com.hk/gpib/group/gpib/cmn/layouts/default.html?uid=dashboard'
+
+ STATE_DURATION = 15
+
+ app_gone = False
+
+ acc_summary = URL(r'https://www.services.online-banking.hsbc.com.hk/gpib/channel/proxy/accountDataSvc/rtrvAcctSumm', JsonAccSum)
+ acc_history = URL('https://www.services.online-banking.hsbc.com.hk/gpib/channel/proxy/accountDataSvc/rtrvTxnSumm', JsonAccHist)
+
+ # catch-all
+ other_page = URL(
+ r' https://www.services.online-banking.hsbc.com.hk/gpib/systemErrorRedirect.html.*',
+ OtherPage)
+
+ __states__ = ('auth_token', 'logged', 'selenium_state')
+
+ def __init__(self, username, password, secret, *args, **kwargs):
+ super(HSBCHK, self).__init__(*args, **kwargs)
+ # accounts index changes at each session
+ self.accounts_dict_idx = None
+ self.username = username
+ self.password = password
+ self.secret = secret
+ self.logged = False
+ self.auth_token = None
+
+ def create_selenium_browser(self):
+ dirname = self.responses_dirname
+ if dirname:
+ dirname += '/selenium'
+
+ return LoginBrowser(
+ self.username,
+ self.password,
+ self.secret,
+ logger=self.logger,
+ responses_dirname=dirname,
+ proxy=self.PROXIES
+ )
+
+ def load_selenium_session(self, selenium):
+ super(HSBCHK, self).load_selenium_session(selenium)
+ self.location(
+ selenium.url,
+ referrer="https://www.security.online-banking.hsbc.com.hk/gsa/SaaS30Resource/"
+ )
+
+ def load_state(self, state):
+ if ('expire' in state and parser.parse(state['expire']) > datetime.now()) or state.get('auth_token'):
+ return super(HSBCHK, self).load_state(state)
+
+ def open(self, *args, **kwargs):
+ try:
+ return super(HSBCHK, self).open(*args, **kwargs)
+ except ClientError as e:
+ if e.response.status_code == 401:
+ self.auth_token = None
+ self.logged = False
+ self.session.cookies.clear()
+ raise LoggedOut()
+ if e.response.status_code == 409:
+ raise NoAccountsException()
+ raise
+
+ def do_login(self):
+ self.auth_token = None
+ self.logger.debug("currrent state is Logged:%s", self.logged)
+ super(HSBCHK, self).do_login()
+ self.auth_token = self.session.cookies.get('SYNC_TOKEN')
+ self.logged = True
+
+ @need_login
+ def iter_accounts(self):
+ # on new session initialize accounts dict
+ if not self.accounts_dict_idx:
+ self.accounts_dict_idx = dict()
+
+ self.update_header()
+ jq = {"accountSummaryFilter":{"txnTypCdes":[],"entityCdes":[{"ctryCde":"HK","grpMmbr":"HBAP"}]}}
+ for a in self.acc_summary.go(json = jq).iter_accounts():
+ self.accounts_dict_idx[a.id] = a
+ yield a
+
+ @need_login
+ def get_history(self, account, coming=False, retry_li=True):
+ if not self.accounts_dict_idx:
+ self.iter_accounts()
+
+ self.update_header()
+
+ today = date.today()
+ fromdate = today - timedelta(100)
+ jq = {
+ "retreiveTxnSummaryFilter": {
+ "txnDatRnge": {
+ "fromDate": fromdate.isoformat(),
+ "toDate": today.isoformat()
+ },
+ "numOfRec": -1,
+ "txnAmtRnge": None,
+ "txnHistType": None # "U"
+ },
+ "acctIdr": {
+ "acctIndex": self.accounts_dict_idx[account.id]._idx,
+ "entProdTypCde": account._entProdTypCde,
+ "entProdCatCde": account._entProdCatCde
+ },
+ "pagingInfo": {
+ "startDetail": None,
+ "pagingDirectionCode": "PD"
+ },
+ "extensions": None
+ }
+ try:
+ self.acc_history.go(json = jq)
+ except NoAccountsException:
+ return []
+ return self.page.iter_history()
+
+ def update_header(self):
+ self.session.headers.update({
+ "Origin":"https://www.services.online-banking.hsbc.com.hk",
+ "Referer":"https://www.services.online-banking.hsbc.com.hk/gpib/group/gpib/cmn/layouts/default.html?uid=dashboard",
+ "Content-type":"application/json",
+ "X-HDR-Synchronizer-Token": self.session.cookies.get('SYNC_TOKEN')
+ })
diff --git a/modules/hsbchk/favicon.png b/modules/hsbchk/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9c389378edb506f2f54118cac615b5c868830e2
Binary files /dev/null and b/modules/hsbchk/favicon.png differ
diff --git a/modules/hsbchk/module.py b/modules/hsbchk/module.py
new file mode 100755
index 0000000000000000000000000000000000000000..d78cc669d9a7da9523915ead9c320f9a496385f7
--- /dev/null
+++ b/modules/hsbchk/module.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012-2013 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+#from weboob.capabilities.bank import CapBankWealth, AccountNotFound
+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 HSBCHK
+
+
+__all__ = ['HSBCHKModule']
+
+
+class HSBCHKModule(Module, CapBank):
+ NAME = 'hsbchk'
+ MAINTAINER = u'sinopsysHK'
+ EMAIL = 'sinofwd@gmail.com'
+ VERSION = '1.6'
+ LICENSE = 'LGPLv3+'
+ DESCRIPTION = 'HSBC Hong Kong'
+ CONFIG = BackendConfig(ValueBackendPassword('login', label='User identifier', masked=False),
+ ValueBackendPassword('password', label='Password'),
+ ValueBackendPassword('secret', label=u'Memorable answer'))
+ BROWSER = HSBCHK
+
+ def create_default_browser(self):
+ return self.create_browser(
+ self.config['login'].get(),
+ self.config['password'].get(),
+ self.config['secret'].get()
+ )
+
+ def iter_accounts(self):
+ for account in self.browser.iter_accounts():
+ yield account
+
+ def get_account(self, _id):
+ return find_object(self.browser.iter_accounts(), id=_id, error=AccountNotFound)
+
+ def iter_history(self, account):
+ for tr in self.browser.get_history(account):
+ yield tr
+
+ def iter_investment(self, account):
+ raise NotImplemented
+
+ def iter_coming(self, account):
+ # No coming entries on HSBC HK
+ return []
diff --git a/modules/hsbchk/pages/__init__.py b/modules/hsbchk/pages/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/modules/hsbchk/pages/account_pages.py b/modules/hsbchk/pages/account_pages.py
new file mode 100755
index 0000000000000000000000000000000000000000..2e5bc308b522428931805d185ab7d27a8e864ea6
--- /dev/null
+++ b/modules/hsbchk/pages/account_pages.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2012 Julien Veyssier
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+import re
+from decimal import Decimal
+import requests
+import json
+
+from weboob.browser.elements import DictElement, ItemElement, method
+from weboob.browser.filters.json import Dict
+from weboob.browser.filters.standard import (
+ CleanDecimal, CleanText, Date
+)
+from weboob.browser.pages import HTMLPage, LoggedPage, pagination, JsonPage
+from weboob.capabilities.bank import Account
+from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword
+from weboob.tools.capabilities.bank.transactions import FrenchTransaction
+
+
+class Transaction(FrenchTransaction):
+ PATTERNS = [
+ (re.compile(r'^PAYMENT - THANK YOU'), FrenchTransaction.TYPE_CARD_SUMMARY),
+ (re.compile(r'^CREDIT CARD PAYMENT (?P.*)'), FrenchTransaction.TYPE_CARD_SUMMARY),
+ (re.compile(r'^CREDIT INTEREST'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'DEBIT INTEREST'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^UNAUTHORIZED OD CHARGE'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^ATM WITHDRAWAL\ *\((?P\d{2})(?P\w{3})(?P\d{2})\)'), FrenchTransaction.TYPE_WITHDRAWAL),
+ (re.compile(r'^POS CUP\ *\((?P\d{2})(?P\w{3})(?P\d{2})\)\ *(?P.*)'), FrenchTransaction.TYPE_CARD),
+ (re.compile(r'^EPS\d*\ *\((?P\d{2})(?P\w{3})(?P\d{2})\)\ *(?P.*)'), FrenchTransaction.TYPE_CARD),
+ (re.compile(r'^CR TO (?P.*)\((?P\d{2})(?P\w{3})(?P\d{2})\)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^FROM (?P.*)\((?P\d{2})(?P\w{3})(?P\d{2})\)'), FrenchTransaction.TYPE_TRANSFER),
+ ]
+
+
+class JsonBasePage(JsonPage):
+
+ def on_load(self):
+ coderet = Dict('responseInfo/reasons/0/code')(self.doc)
+ conditions = (
+ coderet == '000',
+ )
+ assert any(conditions), 'Error %s is not handled yet' % coderet
+
+
+class JsonAccSum(LoggedPage, JsonBasePage):
+
+ PRODUCT_TO_TYPE = {
+ u"SAV": Account.TYPE_SAVINGS,
+ u"CUR": Account.TYPE_CHECKING,
+ u"TD": Account.TYPE_DEPOSIT,
+ u"INV": Account.TYPE_MARKET,
+ u"CC": Account.TYPE_CARD,
+ }
+ LABEL = {
+ u"SAV": "Savings",
+ u"CUR": "Current",
+ u"TD": "Time deposit",
+ u"INV": "Investment",
+ u"CC": "Credit card",
+ }
+
+ def get_acc_dict(self, json):
+ acc_dict = {}
+ if json.get('hasAcctDetails'):
+ acc_dict = {
+ 'bank_name': 'HSBC HK',
+ 'id': '%s-%s-%s' % (
+ json.get('displyID'),
+ json.get('prodCatCde'),
+ json.get('ldgrBal').get('ccy')
+ ),
+ '_idx': json.get('acctIndex'),
+ '_entProdCatCde': json.get('entProdCatCde'),
+ '_entProdTypCde': json.get('entProdTypCde'),
+ 'number': json.get('displyID'),
+ 'type': self.PRODUCT_TO_TYPE.get(json.get('prodCatCde')) or Account.TYPE_UNKNOWN,
+ 'label': '%s %s' % (
+ self.LABEL.get(json.get('prodCatCde')),
+ json.get('ldgrBal').get('ccy')
+ ),
+ 'currency': json.get('ldgrBal').get('ccy'),
+ 'balance': Decimal(json.get('ldgrBal').get('amt')),
+ }
+ else:
+ acc_dict = {
+ 'bank_name': 'HSBC HK',
+ 'id': '%s-%s' % (
+ json.get('displyID'),
+ json.get('prodCatCde')
+ ),
+ '_idx': json.get('acctIndex'),
+ 'number': json.get('displyID'),
+ 'type': self.PRODUCT_TO_TYPE.get(json.get('prodCatCde')) or Account.TYPE_UNKNOWN,
+ 'label': '%s' % (
+ self.LABEL.get(json.get('prodCatCde'))
+ )
+ }
+ return acc_dict
+
+ def iter_accounts(self):
+
+ for country in self.get('countriesAccountList'):
+ for acc in country.get('acctLiteWrapper'):
+ acc_prod = acc.get('prodCatCde')
+ if acc_prod == "MST":
+ for subacc in acc.get('subAcctInfo'):
+ res = Account.from_dict(self.get_acc_dict(subacc))
+ if subacc.get('hasAcctDetails'):
+ yield res
+ else:
+ self.logger.debug("skip account with no history: %s", res)
+ elif acc_prod == "CC":
+ self.logger.debug("acc: %s", str(acc))
+ res = Account.from_dict(self.get_acc_dict(acc))
+ if acc.get('hasAcctDetails'):
+ yield res
+ else:
+ self.logger.debug("skip account with no history: %s", res)
+ else:
+ self.logger.error("Unknown account product code [%s]", acc_prod)
+
+
+class JsonAccHist(LoggedPage, JsonBasePage):
+ @pagination
+ @method
+ class iter_history(DictElement):
+
+ item_xpath = "txnSumm"
+
+ def next_page(self):
+ self.logger.debug(
+ "paging (%s): %s",
+ Dict('responsePagingInfo/moreRecords', default='N')(self.page.doc),
+ self.page.doc.get('responsePagingInfo'))
+ if Dict('responsePagingInfo/moreRecords', default='N')(self.page.doc) == 'Y':
+ self.logger.info("more values are available")
+ """
+ prev_req = self.page.response.request
+ jq = json.loads(prev_req.body)
+ jq['pagingInfo']['startDetail']=Dict('responsePagingInfo/endDetail')(self.page.doc)
+ return requests.Request(
+ self.page.url,
+ headers = prev_req.headers,
+ json = jq
+ )
+ """
+ return
+
+ class item(ItemElement):
+ klass = Transaction
+
+ obj_rdate = Date(Dict('txnDate'))
+ obj_date = Date(Dict('txnPostDate'))
+ obj_amount = CleanDecimal(Dict('txnAmt/amt'))
+
+ def obj_raw(self):
+ return Transaction.Raw(Dict('txnDetail/0'))(self)
+
+ def obj_type(self):
+ for pattern, type in Transaction.PATTERNS:
+ if pattern.match(Dict('txnDetail/0')(self)):
+ return type
+
+ if Dict('txnHistType', default=None)(self) in ['U', 'B']:
+ return Transaction.TYPE_CARD
+ return Transaction.TYPE_TRANSFER
+
+
+class AppGonePage(HTMLPage):
+ def on_load(self):
+ self.browser.app_gone = True
+ self.logger.info('Application has gone. Relogging...')
+ self.browser.do_logout()
+ self.browser.do_login()
+
+
+class OtherPage(HTMLPage):
+ ERROR_CLASSES = [
+ ('Votre contrat est suspendu', ActionNeeded),
+ ("Vos données d'identification (identifiant - code secret) sont incorrectes", BrowserIncorrectPassword),
+ ('Erreur : Votre contrat est clôturé.', ActionNeeded),
+ ]
+
+ def on_load(self):
+ for msg, exc in self.ERROR_CLASSES:
+ for tag in self.doc.xpath('//p[@class="debit"]//strong[text()[contains(.,$msg)]]', msg=msg):
+ raise exc(CleanText('.')(tag))
diff --git a/modules/hsbchk/pages/login.py b/modules/hsbchk/pages/login.py
new file mode 100755
index 0000000000000000000000000000000000000000..ab6530d283f94d24a29ac0108e3bc5303ed1efcd
--- /dev/null
+++ b/modules/hsbchk/pages/login.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2012 Julien Veyssier
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+from weboob.browser.filters.standard import (
+ CleanText
+)
+from weboob.browser.selenium import (
+ SeleniumPage, VisibleXPath
+)
+from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable
+
+from selenium.webdriver.common.by import By
+
+
+class LoginPage(SeleniumPage):
+
+ is_here = VisibleXPath('//h2[text()[contains(.,"Log on to Internet Banking")]]')
+
+ @property
+ def logged(self):
+ if self.doc.xpath('//p[contains(text(), "You are now being redirected to your Personal Internet Banking.")]'):
+ return True
+ return False
+
+ def on_load(self):
+ for message in self.doc.xpath('//div[contains(@class, "alertBox")]'):
+ error_msg = CleanText('.')(message)
+ if any(msg in error_msg for msg in ['The username you entered doesn\'t match our records. Please try again.',
+ 'Please enter your memorable answer and password.',
+ 'The information you entered does not match our records. Please try again.',
+ 'mot de passe invalide']):
+ raise BrowserIncorrectPassword(error_msg)
+ else:
+ raise BrowserUnavailable(error_msg)
+
+ def get_error(self):
+ for message in self.doc.xpath('//div[contains(@data-dojo-type, "hsbcwidget/alertBox")]'):
+ error_msg = CleanText('.')(message)
+ if any(msg in error_msg for msg in ['The username you entered doesn\'t match our records. Please try again.',
+ 'Please enter a valid Username.',
+ 'mot de passe invalide']):
+ raise BrowserIncorrectPassword(error_msg)
+ else:
+ raise BrowserUnavailable(error_msg)
+ return
+
+ def login(self, login):
+ self.driver.find_element_by_name("userid").send_keys(login)
+ self.driver.find_element_by_class_name("submit_input").click()
+
+ def get_no_secure_key(self):
+ self.driver.find_element_by_xpath('//a[span[contains(text(), "Without Security Device")]]').click()
+
+ def login_w_secure(self, password, secret):
+ self.driver.find_element_by_name("memorableAnswer").send_keys(secret)
+ if len(password) < 8:
+ raise BrowserIncorrectPassword('The password must be at least %d characters' % 8)
+ elif len(password) > 8:
+ # HSBC only use 6 first and last two from the password
+ password = password[:6] + password[-2:]
+
+ elts = self.driver.find_elements(By.XPATH, "//input[@type='password' and contains(@id,'pass')]")
+ for elt in elts:
+ if elt.get_attribute('disabled') is None and elt.get_attribute('class') == "smallestInput active":
+ elt.send_keys(password[int(elt.get_attribute('id')[-1]) - 1])
+ self.driver.find_element_by_xpath("//input[@class='submit_input']").click()
diff --git a/modules/hsbchk/sbrowser.py b/modules/hsbchk/sbrowser.py
new file mode 100755
index 0000000000000000000000000000000000000000..1542a5510e11fcb8c3d0676b642de8d0d25563da
--- /dev/null
+++ b/modules/hsbchk/sbrowser.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012-2013 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+import os
+
+try:
+ from selenium import webdriver
+except ImportError:
+ raise ImportError('Please install python-selenium')
+
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.proxy import Proxy, ProxyType
+from selenium.common.exceptions import (
+ TimeoutException
+)
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, BrowserHTTPError
+from weboob.browser.selenium import (SeleniumBrowser, DirFirefoxProfile, VisibleXPath)
+from weboob.browser import URL
+
+from .pages.login import (
+ LoginPage
+)
+
+class LoginBrowser(SeleniumBrowser):
+ BASEURL = 'https://www.hsbc.com.hk/'
+
+ #DRIVER = webdriver.Remote
+
+ app_gone = False
+
+ preconnection = URL(r'https://www.ebanking.hsbc.com.hk/1/2/logon?LANGTAG=en&COUNTRYTAG=US', LoginPage)
+ login = URL(r'https://www.security.online-banking.hsbc.com.hk/gsa/SaaS30Resource/*', LoginPage)
+
+ def __init__(self, username, password, secret, *args, **kwargs):
+ super(LoginBrowser, self).__init__(*args, **kwargs)
+ self.username = username
+ self.password = password
+ self.secret = secret
+ self.web_space = None
+ self.home_url = None
+
+ def _build_capabilities(self):
+ capa = super(LoginBrowser, self)._build_capabilities()
+ capa['marionette'] = True
+ return capa
+
+ def _setup_driver(self):
+ proxy = Proxy()
+ proxy.proxy_type = ProxyType.DIRECT
+ if 'http' in self.proxy:
+ proxy.http_proxy = self.proxy['http']
+ if 'https' in self.proxy:
+ proxy.ssl_proxy = self.proxy['https']
+
+ capa = self._build_capabilities()
+ proxy.add_to_capabilities(capa)
+
+ options = self._build_options()
+ # TODO some browsers don't need headless
+ # TODO handle different proxy setting?
+ options.set_headless(self.HEADLESS)
+
+ if self.DRIVER is webdriver.Firefox:
+ if self.responses_dirname and not os.path.isdir(self.responses_dirname):
+ os.makedirs(self.responses_dirname)
+
+ options.profile = DirFirefoxProfile(self.responses_dirname)
+ if self.responses_dirname:
+ capa['profile'] = self.responses_dirname
+ self.driver = self.DRIVER(options=options, capabilities=capa)
+ elif self.DRIVER is webdriver.Chrome:
+ self.driver = self.DRIVER(options=options, desired_capabilities=capa)
+ elif self.DRIVER is webdriver.PhantomJS:
+ if self.responses_dirname:
+ if not os.path.isdir(self.responses_dirname):
+ os.makedirs(self.responses_dirname)
+ log_path = os.path.join(self.responses_dirname, 'selenium.log')
+ else:
+ log_path = NamedTemporaryFile(prefix='weboob_selenium_', suffix='.log', delete=False).name
+
+ self.driver = self.DRIVER(desired_capabilities=capa, service_log_path=log_path)
+ elif self.DRIVER is webdriver.Remote:
+ # self.HEADLESS = False
+ # for debugging purpose
+ self.driver = webdriver.Remote(
+ command_executor='http://:/wd/hub',
+ desired_capabilities=DesiredCapabilities.FIREFOX)
+ else:
+ raise NotImplementedError()
+
+ if self.WINDOW_SIZE:
+ self.driver.set_window_size(*self.WINDOW_SIZE)
+
+
+ def load_state(self, state):
+ return
+
+ def do_login(self):
+ self.logger.debug("start do_login")
+
+ self.app_gone = False
+ self.preconnection.go()
+ try:
+ self.wait_until(VisibleXPath('//h2[text()[contains(.,"Log on to Internet Banking")]]'), timeout=20)
+ self.page.login(self.username)
+ self.wait_until_is_here(self.login, 10)
+ error = self.page.get_error()
+ if error:
+ raise BrowserIncorrectPassword(error)
+
+ self.page.get_no_secure_key()
+ self.wait_until_is_here(self.login, 10)
+ error = self.page.get_error()
+ if error:
+ raise BrowserHTTPError(error)
+ self.page.login_w_secure(self.password, self.secret)
+ if self.login.is_here():
+ error = self.page.get_error()
+ if error:
+ raise BrowserIncorrectPassword(error)
+ WebDriverWait(self.driver, 20).until(EC.title_contains("My banking"))
+
+ except TimeoutException as e:
+ self.logger.exception("timeout while login")
+ raise BrowserUnavailable(e.msg)
diff --git a/modules/hsbchk/test.py b/modules/hsbchk/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..92e3a6810e31bb6f49a09f268dcdae03f3270553
--- /dev/null
+++ b/modules/hsbchk/test.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.tools.test import BackendTest
+from weboob.capabilities.bank import Account
+
+class HSBCHKTest(BackendTest):
+ MODULE = 'hsbchk'
+
+ def test_hsbchk(self):
+ l = list(self.backend.iter_accounts())
+ if len(l) > 0:
+ a = l[0]
+ list(self.backend.iter_history(a))
+
+# def test_investments(self):
+# life_insurance_accounts = [account for account in self.backend.iter_accounts() if account.type == Account.TYPE_LIFE_INSURANCE]
+# investments = {acc.id: list(self.backend.iter_investment(acc)) for acc in life_insurance_accounts}
+# for acc in life_insurance_accounts:
+# invs = investments[acc.id]
+# self.assertLessEquals(sum([inv.valuation for inv in invs]), acc.balance)