diff --git a/modules/orange/bill/__init__.py b/modules/orange/bill/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/orange/bill/browser.py b/modules/orange/bill/browser.py
new file mode 100644
index 00000000..fae5efdd
--- /dev/null
+++ b/modules/orange/bill/browser.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012-2014 Vincent Paredes
+#
+# 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, BillsPage
+
+__all__ = ['OrangeBillBrowser']
+
+
+class OrangeBillBrowser(LoginBrowser):
+ loginpage = URL('https://id.orange.fr/auth_user/bin/auth_user.cgi', LoginPage)
+ billspage = URL('https://m.espaceclientv3.orange.fr/\?page=factures-archives',
+ 'https://.*.espaceclientv3.orange.fr/\?page=factures-archives',
+ BillsPage)
+
+ def do_login(self):
+ assert isinstance(self.username, basestring)
+ assert isinstance(self.password, basestring)
+
+ self.loginpage.stay_or_go().login(self.username, self.password)
+
+ self.billspage.go()
+ if self.loginpage.is_here():
+ raise BrowserIncorrectPassword()
+
+ def get_nb_remaining_free_sms(self):
+ raise NotImplementedError()
+
+ def post_message(self, message, sender):
+ raise NotImplementedError()
+
+ @need_login
+ def get_subscription_list(self):
+ return self.billspage.stay_or_go().get_list()
+
+ @need_login
+ def iter_bills(self, subscription):
+ return self.billspage.stay_or_go().get_bills(subid=subscription.id)
diff --git a/modules/orange/bill/pages/__init__.py b/modules/orange/bill/pages/__init__.py
new file mode 100644
index 00000000..a158b5bb
--- /dev/null
+++ b/modules/orange/bill/pages/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Vincent Paredes
+#
+# 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 .login import LoginPage
+from .bills import BillsPage
+
+__all__ = ['LoginPage', BillsPage]
diff --git a/modules/orange/bill/pages/bills.py b/modules/orange/bill/pages/bills.py
new file mode 100644
index 00000000..b2b47262
--- /dev/null
+++ b/modules/orange/bill/pages/bills.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Vincent Paredes
+#
+# 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 re
+
+from weboob.browser.pages import HTMLPage
+from weboob.capabilities.bill import Subscription
+from weboob.browser.elements import ListElement, ItemElement, method
+from weboob.browser.filters.standard import CleanDecimal, CleanText, Env, Format, Date
+from weboob.browser.filters.html import Attr
+from weboob.capabilities.bill import Bill
+
+
+class BillsPage(HTMLPage):
+ @method
+ class get_list(ListElement):
+ class item(ItemElement):
+ klass = Subscription
+
+ obj_label = CleanText('(//li[@class="n1 menuUsage toHighlight"])[1]')
+ obj_subscriber = CleanText('//div[@class="blocCompte blocPrincipal"]//h2/a')
+ obj_id = Env('id')
+
+ def parse(self, el):
+ self.env['id'] = re.sub(r'[^\d\-\.]', '', el.xpath('(//li[@class="n1 menuUsage toHighlight"])[1]//a')[0].text)
+
+ @method
+ class get_bills(ListElement):
+ item_xpath = '//ul[@class="liste fe_clearfix factures"]/li'
+
+ class item(ItemElement):
+ klass = Bill
+
+ obj__url = Attr('.//span[@class="telecharger pdf"]/a', 'href')
+ obj_id = Format('%s.%s', Env('subid'), CleanDecimal(CleanText('.//span[@class="date magic_gras magic_font13"]')))
+ obj_date = Date(CleanText('.//span[@class="date magic_gras magic_font13"]'))
+ obj_format = u"pdf"
+ obj_price = CleanDecimal('span[@class="montant"]', replace_dots=True)
+
diff --git a/modules/orange/bill/pages/login.py b/modules/orange/bill/pages/login.py
new file mode 100644
index 00000000..1708f747
--- /dev/null
+++ b/modules/orange/bill/pages/login.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Vincent Paredes
+#
+# 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.pages import HTMLPage
+
+class LoginPage(HTMLPage):
+ def login(self, login, password):
+ form = self.get_form(xpath='//form[@id="AuthentForm"]')
+ form['credential'] = login
+ form['password'] = password
+ form.submit()
+
diff --git a/modules/orange/module.py b/modules/orange/module.py
index 8f60c11d..859e1eb8 100644
--- a/modules/orange/module.py
+++ b/modules/orange/module.py
@@ -18,18 +18,38 @@
# along with weboob. If not, see .
+from weboob.capabilities.bill import CapBill, Subscription, Bill, SubscriptionNotFound, BillNotFound
from weboob.capabilities.messages import CantSendMessage, CapMessages, CapMessagesPost
+from weboob.capabilities.base import find_object
from weboob.capabilities.account import CapAccount, StatusField
from weboob.tools.backend import Module, BackendConfig
from weboob.tools.value import ValueBackendPassword, Value
from .browser import OrangeBrowser
+from .bill.browser import OrangeBillBrowser
__all__ = ['OrangeModule']
+# We need to have a switcher, CapMessages use a browser1 and
+# CapBill use a browser2
+# This will be remove when CapMessages use a browser2
+def browser_switcher(b):
+ def set_browser(func):
+ def func_wrapper(*args, **kwargs):
+ self = args[0]
+ if self._browser is None or type(self._browser) != b:
+ self.BROWSER = b
+ try:
+ self._browser = self._browsers[b]
+ except KeyError:
+ self._browsers[b] = self.create_default_browser()
+ self._browser = self._browsers[b]
+ return func(*args, **kwargs)
+ return func_wrapper
+ return set_browser
-class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost):
+class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost, CapBill):
NAME = 'orange'
MAINTAINER = u'Lucas Nussbaum'
EMAIL = 'lucas@lucas-nussbaum.net'
@@ -38,21 +58,54 @@ class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost):
LICENSE = 'AGPLv3+'
CONFIG = BackendConfig(Value('login', label='Login'),
ValueBackendPassword('password', label='Password'),
- Value('phonenumber', Label='Phone number')
+ Value('phonenumber', label='Phone number', default='')
)
- BROWSER = OrangeBrowser
ACCOUNT_REGISTER_PROPERTIES = None
+ BROWSER = OrangeBrowser
+
+
+ def __init__(self, *args, **kwargs):
+ self._browsers = dict()
+ super(OrangeModule, self).__init__(*args, **kwargs)
def create_default_browser(self):
return self.create_browser(self.config['login'].get(), self.config['password'].get())
+ @browser_switcher(OrangeBrowser)
def get_account_status(self):
- with self.browser:
- return (StatusField('nb_remaining_free_sms', 'Number of remaining free SMS',
+ return (StatusField('nb_remaining_free_sms', 'Number of remaining free SMS',
self.browser.get_nb_remaining_free_sms()),)
+ @browser_switcher(OrangeBrowser)
def post_message(self, message):
if not message.content.strip():
raise CantSendMessage(u'Message content is empty.')
- with self.browser:
- self.browser.post_message(message, self.config['phonenumber'].get())
+ self.browser.post_message(message, self.config['phonenumber'].get())
+
+ @browser_switcher(OrangeBillBrowser)
+ def iter_subscription(self):
+ return self.browser.get_subscription_list()
+
+ @browser_switcher(OrangeBillBrowser)
+ def get_subscription(self, _id):
+ return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound)
+
+ @browser_switcher(OrangeBillBrowser)
+ def get_bill(self, _id):
+ subid = _id.split('.')[0]
+ subscription = self.get_subscription(subid)
+
+ return find_object(self.iter_bills(subscription), id=_id, error=BillNotFound)
+
+ @browser_switcher(OrangeBillBrowser)
+ def iter_bills(self, subscription):
+ if not isinstance(subscription, Subscription):
+ subscription = self.get_subscription(subscription)
+ return self.browser.iter_bills(subscription)
+
+ @browser_switcher(OrangeBillBrowser)
+ def download_bill(self, bill):
+ if not isinstance(bill, Bill):
+ bill = self.get_bill(bill)
+ return self.browser.open(bill._url).content
+