From 8ecbcd601fe7588a3f77e9981db47b7531786447 Mon Sep 17 00:00:00 2001 From: Florian Duguet Date: Thu, 25 Apr 2019 12:08:55 +0200 Subject: [PATCH] [bouygues] total delete --- modules/bouygues/__init__.py | 3 - modules/bouygues/browser.py | 158 ------------------------- modules/bouygues/module.py | 77 ------------ modules/bouygues/pages.py | 219 ----------------------------------- 4 files changed, 457 deletions(-) delete mode 100644 modules/bouygues/__init__.py delete mode 100644 modules/bouygues/browser.py delete mode 100644 modules/bouygues/module.py delete mode 100644 modules/bouygues/pages.py diff --git a/modules/bouygues/__init__.py b/modules/bouygues/__init__.py deleted file mode 100644 index 0e6b4868b7..0000000000 --- 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 513b2680b2..0000000000 --- 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 227223f595..0000000000 --- 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 ddfe94cd0e..0000000000 --- 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 -- GitLab