diff --git a/modules/bouygues/__init__.py b/modules/bouygues/__init__.py
deleted file mode 100644
index 0e6b4868b7ae0572edf1556d8dbf507eddce48f5..0000000000000000000000000000000000000000
--- a/modules/bouygues/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .module import BouyguesModule
-
-__all__ = ['BouyguesModule']
diff --git a/modules/bouygues/browser.py b/modules/bouygues/browser.py
deleted file mode 100644
index 513b2680b27e5eff40a6c701dccf517606b444b9..0000000000000000000000000000000000000000
--- a/modules/bouygues/browser.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright(C) 2010-2015 Bezleputh
-#
-# 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 Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this weboob module. If not, see .
-
-from jose import jwt
-
-from weboob.browser import LoginBrowser, URL, need_login
-from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable
-from weboob.browser.exceptions import ClientError, HTTPNotFound
-from weboob.tools.compat import urlparse, parse_qs
-from .pages import (
- DocumentsPage, HomePage, LoginPage, SubscriberPage, SubscriptionPage, SubscriptionDetailPage,
- SendSMSPage, SendSMSErrorPage, UselessPage, DocumentFilePage, ProfilePage,
-)
-
-from weboob.capabilities.messages import CantSendMessage
-
-__all__ = ['BouyguesBrowser']
-
-
-class BouyguesBrowser(LoginBrowser):
- BASEURL = 'https://api.bouyguestelecom.fr'
- TIMEOUT = 20
-
- login = URL(r'https://www.mon-compte.bouyguestelecom.fr/cas/login', LoginPage)
- home = URL(r'https://www.bouyguestelecom.fr/mon-compte', HomePage)
- subscriber = URL(r'/personnes/(?P\d+)$', SubscriberPage)
- subscriptions = URL(r'/personnes/(?P\d+)/comptes-facturation', SubscriptionPage)
-
- subscriptions_details = URL(r'/comptes-facturation/(?P\d+)/contrats-payes', SubscriptionDetailPage)
- document_file = URL(r'/comptes-facturation/(?P\d+)/factures/.*/documents', DocumentFilePage)
- documents = URL(r'/comptes-facturation/(?P\d+)/factures', DocumentsPage)
-
- sms_page = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/sendSMS.phtml',
- r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/confirmSendSMS.phtml',
- SendSMSPage)
- confirm = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/resultSendSMS.phtml', UselessPage)
- sms_error_page = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/SMS_erreur.phtml',
- SendSMSErrorPage)
- profile = URL(r'/personnes/(?P\d+)/coordonnees', ProfilePage)
-
- def __init__(self, username, password, lastname, *args, **kwargs):
- super(BouyguesBrowser, self).__init__(username, password, *args, **kwargs)
- self.lastname = lastname
- self.headers = None
- self.id_user = None
-
- def do_login(self):
- self.login.go()
-
- if self.home.is_here():
- return
-
- self.page.login(self.username, self.password, self.lastname)
-
- if self.login.is_here():
- error = self.page.get_error()
- if error and 'mot de passe' in error:
- raise BrowserIncorrectPassword(error)
- raise AssertionError("Unhandled error at login: {}".format(error))
-
- # after login we need to get some tokens to use bouygues api
- data = {
- 'response_type': 'id_token token',
- 'client_id': 'a360.bouyguestelecom.fr',
- 'redirect_uri': 'https://www.bouyguestelecom.fr/mon-compte/'
- }
- self.location('https://oauth2.bouyguestelecom.fr/authorize', params=data)
-
- parsed_url = urlparse(self.response.url)
- fragment = parse_qs(parsed_url.fragment)
-
- if not fragment:
- query = parse_qs(parsed_url.query)
- if 'server_error' in query.get('error', []):
- raise BrowserUnavailable(query['error_description'][0])
-
- claims = jwt.get_unverified_claims(fragment['id_token'][0])
- self.headers = {'Authorization': 'Bearer %s' % fragment['access_token'][0]}
- self.id_user = claims['id_personne']
-
- @need_login
- def post_message(self, message):
- self.sms_page.go()
-
- if self.sms_error_page.is_here():
- raise CantSendMessage(self.page.get_error_message())
-
- receivers = ";".join(message.receivers) if message.receivers else self.username
- self.page.send_sms(message, receivers)
-
- if self.sms_error_page.is_here():
- raise CantSendMessage(self.page.get_error_message())
-
- self.confirm.open()
-
- @need_login
- def iter_subscriptions(self):
- self.subscriber.go(idUser=self.id_user, headers=self.headers)
- subscriber = self.page.get_subscriber()
- phone_list = self.page.get_phone_list()
-
- self.subscriptions.go(idUser=self.id_user, headers=self.headers)
- for sub in self.page.iter_subscriptions(subscriber=subscriber):
- try:
- self.subscriptions_details.go(idSub=sub.id, headers=self.headers)
- sub.label = self.page.get_label()
- sub._is_holder = self.page.is_holder()
- except ClientError:
- # if another person pay for your subscription you may not have access to this page with your credentials
- sub.label = phone_list
- if not sub.label:
- if not sub._is_holder:
- sub.label = subscriber
- else:
- # If the subscriber is the holder but the subscription does not have a phone number anyway
- # It means that the subscription has not been activated yet
- continue
- yield sub
-
- @need_login
- def iter_documents(self, subscription):
- try:
- self.location(subscription.url, headers=self.headers)
- return self.page.iter_documents(subid=subscription.id)
- except HTTPNotFound as error:
- if error.response.json()['error'] in ('facture_introuvable', 'compte_jamais_facture'):
- return []
- raise
-
- @need_login
- def download_document(self, document):
- self.location(document.url, headers=self.headers)
- return self.open(self.page.get_one_shot_download_url()).content
-
- @need_login
- def get_profile(self):
- self.subscriber.go(idUser=self.id_user, headers=self.headers)
- subscriber = self.page.get_subscriber()
-
- self.profile.go(idUser=self.id_user, headers=self.headers)
-
- return self.page.get_profile(subscriber=subscriber)
diff --git a/modules/bouygues/module.py b/modules/bouygues/module.py
deleted file mode 100644
index 227223f5952eb2ac34613e59175624c1990b1772..0000000000000000000000000000000000000000
--- a/modules/bouygues/module.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright(C) 2010-2015 Bezleputh
-#
-# 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 Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this weboob module. If not, see .
-
-from __future__ import unicode_literals
-
-from weboob.capabilities.bill import CapDocument, Subscription, Document, SubscriptionNotFound, DocumentNotFound
-from weboob.capabilities.messages import CantSendMessage, CapMessages, CapMessagesPost
-from weboob.capabilities.base import find_object
-from weboob.capabilities.profile import CapProfile
-from weboob.tools.backend import Module, BackendConfig
-from weboob.tools.value import ValueBackendPassword, Value
-
-from .browser import BouyguesBrowser
-
-
-__all__ = ['BouyguesModule']
-
-
-class BouyguesModule(Module, CapMessages, CapMessagesPost, CapDocument, CapProfile):
- NAME = 'bouygues'
- MAINTAINER = 'Bezleputh'
- EMAIL = 'carton_ben@yahoo.fr'
- VERSION = '1.6'
- DESCRIPTION = u'Bouygues Télécom French mobile phone provider'
- LICENSE = 'AGPLv3+'
- CONFIG = BackendConfig(Value('login', label='E-mail / N° de Téléphone'),
- ValueBackendPassword('password', label='Mot de passe'),
- ValueBackendPassword('lastname', label='Nom de famille', default=u''))
- BROWSER = BouyguesBrowser
-
- def create_default_browser(self):
- return self.create_browser(username=self.config['login'].get(), password=self.config['password'].get(), lastname=self.config['lastname'].get())
-
- def post_message(self, message):
- if not message.content.strip():
- raise CantSendMessage('Message content is empty.')
- self.browser.post_message(message)
-
- def iter_subscription(self):
- return self.browser.iter_subscriptions()
-
- def get_subscription(self, _id):
- return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound)
-
- def get_document(self, _id):
- subid = _id.rsplit('_', 1)[0]
- subscription = self.get_subscription(subid)
- return find_object(self.iter_documents(subscription), id=_id, error=DocumentNotFound)
-
- def iter_documents(self, subscription):
- if not isinstance(subscription, Subscription):
- subscription = self.get_subscription(subscription)
- return self.browser.iter_documents(subscription)
-
- def download_document(self, document):
- if not isinstance(document, Document):
- document = self.get_document(document)
- return self.browser.download_document(document)
-
- def get_profile(self):
- return self.browser.get_profile()
diff --git a/modules/bouygues/pages.py b/modules/bouygues/pages.py
deleted file mode 100644
index ddfe94cd0e586c0fc6893d7568741388cf8f9671..0000000000000000000000000000000000000000
--- a/modules/bouygues/pages.py
+++ /dev/null
@@ -1,219 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright(C) 2010-2015 Bezleputh
-#
-# 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 Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this weboob module. If not, see .
-
-from __future__ import unicode_literals
-
-import re
-from datetime import datetime, timedelta
-
-from weboob.capabilities.messages import CantSendMessage
-from weboob.exceptions import BrowserIncorrectPassword, ParseError
-
-from weboob.capabilities.base import NotLoaded
-from weboob.capabilities.bill import Bill, Subscription
-from weboob.capabilities.profile import Profile
-from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage, PDFPage
-from weboob.browser.filters.json import Dict
-from weboob.browser.filters.standard import CleanDecimal, CleanText, Env, Format, Regexp
-from weboob.browser.elements import DictElement, ItemElement, method
-
-
-class LoginPage(HTMLPage):
- def login(self, login, password, lastname):
- form = self.get_form(id='log_data')
-
- form['username'] = login
- form['password'] = password
-
- if 'lastname' in form:
- if not lastname:
- raise BrowserIncorrectPassword('Le nom de famille est obligatoire.')
- form['lastname'] = lastname
-
- form.submit()
-
- def get_error(self):
- return CleanText('//div[@id="alert_msg"]//p')(self.doc)
-
-
-class HomePage(LoggedPage, HTMLPage):
- pass
-
-
-class SubscriberPage(LoggedPage, JsonPage):
- def get_subscriber(self):
- if self.doc['type'] == 'INDIVIDU':
- sub_dict = self.doc
- else:
- sub_dict = self.doc['representantLegal']
- return "%s %s %s" % (sub_dict['civilite'], sub_dict['prenom'], sub_dict['nom'])
-
- def get_phone_list(self):
- num_tel_list = []
- for phone in self.doc.get('comptesAcces', []):
- num_tel_list.append(' '.join(phone[i:i + 2] for i in range(0, len(phone), 2)))
-
- return ' - '.join(num_tel_list)
-
-
-class SubscriptionPage(LoggedPage, JsonPage):
- @method
- class iter_subscriptions(DictElement):
- item_xpath = 'items'
-
- class item(ItemElement):
- klass = Subscription
-
- obj_id = Dict('id')
- obj_url = Dict('_links/factures/href')
- obj_subscriber = Env('subscriber')
-
-
-class SubscriptionDetailPage(LoggedPage, JsonPage):
- def get_label(self):
- label_list = []
- for s in self.doc['items']:
- if 'numeroTel' in s:
- phone = re.sub(r'^\+\d{2}', '0', s['numeroTel'])
- label_list.append(' '.join([phone[i:i + 2] for i in range(0, len(phone), 2)]))
- else:
- continue
-
- return ' - '.join(label_list)
-
- def is_holder(self):
- return any(CleanText(Dict('utilisateur/libelleProfilDroits'), default=None)(s) == 'Accès titulaire' for s in self.doc['items'] if 'utilisateur' in s)
-
-
-class SendSMSPage(HTMLPage):
- def send_sms(self, message, receivers):
- sms_number = CleanDecimal(Regexp(CleanText('//span[@class="txt12-o"][1]/strong'), r'(\d*) SMS.*'))(self.doc)
-
- if sms_number == 0:
- msg = CleanText('//span[@class="txt12-o"][1]')(self.doc)
- raise CantSendMessage(msg)
-
- form = self.get_form('//form[@name="formSMS"]')
- form["fieldMsisdn"] = receivers
- form["fieldMessage"] = message.content
-
- form.submit()
-
-
-class SendSMSErrorPage(HTMLPage):
- def get_error_message(self):
- return CleanText('//span[@class="txt12-o"][1]')(self.doc)
-
-
-class DocumentsPage(LoggedPage, JsonPage):
- FRENCH_MONTHS = {
- 1: 'Janvier',
- 2: 'Février',
- 3: 'Mars',
- 4: 'Avril',
- 5: 'Mai',
- 6: 'Juin',
- 7: 'Juillet',
- 8: 'Août',
- 9: 'Septembre',
- 10: 'Octobre',
- 11: 'Novembre',
- 12: 'Décembre',
- }
-
- @method
- class iter_documents(DictElement):
- item_xpath = 'items'
-
- class item(ItemElement):
- klass = Bill
-
- obj_id = Format('%s_%s', Env('subid'), Dict('idFacture'))
-
- def obj_url(self):
- try:
- link = Dict('_links/facturePDF/href')(self)
- except ParseError:
- # yes, sometimes it's just a misspelling word, but just sometimes...
- link = Dict('_links/facturePDFDF/href')(self)
-
- return 'https://api.bouyguestelecom.fr%s' % link
-
- obj_date = Env('date')
- obj_duedate = Env('duedate')
- obj_format = 'pdf'
- obj_label = Env('label')
- obj_price = CleanDecimal(Dict('mntTotFacture'))
- obj_currency = 'EUR'
-
- def parse(self, el):
- bill_date = datetime.strptime(Dict('dateFacturation')(self), "%Y-%m-%dT%H:%M:%SZ").date()
-
- # dateFacturation is like: 'YYYY-MM-DDTHH:00:00Z' where Z is UTC time and HH 23 in winter and 22 in summer
- # which always correspond to the day after at midnight in French time zone
- # so we remove hour and consider the day after as date (which is also the date inside pdf)
- self.env['date'] = bill_date + timedelta(days=1)
-
- duedate = Dict('dateLimitePaieFacture', default=NotLoaded)(self)
- if duedate:
- self.env['duedate'] = datetime.strptime(duedate, "%Y-%m-%dT%H:%M:%SZ").date() + timedelta(days=1)
- else:
- # for some connections we don't have duedate (why ?)
- self.env['duedate'] = NotLoaded
-
- self.env['label'] = "%s %d" % (self.page.FRENCH_MONTHS[self.env['date'].month], self.env['date'].year)
-
- def get_one_shot_download_url(self):
- return self.doc['_actions']['telecharger']['action']
-
-
-class ProfilePage(LoggedPage, JsonPage):
- def get_profile(self, subscriber):
- data = self.doc
-
- last_address = data['adressesPostales'][0]
- for address in data['adressesPostales']:
- if address['dateMiseAJour'] > last_address['dateMiseAJour']:
- last_address = address
-
- p = Profile()
- p.name = subscriber
- p.address = '%s %s %s %s' % (last_address['numero'], last_address['rue'],
- last_address['codePostal'], last_address['ville'])
- p.country = last_address['pays']
-
- for email in data['emails']:
- if email['emailPrincipal']:
- p.email = email['email']
- break
-
- if 'telephones' in data:
- for phone in data['telephones']:
- if phone['telephonePrincipal']:
- p.phone = phone['numero']
- break
-
- return p
-
-
-class UselessPage(HTMLPage):
- pass
-
-
-class DocumentFilePage(PDFPage):
- pass