class BoursoramaModule(Module, CapBankTransferAddRecipient, CapProfile, CapContact): NAME = 'boursorama' MAINTAINER = u'Gabriel Kerneis' EMAIL = '*****@*****.**' VERSION = '1.3' LICENSE = 'AGPLv3+' DESCRIPTION = u'Boursorama' CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', masked=False), ValueBackendPassword('password', label='Mot de passe'), ValueBool('enable_twofactors', label='Send validation sms', default=False), Value('device', label='Device name', regexp='\w*', default=''), Value('pin_code', label='Sms code', required=False), ) BROWSER = BoursoramaBrowser def create_default_browser(self): return self.create_browser(self.config) def iter_accounts(self): return self.browser.get_accounts_list() def get_account(self, _id): account = self.browser.get_account(_id) if account: return account else: raise AccountNotFound() def iter_history(self, account): for tr in self.browser.get_history(account): if not tr._is_coming: yield tr def iter_coming(self, account): for tr in self.browser.get_history(account, coming=True): if tr._is_coming: yield tr def iter_investment(self, account): return self.browser.get_investment(account) def get_profile(self): return self.browser.get_profile() def iter_contacts(self): return self.browser.get_advisor() def iter_transfer_recipients(self, account): if not isinstance(account, Account): account = self.get_account(account) return self.browser.iter_transfer_recipients(account) def init_transfer(self, transfer, **kwargs): return self.browser.init_transfer(transfer, **kwargs) def new_recipient(self, recipient, **kwargs): return self.browser.new_recipient(recipient, **kwargs) def execute_transfer(self, transfer, **kwargs): return self.browser.execute_transfer(transfer, **kwargs)
class BoursoramaModule(Module, CapBank): NAME = 'boursorama' MAINTAINER = u'Gabriel Kerneis' EMAIL = '*****@*****.**' VERSION = '1.2' LICENSE = 'AGPLv3+' DESCRIPTION = u'Boursorama' CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', masked=False), ValueBackendPassword('password', label='Mot de passe'), ValueBool('enable_twofactors', label='Send validation sms', default=False), Value('device', label='Device name', regexp='\w*', default=''), Value('pin_code', label='Sms code', required=False), ) BROWSER = Boursorama def create_default_browser(self): return self.create_browser(self.config) def iter_accounts(self): return self.browser.get_accounts_list() def get_account(self, _id): with self.browser: account = self.browser.get_account(_id) if account: return account else: raise AccountNotFound() def iter_history(self, account): return self.browser.get_history(account) def iter_investment(self, account): return self.browser.get_investment(account)
class ZerobinModule(Module, CapPaste): NAME = 'zerobin' DESCRIPTION = u'ZeroBin/0bin/PrivateBin encrypted pastebin' MAINTAINER = u'Vincent A' EMAIL = '*****@*****.**' LICENSE = 'AGPLv3+' VERSION = '1.6' CONFIG = BackendConfig( Value('url', label='URL of the zerobin/0bin/privatebin', regexp='https?://.*', default='https://zerobin.net'), ValueBool('discussion', label='Allow paste comments (ZeroBin only)', default=False), ) BROWSER = ZerobinBrowser def create_default_browser(self): return self.create_browser(self.config['url'].get(), self.config['discussion'].get()) def can_post(self, contents, title=None, public=None, max_age=None): """ Checks if the paste can be pasted by this backend. Some properties are considered required (public/private, max_age) while others are just bonuses (language). contents: Can be used to check encodability, maximum length, etc. title: Can be used to check length, allowed characters. Should not be required. public: True must be public, False must be private, None do not care. max_age: Maximum time to live in seconds. A score of 0 means the backend is not suitable. A score of 1 means the backend is suitable. Higher scores means it is more suitable than others with a lower score. :rtype: int :returns: score """ if public: return 0 return self.browser.can_post(contents, max_age) def get_paste(self, id): if '#' not in id: return elif id.startswith('http://') or id.startswith('https://'): if not id.startswith(self.config['url'].get()): return return self.browser.get_paste(id) def new_paste(self, *args, **kwargs): return ZeroPaste(*args, **kwargs) def post_paste(self, paste, max_age=None): self.browser.post_paste(paste, max_age)
def new_recipient(self, recipient, **params): if 'code' in params: # for sms authentication return self.send_code(recipient, **params) # prepare commun data for all authentication method data = {} data['adresseBeneficiaire'] = '' data['iban'] = recipient.iban data['libelleBeneficiaire'] = recipient.label data['notification'] = True data['typeBeneficiaire'] = '' # provisional if self.digital_key: if 'digital_key' in params: return self.new_recipient_digital_key(recipient, data) # need to be on recipient page send sms or mobile notification # needed to get the phone number, enabling the possibility to send sms. # all users with validated phone number can receive sms code self.recipients.go(data=JSON({'type': 'TOUS'})) # check type of recipient activation type_activation = 'sms' # provisional if self.digital_key: if self.page.has_digital_key(): # force users with digital key activated to use digital key authentication type_activation = 'digital_key' if type_activation == 'sms': # post recipient data sending sms with same request data['typeEnvoi'] = 'SMS' recipient = self.add_recip.go(data=json.dumps(data), headers={ 'Content-Type': 'application/json' }).get_recipient(recipient) raise AddRecipientStep( recipient, Value('code', label='Saisissez le code reçu par SMS.')) elif type_activation == 'digital_key': # recipient validated with digital key are immediatly available recipient.enabled_date = datetime.today() raise AddRecipientStep( recipient, ValueBool( 'digital_key', label= 'Validez pour recevoir une demande sur votre application bancaire. La validation de votre bénéficiaire peut prendre plusieurs minutes.' ))
def double_auth(self, recipient): try: form = self.get_form(id='formCache') except FormNotFound: raise AddRecipientError('form not found') self.browser.context = form['context'] self.browser.dup = form['dup'] self.browser.logged = 1 getsigninfo_data = {} getsigninfo_data['b64_jeton_transaction'] = form['context'] getsigninfo_data['action_level'] = self.get_action_level() r = self.browser.open( 'https://particuliers.secure.societegenerale.fr/sec/getsigninfo.json', data=getsigninfo_data) assert r.page.doc['commun']['statut'] == 'ok' recipient = self.get_recipient_object(recipient, get_info=True) self.browser.page = None if r.page.doc['donnees']['sign_proc'] == 'csa': send_data = {} send_data['csa_op'] = 'sign' send_data['context'] = form['context'] r = self.browser.open( 'https://particuliers.secure.societegenerale.fr/sec/csa/send.json', data=send_data) assert r.page.doc['commun']['statut'] == 'ok' raise AddRecipientStep( recipient, Value( 'code', label= u'Cette opération doit être validée par un Code Sécurité.') ) elif r.page.doc['donnees']['sign_proc'] == 'OOB': oob_data = {} oob_data['b64_jeton_transaction'] = form['context'] r = self.browser.open( 'https://particuliers.secure.societegenerale.fr/sec/oob_sendoob.json', data=oob_data) assert r.page.doc['commun']['statut'] == 'ok' self.browser.id_transaction = r.page.doc['donnees'][ 'id-transaction'] raise AddRecipientStep( recipient, ValueBool( 'pass', label= u'Valider cette opération sur votre applicaton société générale' )) else: raise AddRecipientError('sign process unknown')
def send_notif_to_user(self, recipient): """Add recipient with 'pass sécurité' authentication""" data = {} data['b64_jeton_transaction'] = self.context r = self.open(self.absurl('/sec/oob_sendoob.json'), data=data) self.id_transaction = r.page.get_transaction_id() raise AddRecipientStep( recipient, ValueBool( 'pass', label= 'Valider cette opération sur votre applicaton société générale' ))
class BoursoramaBackend(BaseBackend, ICapBank): NAME = 'boursorama' MAINTAINER = u'Gabriel Kerneis' EMAIL = '*****@*****.**' VERSION = '0.h' LICENSE = 'AGPLv3+' DESCRIPTION = u'Boursorama French bank website' CONFIG = BackendConfig(ValueBackendPassword('login', label='Account ID', masked=False), ValueBackendPassword('password', label='Password'), ValueBool('enable_twofactors', label='Send validation sms', default=False), Value('device', label='Device name', regexp='\w*', default=''), ) BROWSER = Boursorama def create_default_browser(self): return self.create_browser( self.config["device"].get() , self.config["enable_twofactors"].get() , self.config['login'].get() , self.config['password'].get()) def iter_accounts(self): for account in self.browser.get_accounts_list(): yield account def get_account(self, _id): if not _id.isdigit(): raise AccountNotFound() with self.browser: account = self.browser.get_account(_id) if account: return account else: raise AccountNotFound() def iter_history(self, account): with self.browser: for history in self.browser.get_history(account): yield history
class DLFPModule(Module, CapMessages, CapMessagesPost, CapContent): NAME = 'dlfp' MAINTAINER = u'Romain Bignon' EMAIL = '*****@*****.**' VERSION = '1.2' LICENSE = 'AGPLv3+' DESCRIPTION = "Da Linux French Page news website" CONFIG = BackendConfig(Value('username', label='Username', default=''), ValueBackendPassword('password', label='Password', default=''), ValueBool('get_news', label='Get newspapers', default=True), ValueBool('get_diaries', label='Get diaries', default=False), ValueBool('get_polls', label='Get polls', default=False), ValueBool('get_board', label='Get board', default=False), ValueBool('get_wiki', label='Get wiki', default=False), ValueBool('get_tracker', label='Get tracker', default=False)) STORAGE = {'seen': {}} BROWSER = DLFP FEEDS = {'get_news': "https://linuxfr.org/news.atom", 'get_diaries': "https://linuxfr.org/journaux.atom", 'get_polls': "https://linuxfr.org/sondages.atom", 'get_board': "https://linuxfr.org/forums.atom", 'get_wiki': "https://linuxfr.org/wiki.atom", 'get_tracker': "https://linuxfr.org/suivi.atom", } def create_default_browser(self): username = self.config['username'].get() if username: password = self.config['password'].get() else: password = None return self.create_browser(username, password) def deinit(self): # don't need to logout if the browser hasn't been used. if not self._browser: return with self.browser: self.browser.close_session() #### CapMessages ############################################## def iter_threads(self): whats = set() for param, url in self.FEEDS.iteritems(): if self.config[param].get(): whats.add(url) for what in whats: for article in Newsfeed(what, rssid).iter_entries(): if article.datetime and (datetime.now() - article.datetime) > timedelta(days=60): continue thread = Thread(article.id, article.link) thread.title = article.title thread._rsscomment = article.rsscomment if article.datetime: thread.date = article.datetime yield thread def get_thread(self, id, getseen=True): if not isinstance(id, Thread): thread = None else: thread = id id = thread.id if thread.date: self.storage.set('date', id, thread.date) self.storage.save() with self.browser: content = self.browser.get_content(id) if not content: return None if not thread: thread = Thread(content.id) flags = Message.IS_HTML if thread.id not in self.storage.get('seen', default={}): flags |= Message.IS_UNREAD thread.title = content.title if not thread.date: thread.date = content.date thread.root = Message(thread=thread, id='0', # root message url=self.browser.absurl(id2url(content.id)), title=content.title, sender=content.author or u'', receivers=None, date=thread.date, parent=None, content=content.body, signature='URL: %s' % self.browser.absurl(id2url(content.id)), children=[], flags=flags) for com in content.comments: self._insert_comment(com, thread.root, getseen) return thread def _insert_comment(self, com, parent, getseen=True): """" Insert 'com' comment and its children in the parent message. """ flags = Message.IS_HTML if com.id not in self.storage.get('seen', parent.thread.id, 'comments', default=[]): flags |= Message.IS_UNREAD if getseen or flags & Message.IS_UNREAD: com.parse() message = Message(thread=parent.thread, id=com.id, url=com.url, title=com.title, sender=com.author or u'', receivers=None, date=com.date, parent=parent, content=com.body, signature=com.signature + '<br />'.join(['Score: %d' % com.score, 'URL: %s' % com.url]), children=[], flags=flags) else: message = Message(thread=parent.thread, id=com.id, children=[], parent=parent, flags=flags) parent.children.append(message) for sub in com.comments: self._insert_comment(sub, message, getseen) def iter_unread_messages(self): for thread in self.iter_threads(): # Check if we have seen all comments of this thread. with self.browser: oldhash = self.storage.get('hash', thread.id, default="") newhash = self.browser.get_hash(thread._rsscomment) if oldhash != newhash: self.storage.set('hash', thread.id, newhash) self.storage.save() self.fill_thread(thread, 'root', getseen=False) for m in thread.iter_all_messages(): if m.flags & m.IS_UNREAD: yield m def set_message_read(self, message): self.storage.set('seen', message.thread.id, 'comments', self.storage.get('seen', message.thread.id, 'comments', default=[]) + [message.id]) self.storage.save() lastpurge = self.storage.get('lastpurge', default=0) # 86400 = one day if time.time() - lastpurge > 86400: self.storage.set('lastpurge', time.time()) self.storage.save() # we can't directly delete without a "RuntimeError: dictionary changed size during iteration" todelete = [] for id in self.storage.get('seen', default={}): date = self.storage.get('date', id, default=0) # if no date available, create a new one (compatibility with "old" storage) if date == 0: self.storage.set('date', id, datetime.now()) elif datetime.now() - date > timedelta(days=60): todelete.append(id) for id in todelete: self.storage.delete('hash', id) self.storage.delete('date', id) self.storage.delete('seen', id) self.storage.save() def fill_thread(self, thread, fields, getseen=True): return self.get_thread(thread, getseen) #### CapMessagesReply ######################################### def post_message(self, message): if not self.browser.username: raise BrowserForbidden() if not message.parent: raise CantSendMessage('Posting news and diaries on DLFP is not supported yet') assert message.thread with self.browser: return self.browser.post_comment(message.thread.id, message.parent.id, message.title, message.content) #### CapContent ############################################### def get_content(self, _id, revision=None): if isinstance(_id, basestring): content = Content(_id) else: content = _id _id = content.id if revision: raise NotImplementedError('Website does not provide access to older revisions sources.') with self.browser: data = self.browser.get_wiki_content(_id) if data is None: return None content.content = data return content def push_content(self, content, message=None, minor=False): if not self.browser.username: raise BrowserForbidden() with self.browser: return self.browser.set_wiki_content(content.id, content.content, message) def get_content_preview(self, content): with self.browser: return self.browser.get_wiki_preview(content.id, content.content) OBJECTS = {Thread: fill_thread}
class SenscritiqueModule(Module, CapCalendarEvent): NAME = 'senscritique' DESCRIPTION = u'senscritique website' MAINTAINER = u'Bezleputh' EMAIL = '*****@*****.**' LICENSE = 'AGPLv3+' VERSION = '1.1' ASSOCIATED_CATEGORIES = [CATEGORIES.TELE] BROWSER = SenscritiqueBrowser tv_settings_choices = OrderedDict([(k, u'%s' % (v)) for k, v in sorted({ '000000': u'-- Indifférent --', '9': u'TNT', '1': u'Canalsat', '2': u'Numericable', '10': u'Orange', '11': u'Free', '12': u'SFR', '15': u'Darty box via ADSL', '16': u'Bouygues', }.iteritems())]) """ dict that represents ids list of general-interest channels included in a tv package {'tv package id': ['general-interest channels ids list']} """ general = { 9: [46, 2, 48, 56], 1: [ 49, 46, 21, 2, 36, 59, 54, 48, 56, 50, 32, 1, 51, 24, 38, 34, 37, 6, 25, 11, 53, 26, 47 ], 2: [ 49, 46, 21, 2, 36, 59, 54, 48, 56, 50, 32, 1, 51, 24, 38, 34, 37, 6, 25, 11, 53, 26, 47 ], 10: [46, 46, 2, 36, 59, 54, 32, 24, 34, 37, 53, 47], 11: [46, 46, 2, 36, 59, 54, 32, 24, 34, 37, 53, 47], 12: [49, 46, 2, 36, 59, 54, 32, 24, 34, 37, 53, 47], 15: [49, 46, 2, 36, 32, 24, 34, 37, 53, 47], 16: [49, 46, 2, 36, 59, 54, 32, 24, 34, 37, 53, 47], } """ dict that represents ids list of cinema channels included in a tv package {'tv package id': ['cinema channels ids list']} """ cinema = { 9: [10, 7], 1: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 4055, 44, 3, 45, 42, 41, 43, 13, 12 ], 2: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 4055, 44, 3, 45, 42, 41, 43, 13, 12 ], 10: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 44, 3, 45, 42, 41, 43, 13, 12 ], 11: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 4055, 44, 3, 45, 42, 41, 43, 13, 12 ], 12: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 44, 3, 45, 42, 41, 43, 13, 12 ], 15: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 44, 3, 45, 42, 41, 43, 13, 12 ], 16: [ 10, 7, 9, 8, 52, 19, 18, 17, 16, 20, 15, 14, 4055, 44, 3, 45, 42, 41, 43, 13, 12 ], } CONFIG = BackendConfig( Value('tv_settings', label=u'T.V. package', choices=tv_settings_choices), ValueBool('general', label='General', default=True), ValueBool('cinema', label='Cinema', default=False), ) def get_package_and_channels(self): package = int(self.config['tv_settings'].get()) channels = [] if package: if self.config['general'].get(): channels += self.general[package] if self.config['cinema'].get(): channels += self.cinema[package] return package, channels def search_events(self, query): if self.has_matching_categories(query): package, channels = self.get_package_and_channels() return self.browser.list_events(query.start_date, query.end_date, package, channels) def list_events(self, date_from, date_to=None): items = [] package, channels = self.get_package_and_channels() for item in self.browser.list_events(date_from, date_to, package, channels): items.append(item) items.sort(key=lambda o: o.start_date) return items def get_event(self, _id, event=None): package, channels = self.get_package_and_channels() return self.browser.get_event(_id, event, package=package, channels=channels) def fill_obj(self, event, fields): return self.get_event(event.id, event) OBJECTS = {SensCritiquenCalendarEvent: fill_obj}
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount): NAME = 'aum' MAINTAINER = u'Romain Bignon' EMAIL = '*****@*****.**' VERSION = '0.h' LICENSE = 'AGPLv3+' DESCRIPTION = u'"Adopte un Mec" French dating website' CONFIG = BackendConfig( Value('username', label='Username'), ValueBackendPassword('password', label='Password'), ValueBool('antispam', label='Enable anti-spam', default=False), ValueBool('baskets', label='Get baskets with new messages', default=True), Value('search_query', label='Search query', default='')) STORAGE = { 'profiles_walker': { 'viewed': [] }, 'queries_queue': { 'queue': [] }, 's***s': {}, 'notes': {}, } BROWSER = AuMBrowser MAGIC_ID_BASKET = 1 def __init__(self, *args, **kwargs): BaseBackend.__init__(self, *args, **kwargs) if self.config['antispam'].get(): self.antispam = AntiSpam() else: self.antispam = None def create_default_browser(self): return self.create_browser(self.config['username'].get(), self.config['password'].get(), self.config['search_query'].get()) def report_spam(self, id): with self.browser: pass #self.browser.delete_thread(id) # Do not report fakes to website, to let them to other guys :) #self.browser.report_fake(id) # ---- ICapDating methods --------------------- def init_optimizations(self): self.add_optimization( 'PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)) self.add_optimization('VISIBILITY', Visibility(self.weboob.scheduler, self.browser)) self.add_optimization( 'QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser)) def iter_events(self): all_events = {} with self.browser: all_events[u'baskets'] = (self.browser.get_baskets, 'You were put into %s\'s basket') all_events[u'flashs'] = (self.browser.get_flashs, 'You sent a charm to %s') all_events[u'visits'] = (self.browser.get_visits, 'Visited by %s') for type, (events, message) in all_events.iteritems(): for event in events(): e = Event(event['who']['id']) e.date = parse_dt(event['date']) e.type = type if 'who' in event: e.contact = self._get_partial_contact(event['who']) else: e.contact = self._get_partial_contact(event) if not e.contact: continue e.message = message % e.contact.name yield e # ---- ICapMessages methods --------------------- def fill_thread(self, thread, fields): return self.get_thread(thread) def iter_threads(self): with self.browser: threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # with self.browser: # self.browser.delete_thread(thread['member']['id']) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-thread from %s' % thread['pseudo']) self.report_spam(thread['who']['id']) continue t = Thread(int(thread['who']['id'])) t.flags = Thread.IS_DISCUSSION t.title = u'Discussion with %s' % to_unicode( thread['who']['pseudo']) yield t def get_thread(self, id, contacts=None, get_profiles=False): """ Get a thread and its messages. The 'contacts' parameters is only used for internal calls. """ thread = None if isinstance(id, Thread): thread = id id = thread.id if not thread: thread = Thread(int(id)) thread.flags = Thread.IS_DISCUSSION full = False else: full = True with self.browser: mails = self.browser.get_thread_mails(id, 100) my_name = self.browser.get_my_name() child = None msg = None s**t = self._get_slut(id) if contacts is None: contacts = {} if not thread.title: thread.title = u'Discussion with %s' % mails['who']['pseudo'] self.storage.set('s***s', int(thread.id), 'status', mails['status']) self.storage.save() for mail in mails['results']: flags = 0 if self.antispam and not self.antispam.check_mail(mail): self.logger.info('Skipped a spam-mail from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if parse_dt(mail['date']) > s**t['lastmsg']: flags |= Message.IS_UNREAD if get_profiles: if not mail['from'] in contacts: try: with self.browser: contacts[mail['from']] = self.get_contact( mail['from']) except BrowserHTTPNotFound: pass if self.antispam and mail[ 'from'] in contacts and not self.antispam.check_contact( contacts[mail['from']]): self.logger.info( 'Skipped a spam-mail-profile from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if int(mail['from']) == self.browser.my_id: if mails['remote_status'] == 'new' and msg is None: flags |= Message.IS_NOT_RECEIVED else: flags |= Message.IS_RECEIVED signature = u'' #if mail.get('src', None): # signature += u'Sent from my %s\n\n' % mail['src'] if mail['from'] in contacts: signature += contacts[mail['from']].get_text() msg = Message( thread=thread, id=int( time.strftime('%Y%m%d%H%M%S', parse_dt(mail['date']).timetuple())), title=thread.title, sender=to_unicode(my_name if int(mail['from']) == self.browser. my_id else mails['who']['pseudo']), receivers=[ to_unicode(my_name if int(mail['from']) != self.browser. my_id else mails['who']['pseudo']) ], date=parse_dt(mail['date']), content=to_unicode(unescape(mail['message'] or '').strip()), signature=signature, children=[], flags=flags) if child: msg.children.append(child) child.parent = msg child = msg if full and msg: # If we have get all the messages, replace NotLoaded with None as # parent. msg.parent = None if not full and not msg: # Perhaps there are hidden messages msg = NotLoaded thread.root = msg return thread def iter_unread_messages(self): try: contacts = {} with self.browser: threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # with self.browser: # self.browser.delete_thread(int(thread['member']['id'])) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-unread-thread from %s' % thread['who']['pseudo']) self.report_spam(thread['member']['id']) continue s**t = self._get_slut(thread['who']['id']) if parse_dt( thread['date'] ) > s**t['lastmsg'] or thread['status'] != s**t['status']: t = self.get_thread(thread['who']['id'], contacts, get_profiles=True) for m in t.iter_all_messages(): if m.flags & m.IS_UNREAD: yield m if not self.config['baskets'].get(): return # Send mail when someone added me in her basket. # XXX possibly race condition if a s**t adds me in her basket # between the aum.nb_new_baskets() and aum.get_baskets(). with self.browser: s**t = self._get_slut(-self.MAGIC_ID_BASKET) new_baskets = self.browser.nb_new_baskets() if new_baskets > 0: baskets = self.browser.get_baskets() my_name = self.browser.get_my_name() for basket in baskets: if parse_dt(basket['date']) <= s**t['lastmsg']: continue contact = self.get_contact(basket['who']['id']) if self.antispam and not self.antispam.check_contact( contact): self.logger.info('Skipped a spam-basket from %s' % contact.name) self.report_spam(basket['who']['id']) continue thread = Thread(int(basket['who']['id'])) thread.title = 'Basket of %s' % contact.name thread.root = Message( thread=thread, id=self.MAGIC_ID_BASKET, title=thread.title, sender=contact.name, receivers=[my_name], date=parse_dt(basket['date']), content='You are taken in her basket!', signature=contact.get_text(), children=[], flags=Message.IS_UNREAD) yield thread.root except BrowserUnavailable as e: self.logger.debug('No messages, browser is unavailable: %s' % e) pass # don't care about waiting def set_message_read(self, message): if int(message.id) == self.MAGIC_ID_BASKET: # Save the last baskets checks. s**t = self._get_slut(-self.MAGIC_ID_BASKET) if s**t['lastmsg'] < message.date: s**t['lastmsg'] = message.date self.storage.set('s***s', -self.MAGIC_ID_BASKET, s**t) self.storage.save() return s**t = self._get_slut(message.thread.id) if s**t['lastmsg'] < message.date: s**t['lastmsg'] = message.date self.storage.set('s***s', int(message.thread.id), s**t) self.storage.save() def _get_slut(self, id): id = int(id) s***s = self.storage.get('s***s') if not s***s or not id in s***s: s**t = {'lastmsg': datetime.datetime(1970, 1, 1), 'status': None} else: s**t = self.storage.get('s***s', id) s**t['lastmsg'] = s**t.get('lastmsg', datetime.datetime( 1970, 1, 1)).replace(tzinfo=tz.tzutc()) s**t['status'] = s**t.get('status', None) return s**t # ---- ICapMessagesPost methods --------------------- def post_message(self, message): with self.browser: self.browser.post_mail(message.thread.id, message.content) # ---- ICapContact methods --------------------- def fill_contact(self, contact, fields): if 'profile' in fields: contact = self.get_contact(contact) if contact and 'photos' in fields: for name, photo in contact.photos.iteritems(): with self.browser: if photo.url and not photo.data: data = self.browser.openurl(photo.url).read() contact.set_photo(name, data=data) if photo.thumbnail_url and not photo.thumbnail_data: data = self.browser.openurl(photo.thumbnail_url).read() contact.set_photo(name, thumbnail_data=data) def fill_photo(self, photo, fields): with self.browser: if 'data' in fields and photo.url and not photo.data: photo.data = self.browser.readurl(photo.url) if 'thumbnail_data' in fields and photo.thumbnail_url and not photo.thumbnail_data: photo.thumbnail_data = self.browser.readurl( photo.thumbnail_url) return photo def get_contact(self, contact): with self.browser: if isinstance(contact, Contact): _id = contact.id elif isinstance(contact, (int, long, basestring)): _id = contact else: raise TypeError( "The parameter 'contact' isn't a contact nor a int/long/str/unicode: %s" % contact) profile = self.browser.get_full_profile(_id) if not profile: return None _id = profile['id'] if isinstance(contact, Contact): contact.id = _id contact.name = profile['pseudo'] else: contact = Contact(_id, profile['pseudo'], Contact.STATUS_ONLINE) contact.url = self.browser.id2url(_id) contact.parse_profile(profile, self.browser.get_consts()) return contact def _get_partial_contact(self, contact): s = 0 if contact.get('online', False): s = Contact.STATUS_ONLINE else: s = Contact.STATUS_OFFLINE c = Contact(contact['id'], to_unicode(contact['pseudo']), s) c.url = self.browser.id2url(contact['id']) if 'age' in contact: c.status_msg = u'%s old, %s' % (contact['age'], contact['city']) if contact['cover'] is not None: url = contact['cover'] + '/%(type)s' else: url = u'http://s.adopteunmec.com/www/img/thumb0.jpg' c.set_photo(u'image%s' % contact['cover'], url=url % {'type': 'full'}, thumbnail_url=url % {'type': 'small'}) return c def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): with self.browser: threads = self.browser.get_threads_list(count=100) for thread in threads: c = self._get_partial_contact(thread['who']) if c and (c.status & status) and (not ids or c.id in ids): yield c def send_query(self, id): if isinstance(id, Contact): id = id.id queries_queue = None try: queries_queue = self.get_optimization('QUERIES_QUEUE') except OptimizationNotFound: pass if queries_queue and queries_queue.is_running(): if queries_queue.enqueue_query(id): return Query(id, 'A charm has been sent') else: return Query(id, 'Unable to send charm: it has been enqueued') else: with self.browser: if not self.browser.send_charm(id): raise QueryError('No enough charms available') return Query(id, 'A charm has been sent') def get_notes(self, id): if isinstance(id, Contact): id = id.id return self.storage.get('notes', id) def save_notes(self, id, notes): if isinstance(id, Contact): id = id.id self.storage.set('notes', id, notes) self.storage.save() # ---- ICapChat methods --------------------- def iter_chat_messages(self, _id=None): with self.browser: return self.browser.iter_chat_messages(_id) def send_chat_message(self, _id, message): with self.browser: return self.browser.send_chat_message(_id, message) #def start_chat_polling(self): #self._profile_walker = ProfilesWalker(self.weboob.scheduler, self.storage, self.browser) # ---- ICapAccount methods --------------------- ACCOUNT_REGISTER_PROPERTIES = ValuesDict( Value('username', label='Email address', regexp='^[^ ]+@[^ ]+\.[^ ]+$'), Value('password', label='Password', regexp='^[^ ]+$', masked=True), Value('sex', label='Sex', choices={ 'm': 'Male', 'f': 'Female' }), Value('birthday', label='Birthday (dd/mm/yyyy)', regexp='^\d+/\d+/\d+$'), Value('zipcode', label='Zipcode'), Value('country', label='Country', choices={ 'fr': 'France', 'be': 'Belgique', 'ch': 'Suisse', 'ca': 'Canada' }, default='fr'), Value('godfather', label='Godfather', regexp='^\d*$', default=''), ) @classmethod def register_account(klass, account): """ Register an account on website This is a static method, it would be called even if the backend is instancied. @param account an Account object which describe the account to create """ browser = None bday, bmonth, byear = account.properties['birthday'].get().split( '/', 2) while not browser: try: browser = klass.BROWSER(account.properties['username'].get()) browser.register( password=account.properties['password'].get(), sex=(0 if account.properties['sex'].get() == 'm' else 1), birthday_d=int(bday), birthday_m=int(bmonth), birthday_y=int(byear), zipcode=account.properties['zipcode'].get(), country=account.properties['country'].get(), godfather=account.properties['godfather'].get()) except CaptchaError: getLogger('aum').info('Unable to resolve captcha. Retrying...') browser = None REGISTER_REGEXP = re.compile( '.*http://www.adopteunmec.com/register4.php\?([^\' ]*)\'') def confirm_account(self, mail): msg = email.message_from_string(mail) content = u'' for part in msg.walk(): s = part.get_payload(decode=True) content += unicode(s, 'iso-8859-15') url = None for s in content.split(): m = self.REGISTER_REGEXP.match(s) if m: url = '/register4.php?' + m.group(1) break if url: browser = self.create_browser('') browser.openurl(url) return True return False def get_account(self): """ Get the current account. """ raise NotImplementedError() def update_account(self, account): """ Update the current account. """ raise NotImplementedError() def get_account_status(self): with self.browser: return ( StatusField('myname', 'My name', self.browser.get_my_name()), StatusField('score', 'Score', self.browser.score()), StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()), StatusField('newvisits', 'New visits', self.browser.nb_new_visites()), ) OBJECTS = { Thread: fill_thread, Contact: fill_contact, ContactPhoto: fill_photo }
class BNPorcModule( Module, CapBankWealth, CapBankTransferAddRecipient, CapMessages, CapContact, CapProfile, CapDocument ): NAME = 'bnporc' MAINTAINER = u'Romain Bignon' EMAIL = '*****@*****.**' VERSION = '2.1' LICENSE = 'LGPLv3+' DESCRIPTION = 'BNP Paribas' CONFIG = BackendConfig( ValueBackendPassword('login', label=u'Numéro client', masked=False), ValueBackendPassword('password', label=u'Code secret', regexp='^(\d{6})$'), ValueBool('rotating_password', label=u'Automatically renew password every 100 connections', default=False), ValueBool('digital_key', label=u'User with digital key have to add recipient with digital key', default=False), Value( 'website', label='Type de compte', default='pp', choices={ 'pp': 'Particuliers/Professionnels', 'hbank': 'HelloBank', 'ent': 'Entreprises', 'ent2': 'Entreprises et PME (nouveau site)' } ) ) STORAGE = {'seen': []} accepted_document_types = ( DocumentTypes.STATEMENT, DocumentTypes.REPORT, DocumentTypes.BILL, DocumentTypes.OTHER, ) # Store the messages *list* for this duration CACHE_THREADS = timedelta(seconds=3 * 60 * 60) def __init__(self, *args, **kwargs): Module.__init__(self, *args, **kwargs) self._threads = None self._threads_age = datetime.utcnow() def create_default_browser(self): b = {'ent': BNPEnterprise, 'ent2': BNPCompany, 'pp': BNPPartPro, 'hbank': HelloBank} self.BROWSER = b[self.config['website'].get()] return self.create_browser(self.config) def iter_resources(self, objs, split_path): if Account in objs: self._restrict_level(split_path) return self.iter_accounts() if Subscription in objs: self._restrict_level(split_path) return self.iter_subscription() def iter_accounts(self): return self.browser.iter_accounts() def get_account(self, _id): account = self.browser.get_account(_id) if account: return account else: raise AccountNotFound() def iter_history(self, account): return self.browser.iter_history(account) def iter_coming(self, account): return self.browser.iter_coming_operations(account) def iter_investment(self, account): return self.browser.iter_investment(account) def iter_transfer_recipients(self, origin_account): if self.config['website'].get() != 'pp': raise NotImplementedError() if isinstance(origin_account, Account): origin_account = origin_account.id return self.browser.iter_recipients(origin_account) def new_recipient(self, recipient, **params): if self.config['website'].get() != 'pp': raise NotImplementedError() # Recipient label has max 70 chars. recipient.label = ' '.join(w for w in re.sub('[^0-9a-zA-Z-,\.: ]+', '', recipient.label).split())[:70] return self.browser.new_recipient(recipient, **params) def init_transfer(self, transfer, **params): if self.config['website'].get() != 'pp': raise NotImplementedError() if transfer.label is None: raise TransferInvalidLabel() self.logger.info('Going to do a new transfer') if transfer.account_iban: account = find_object(self.iter_accounts(), iban=transfer.account_iban, error=AccountNotFound) else: account = find_object(self.iter_accounts(), id=transfer.account_id, error=AccountNotFound) recipient = strict_find_object(self.iter_transfer_recipients(account.id), iban=transfer.recipient_iban) if not recipient: recipient = strict_find_object( self.iter_transfer_recipients(account.id), id=transfer.recipient_id, error=RecipientNotFound ) assert account.id.isdigit() # quantize to show 2 decimals. amount = Decimal(transfer.amount).quantize(Decimal(10) ** -2) return self.browser.init_transfer(account, recipient, amount, transfer.label, transfer.exec_date) def execute_transfer(self, transfer, **params): return self.browser.execute_transfer(transfer) def transfer_check_recipient_id(self, old, new): # external recipient id can change, check the iban in recipient id iban = re.search(r'([A-Z]{2}[A-Z\d]+)', old) if iban: # external recipients id iban = iban.group(1) return iban in new else: # iternal recipients id return old == new def iter_contacts(self): if not hasattr(self.browser, 'get_advisor'): raise NotImplementedError() for advisor in self.browser.get_advisor(): yield advisor def get_profile(self): if not hasattr(self.browser, 'get_profile'): raise NotImplementedError() return self.browser.get_profile() def iter_threads(self, cache=False): """ If cache is False, always fetch the threads from the website. """ old = self._threads_age < datetime.utcnow() - self.CACHE_THREADS threads = self._threads if not cache or threads is None or old: threads = list(self.browser.iter_threads()) # the website is stupid and does not have the messages in the proper order threads = sorted(threads, key=lambda t: t.date, reverse=True) self._threads = threads seen = self.storage.get('seen', default=[]) for thread in threads: if thread.id not in seen: thread.root.flags |= thread.root.IS_UNREAD else: thread.root.flags &= ~thread.root.IS_UNREAD yield thread def fill_thread(self, thread, fields=None): if fields is None or 'root' in fields: return self.get_thread(thread) def get_thread(self, _id): if self.config['website'].get() != 'ppold': raise NotImplementedError() if isinstance(_id, Thread): thread = _id _id = thread.id else: thread = Thread(_id) thread = self.browser.get_thread(thread) return thread def iter_unread_messages(self): if self.config['website'].get() != 'ppold': raise NotImplementedError() threads = list(self.iter_threads(cache=True)) for thread in threads: if thread.root.flags & thread.root.IS_UNREAD: thread = self.fillobj(thread) or thread yield thread.root def set_message_read(self, message): self.storage.get('seen', default=[]).append(message.thread.id) self.storage.save() def get_subscription(self, _id): return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound) def iter_documents(self, subscription): if not isinstance(subscription, Subscription): subscription = self.get_subscription(subscription) return self.browser.iter_documents(subscription) def iter_subscription(self): return self.browser.iter_subscription() def get_document(self, _id): subscription_id = _id.split('_')[0] subscription = self.get_subscription(subscription_id) return find_object(self.iter_documents(subscription), id=_id, error=DocumentNotFound) def download_document(self, document): if not isinstance(document, Document): document = self.get_document(document) return self.browser.open(document.url).content OBJECTS = {Thread: fill_thread}
class OkCModule(Module, CapMessages, CapContact, CapMessagesPost, CapDating): NAME = 'okc' MAINTAINER = u'Roger Philibert' EMAIL = '*****@*****.**' VERSION = '2.1' LICENSE = 'AGPLv3+' DESCRIPTION = u'OkCupid' CONFIG = BackendConfig( Value('username', label='Username'), ValueBackendPassword('password', label='Password'), ValueBool('facebook', label='Do you login with Facebook?', default=False)) STORAGE = { 'profiles_walker': { 'viewed': [] }, 's***s': {}, } BROWSER = OkCBrowser def create_default_browser(self): if int(self.config['facebook'].get()): facebook = self.create_browser(klass=FacebookBrowser) facebook.login(self.config['username'].get(), self.config['password'].get()) else: facebook = None return self.create_browser(self.config['username'].get(), self.config['password'].get(), facebook) # ---- CapDating methods --------------------- def init_optimizations(self): self.add_optimization( 'PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)) # ---- CapMessages methods --------------------- def fill_thread(self, thread, fields): return self.get_thread(thread) def iter_threads(self): threads = self.browser.get_threads_list() for thread in threads: t = Thread(thread['user']['userid']) t.flags = Thread.IS_DISCUSSION t.title = u'Discussion with %s' % thread['user']['userinfo'][ 'displayname'] t.date = datetime.fromtimestamp(thread['time']) yield t def get_thread(self, thread): if not isinstance(thread, Thread): thread = Thread(thread) thread.flags = Thread.IS_DISCUSSION messages = self.browser.get_thread_messages(thread.id) contact = self.storage.get('s***s', thread.id, default={'lastmsg': datetime(1970, 1, 1)}) thread.title = u'Discussion with %s' % messages['fields']['username'] me = OkcContact(self.browser.get_profile(self.browser.me['userid'])) other = OkcContact(self.browser.get_profile(thread.id)) parent = None for message in messages['messages']: date = datetime.fromtimestamp(message['timestamp']) flags = 0 if contact['lastmsg'] < date: flags = Message.IS_UNREAD if message['from'] == thread.id: sender = other receiver = me else: receiver = other sender = me if message.get('read', False): flags |= Message.IS_RECEIVED # Apply that flag on all previous messages as the 'read' # attribute is only set on the last read message. pmsg = parent while pmsg: if pmsg.flags & Message.IS_NOT_RECEIVED: pmsg.flags |= Message.IS_RECEIVED pmsg.flags &= ~Message.IS_NOT_RECEIVED pmsg = pmsg.parent else: flags |= Message.IS_NOT_RECEIVED msg = Message(thread=thread, id=message['id'], title=thread.title, sender=sender.name, receivers=[receiver.name], date=date, content=to_unicode(HTMLParser().unescape( message['body'])), children=[], parent=parent, signature=sender.get_text(), flags=flags) if parent: parent.children = [msg] else: thread.root = msg parent = msg return thread def iter_unread_messages(self): for thread in self.iter_threads(): contact = self.storage.get( 's***s', thread.id, default={'lastmsg': datetime(1970, 1, 1)}) if thread.date <= contact['lastmsg']: continue thread = self.get_thread(thread) for message in thread.iter_all_messages(): if message.flags & message.IS_UNREAD: yield message def set_message_read(self, message): contact = self.storage.get('s***s', message.thread.id, default={'lastmsg': datetime(1970, 1, 1)}) if contact['lastmsg'] < message.date: contact['lastmsg'] = message.date self.storage.set('s***s', message.thread.id, contact) self.storage.save() # ---- CapMessagesPost methods --------------------- def post_message(self, message): self.browser.post_message(message.thread.id, message.content) # ---- CapContact methods --------------------- def fill_contact(self, contact, fields): if 'profile' in fields: contact = self.get_contact(contact) if contact and 'photos' in fields: for name, photo in contact.photos.items(): if photo.url and not photo.data: data = self.browser.open(photo.url).content contact.set_photo(name, data=data) if photo.thumbnail_url and not photo.thumbnail_data: data = self.browser.open(photo.thumbnail_url).content contact.set_photo(name, thumbnail_data=data) def fill_photo(self, photo, fields): if 'data' in fields and photo.url and not photo.data: photo.data = self.browser.open(photo.url).content if 'thumbnail_data' in fields and photo.thumbnail_url and not photo.thumbnail_data: photo.thumbnail_data = self.browser.open( photo.thumbnail_url).content return photo def get_contact(self, user_id): if isinstance(user_id, Contact): user_id = user_id.id info = self.browser.get_profile(user_id) return OkcContact(info) def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): threads = self.browser.get_threads_list() for thread in threads: c = self.get_contact(thread['user']['username']) if c and (c.status & status) and (not ids or c.id in ids): yield c OBJECTS = { Thread: fill_thread, Contact: fill_contact, ContactPhoto: fill_photo }
class CmbBackend(BaseBackend, ICapBank): NAME = 'cmb' MAINTAINER = u'Johann Broudin' EMAIL = '*****@*****.**' VERSION = '0.e' LICENSE = 'AGPLv3+' AUTH_CERT = os.path.dirname(__file__) AUTH_CERT += '/Verisign_Class_3_Public_Primary_Certification_Authority.pem' CERTHASH = '684d79eb02f59497b5a9c5dcc4c26db1ee637db12f29d703fdf6a80aafef892d' DESCRIPTION = u'Crédit Mutuel de Bretagne French bank website' CONFIG = BackendConfig( ValueBackendPassword('login', label='Account ID', masked=False), ValueBackendPassword('password', label='Password', masked=True), ValueBool('no_check', label='SSL Check ?', default=True)) LABEL_PATTERNS = [ ( # card compile('^CARTE (?P<text>.*)'), Transaction.TYPE_CARD, '%(text)s'), ( # order compile('^PRLV (?P<text>.*)'), Transaction.TYPE_ORDER, '%(text)s'), ( # withdrawal compile('^RET DAB (?P<text>.*)'), Transaction.TYPE_WITHDRAWAL, '%(text)s'), ( # loan payment compile('^ECH (?P<text>.*)'), Transaction.TYPE_LOAN_PAYMENT, '%(text)s'), ( # transfer compile('^VIR (?P<text>.*)'), Transaction.TYPE_TRANSFER, '%(text)s'), ( # payback compile('^ANN (?P<text>.*)'), Transaction.TYPE_PAYBACK, '%(text)s'), ( # bank compile('^F (?P<text>.*)'), Transaction.TYPE_BANK, '%(text)s') ] cookie = None headers = { 'User-Agent': 'Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OSX; en-us) ' + 'AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405' } def sslCallBack(self, cert): pemcert = DER_cert_to_PEM_cert(cert) certhash = sha256(pemcert).hexdigest() return certhash == self.CERTHASH def login(self): params = urlencode({ 'codeEspace': 'NO', 'codeEFS': '01', 'codeSi': '001', 'noPersonne': self.config['login'].get(), 'motDePasse': self.config['password'].get() }) if 'no_check' in self.config and self.config['no_check'].get() == "y": conn = HellHTTPS("www.cmb.fr") else: conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack) conn.connect() headers = {'Content-Type': 'application/x-www-form-urlencoded'} conn.request("POST", "/domiweb/servlet/Identification", params, headers) response = conn.getresponse() conn.close() if response.status == 302: self.cookie = response.getheader('Set-Cookie').split(';')[0] self.cookie += ';' return True else: raise BrowserIncorrectPassword() return False def iter_accounts(self): if not self.cookie: self.login() def do_http(): if 'no_check' in self.config and self.config['no_check'].get( ) == "y": conn = HellHTTPS("www.cmb.fr") else: conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack) conn.connect() headers = self.headers headers['Cookie'] = self.cookie conn.request("GET", '/domiweb/prive/particulier/releve/0-releve.act', {}, headers) response = conn.getresponse() data = response.read() conn.close() return data data = do_http() parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) table = tree.xpath('/html/body/table') if len(table) == 0: title = tree.xpath('/html/head/title')[0].text if title == u"Utilisateur non identifié": self.login() data = do_http() parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) table = tree.xpath('/html/body/table') if len(table) == 0: raise BrokenPageError() else: raise BrokenPageError() for tr in table[1].getiterator('tr'): if tr.get('class') != 'LnTit' and tr.get('class') != 'LnTot': account = Account() td = tr.xpath('td') a = td[1].xpath('a') account.label = unicode(a[0].text).strip() href = a[0].get('href') m = match(r"javascript:releve\((.*),'(.*)','(.*)'\)", href) if not m: continue account.id = unicode(m.group(1) + m.group(2) + m.group(3)) account._cmbvaleur = m.group(1) account._cmbvaleur2 = m.group(2) account._cmbtype = m.group(3) balance = td[2].text balance = balance.replace(',', '.').replace(u"\xa0", '') account.balance = Decimal(balance) span = td[4].xpath('a/span') if len(span): coming = span[0].text.replace(' ', '').replace(',', '.') coming = coming.replace(u"\xa0", '') account.coming = Decimal(coming) else: account.coming = NotAvailable yield account def get_account(self, _id): for account in self.iter_accounts(): if account.id == _id: return account raise AccountNotFound() def iter_history(self, account): if not self.cookie: self.login() page = "/domiweb/prive/particulier/releve/" if account._cmbtype == 'D': page += "10-releve.act" else: page += "2-releve.act" page += "?noPageReleve=1&indiceCompte=" page += account._cmbvaleur page += "&typeCompte=" page += account._cmbvaleur2 page += "&deviseOrigineEcran=EUR" def do_http(): if 'no_check' in self.config and self.config['no_check'].get( ) == "y": conn = HellHTTPS("www.cmb.fr") else: conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack) conn.connect() headers = self.headers headers['Cookie'] = self.cookie conn.request("GET", page, {}, headers) response = conn.getresponse() data = response.read() conn.close return data data = do_http() parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) tables = tree.xpath('/html/body/table') if len(tables) == 0: title = tree.xpath('/html/head/title')[0].text if title == u"Utilisateur non identifié": self.login() data = do_http() parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) tables = tree.xpath('/html/body/table') if len(tables) == 0: raise BrokenPageError() else: raise BrokenPageError() i = 0 for table in tables: if table.get('id') != "tableMouvements": continue for tr in table.getiterator('tr'): if (tr.get('class') != 'LnTit' and tr.get('class') != 'LnTot'): operation = Transaction(i) td = tr.xpath('td') div = td[1].xpath('div') d = div[0].text.split('/') operation.date = date(*reversed([int(x) for x in d])) div = td[2].xpath('div') label = div[0].xpath('a')[0].text.replace('\n', '') operation.raw = unicode(' '.join(label.split())) for pattern, _type, _label in self.LABEL_PATTERNS: mm = pattern.match(operation.raw) if mm: operation.type = _type operation.label = sub('[ ]+', ' ', _label % mm.groupdict()).strip() break amount = td[3].text if amount.count(',') != 1: amount = td[4].text amount = amount.replace(',', '.').replace(u'\xa0', '') operation.amount = Decimal(amount) else: amount = amount.replace(',', '.').replace(u'\xa0', '') operation.amount = -Decimal(amount) i += 1 yield operation
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount): NAME = 'aum' MAINTAINER = u'Romain Bignon' EMAIL = '*****@*****.**' VERSION = '0.e' LICENSE = 'AGPLv3+' DESCRIPTION = u'"Adopte un Mec" French dating website' CONFIG = BackendConfig( Value('username', label='Username'), ValueBackendPassword('password', label='Password'), ValueBool('antispam', label='Enable anti-spam', default=False), ValueBool('baskets', label='Get baskets with new messages', default=True), Value('search_query', label='Search query', default='')) STORAGE = { 'profiles_walker': { 'viewed': [] }, 'queries_queue': { 'queue': [] }, 's***s': {}, 'notes': {}, } BROWSER = AuMBrowser MAGIC_ID_BASKET = 1 def __init__(self, *args, **kwargs): BaseBackend.__init__(self, *args, **kwargs) if self.config['antispam'].get(): self.antispam = AntiSpam() else: self.antispam = None def create_default_browser(self): return self.create_browser(self.config['username'].get(), self.config['password'].get(), self.config['search_query'].get()) def report_spam(self, id): with self.browser: pass #self.browser.delete_thread(id) # Do not report fakes to website, to let them to other guys :) #self.browser.report_fake(id) # ---- ICapDating methods --------------------- def init_optimizations(self): self.add_optimization( 'PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)) self.add_optimization('VISIBILITY', Visibility(self.weboob.scheduler, self.browser)) self.add_optimization( 'QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser)) def iter_events(self): all_events = {} with self.browser: all_events[u'baskets'] = (self.browser.get_baskets, 'You were put into %s\'s basket') all_events[u'flashs'] = (self.browser.get_flashs, 'You sent a charm to %s') all_events[u'visits'] = (self.browser.get_visits, 'Visited by %s') for type, (events, message) in all_events.iteritems(): for event in events(): e = Event(event['who']['id']) e.date = parse_dt(event['date']) e.type = type if 'who' in event: e.contact = self._get_partial_contact(event['who']) else: e.contact = self._get_partial_contact(event) if not e.contact: continue e.message = message % e.contact.name yield e # ---- ICapMessages methods --------------------- def fill_thread(self, thread, fields): return self.get_thread(thread) def iter_threads(self): with self.browser: threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # with self.browser: # self.browser.delete_thread(thread['member']['id']) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-thread from %s' % thread['pseudo']) self.report_spam(thread['who']['id']) continue t = Thread(int(thread['who']['id'])) t.flags = Thread.IS_DISCUSSION t.title = u'Discussion with %s' % to_unicode( thread['who']['pseudo']) yield t def get_thread(self, id, contacts=None, get_profiles=False): """ Get a thread and its messages. The 'contacts' parameters is only used for internal calls. """ thread = None if isinstance(id, Thread): thread = id id = thread.id if not thread: thread = Thread(int(id)) thread.flags = Thread.IS_DISCUSSION full = False else: full = True with self.browser: mails = self.browser.get_thread_mails(id, 100) my_name = self.browser.get_my_name() child = None msg = None s**t = self._get_slut(id) if contacts is None: contacts = {} if not thread.title: thread.title = u'Discussion with %s' % mails['who']['pseudo'] self.storage.set('s***s', int(thread.id), 'status', mails['status']) self.storage.save() for mail in mails['results']: flags = 0 if self.antispam and not self.antispam.check_mail(mail): self.logger.info('Skipped a spam-mail from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if parse_dt(mail['date']) > s**t['lastmsg']: flags |= Message.IS_UNREAD if get_profiles: if not mail['from'] in contacts: with self.browser: contacts[mail['from']] = self.get_contact( mail['from']) if self.antispam and not self.antispam.check_contact( contacts[mail['from']]): self.logger.info( 'Skipped a spam-mail-profile from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if int(mail['from']) == self.browser.my_id: if mails['remote_status'] == 'new' and msg is None: flags |= Message.IS_NOT_RECEIVED else: flags |= Message.IS_RECEIVED signature = u'' #if mail.get('src', None): # signature += u'Sent from my %s\n\n' % mail['src'] if mail['from'] in contacts: signature += contacts[mail['from']].get_text() msg = Message( thread=thread, id=int( time.strftime('%Y%m%d%H%M%S', parse_dt(mail['date']).timetuple())), title=thread.title, sender=to_unicode(my_name if int(mail['from']) == self.browser. my_id else mails['who']['pseudo']), receivers=[ to_unicode(my_name if int(mail['from']) != self.browser. my_id else mails['who']['pseudo']) ], date=parse_dt(mail['date']), content=to_unicode(unescape(mail['message'] or '').strip()), signature=signature, children=[], flags=flags) if child: msg.children.append(child) child.parent = msg child = msg if full and msg: # If we have get all the messages, replace NotLoaded with None as # parent. msg.parent = None if not full and not msg: # Perhaps there are hidden messages msg = NotLoaded thread.root = msg return thread def iter_unread_messages(self): try: contacts = {} with self.browser: threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # with self.browser: # self.browser.delete_thread(int(thread['member']['id'])) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-unread-thread from %s' % thread['who']['pseudo']) self.report_spam(thread['member']['id']) continue s**t = self._get_slut(thread['who']['id']) if parse_dt( thread['date'] ) > s**t['lastmsg'] or thread['status'] != s**t['status']: t = self.get_thread(thread['who']['id'], contacts, get_profiles=True) for m in t.iter_all_messages(): if m.flags & m.IS_UNREAD: yield m if not self.config['baskets'].get(): return # Send mail when someone added me in her basket. # XXX possibly race condition if a s**t adds me in her basket # between the aum.nb_new_baskets() and aum.get_baskets(). with self.browser: s**t = self._get_slut(-self.MAGIC_ID_BASKET) new_baskets = self.browser.nb_new_baskets() if new_baskets > 0: baskets = self.browser.get_baskets() my_name = self.browser.get_my_name() for basket in baskets: if parse_dt(basket['date']) <= s**t['lastmsg']: continue contact = self.get_contact(basket['id']) if self.antispam and not self.antispam.check_contact( contact): self.logger.info('Skipped a spam-basket from %s' % contact.name) self.report_spam(basket['id']) continue thread = Thread(int(basket['id'])) thread.title = 'Basket of %s' % contact.name thread.root = Message( thread=thread, id=self.MAGIC_ID_BASKET, title=thread.title, sender=contact.name, receivers=[my_name], date=parse_dt(basket['date']), content='You are taken in her basket!', signature=contact.get_text(), children=[], flags=Message.IS_UNREAD) yield thread.root except BrowserUnavailable, e: self.logger.debug('No messages, browser is unavailable: %s' % e) pass # don't care about waiting
class AuMModule(Module, CapMessages, CapMessagesPost, CapDating, CapChat, CapContact, CapAccount): NAME = 'aum' MAINTAINER = u'Romain Bignon' EMAIL = '*****@*****.**' VERSION = '2.1' LICENSE = 'AGPLv3+' DESCRIPTION = u'"Adopte un Mec" French dating website' CONFIG = BackendConfig( Value('username', label='Username'), ValueBackendPassword('password', label='Password'), ValueBool('antispam', label='Enable anti-spam', default=False), ValueBool('baskets', label='Get baskets with new messages', default=True), Value('search_query', label='Search query', default='')) STORAGE = { 'profiles_walker': { 'viewed': [] }, 'queries_queue': { 'queue': [] }, 'contacts': {}, 'notes': {}, } BROWSER = AuMBrowser MAGIC_ID_BASKET = 1 def __init__(self, *args, **kwargs): Module.__init__(self, *args, **kwargs) if self.config['antispam'].get(): self.antispam = AntiSpam() else: self.antispam = None def create_default_browser(self): return self.create_browser(self.config['username'].get(), self.config['password'].get(), self.config['search_query'].get()) def report_spam(self, id): pass #self.browser.delete_thread(id) # Do not report fakes to website, to let them to other guys :) #self.browser.report_fake(id) # ---- CapDating methods --------------------- def init_optimizations(self): self.add_optimization( 'PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)) self.add_optimization('VISIBILITY', Visibility(self.weboob.scheduler, self.browser)) self.add_optimization( 'QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser)) def iter_events(self): all_events = {} all_events[u'baskets'] = (self.browser.get_baskets, 'You were put into %s\'s basket') all_events[u'flashs'] = (self.browser.get_flashs, 'You sent a charm to %s') all_events[u'visits'] = (self.browser.get_visits, 'Visited by %s') for type, (events, message) in all_events.items(): for event in events(): e = Event(event['who']['id']) e.date = parse_dt(event['date']) e.type = type if 'who' in event: e.contact = self._get_partial_contact(event['who']) else: e.contact = self._get_partial_contact(event) if not e.contact: continue e.message = message % e.contact.name yield e def iter_new_contacts(self): for _id in self.browser.search_profiles( ): #.difference(self.OPTIM_PROFILE_WALKER.visited_profiles): contact = Contact(_id, '', 0) yield contact # ---- CapMessages methods --------------------- def fill_thread(self, thread, fields): return self.get_thread(thread) def iter_threads(self): threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # self.browser.delete_thread(thread['member']['id']) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-thread from %s' % thread['pseudo']) self.report_spam(thread['who']['id']) continue t = Thread(int(thread['who']['id'])) t.flags = Thread.IS_DISCUSSION t.title = u'Discussion with %s' % to_unicode( thread['who']['pseudo']) yield t def get_thread(self, id, contacts=None, get_profiles=False): """ Get a thread and its messages. The 'contacts' parameters is only used for internal calls. """ thread = None if isinstance(id, Thread): thread = id id = thread.id if not thread: thread = Thread(int(id)) thread.flags = Thread.IS_DISCUSSION full = False else: full = True mails = self.browser.get_thread_mails(id, 100) my_name = self.browser.get_my_name() child = None msg = None contact = self._get_contact(id) if contacts is None: contacts = {} if not thread.title: thread.title = u'Discussion with %s' % mails['who']['pseudo'] self.storage.set('contacts', int(thread.id), 'status', mails['status']) self.storage.save() for mail in mails['results']: flags = 0 if self.antispam and not self.antispam.check_mail(mail): self.logger.info('Skipped a spam-mail from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if parse_dt(mail['date']) > contact['lastmsg']: flags |= Message.IS_UNREAD if get_profiles: if mail['from'] not in contacts: try: contacts[mail['from']] = self.get_contact( mail['from']) except BrowserHTTPNotFound: pass if self.antispam and mail[ 'from'] in contacts and not self.antispam.check_contact( contacts[mail['from']]): self.logger.info( 'Skipped a spam-mail-profile from %s' % mails['who']['pseudo']) self.report_spam(thread.id) break if int(mail['from']) == self.browser.my_id: if mails['remote_status'] == 'new' and msg is None: flags |= Message.IS_NOT_RECEIVED else: flags |= Message.IS_RECEIVED signature = u'' #if mail.get('src', None): # signature += u'Sent from my %s\n\n' % mail['src'] if mail['from'] in contacts: signature += contacts[mail['from']].get_text() msg = Message( thread=thread, id=int( time.strftime('%Y%m%d%H%M%S', parse_dt(mail['date']).timetuple())), title=thread.title, sender=to_unicode(my_name if int(mail['from']) == self.browser. my_id else mails['who']['pseudo']), receivers=[ to_unicode(my_name if int(mail['from']) != self.browser. my_id else mails['who']['pseudo']) ], date=parse_dt(mail['date']), content=to_unicode(HTMLParser().unescape(mail['message'] or '').strip()), signature=signature, children=[], flags=flags) if child: msg.children.append(child) child.parent = msg child = msg if full and msg: # If we have get all the messages, replace NotLoaded with None as # parent. msg.parent = None if not full and not msg: # Perhaps there are hidden messages msg = NotLoaded thread.root = msg return thread def iter_unread_messages(self): try: contacts = {} threads = self.browser.get_threads_list() for thread in threads: #if thread['member'].get('isBan', thread['member'].get('dead', False)): # self.browser.delete_thread(int(thread['member']['id'])) # continue if self.antispam and not self.antispam.check_thread(thread): self.logger.info('Skipped a spam-unread-thread from %s' % thread['who']['pseudo']) self.report_spam(thread['member']['id']) continue contact = self._get_contact(thread['who']['id']) if parse_dt(thread['date']) > contact['lastmsg'] or thread[ 'status'] != contact['status']: try: t = self.get_thread(thread['who']['id'], contacts, get_profiles=True) except BrowserUnavailable: continue for m in t.iter_all_messages(): if m.flags & m.IS_UNREAD: yield m if not self.config['baskets'].get(): return # Send mail when someone added me in her basket. # XXX possibly race condition if a contact adds me in her basket # between the aum.nb_new_baskets() and aum.get_baskets(). contact = self._get_contact(-self.MAGIC_ID_BASKET) new_baskets = self.browser.nb_new_baskets() if new_baskets > 0: baskets = self.browser.get_baskets() my_name = self.browser.get_my_name() for basket in baskets: if parse_dt(basket['date']) <= contact['lastmsg']: continue contact = self.get_contact(basket['who']['id']) if self.antispam and not self.antispam.check_contact( contact): self.logger.info('Skipped a spam-basket from %s' % contact.name) self.report_spam(basket['who']['id']) continue thread = Thread(int(basket['who']['id'])) thread.title = 'Basket of %s' % contact.name thread.root = Message( thread=thread, id=self.MAGIC_ID_BASKET, title=thread.title, sender=contact.name, receivers=[my_name], date=parse_dt(basket['date']), content='You are taken in her basket!', signature=contact.get_text(), children=[], flags=Message.IS_UNREAD) yield thread.root except BrowserUnavailable as e: self.logger.debug('No messages, browser is unavailable: %s' % e) pass # don't care about waiting def set_message_read(self, message): if int(message.id) == self.MAGIC_ID_BASKET: # Save the last baskets checks. contact = self._get_contact(-self.MAGIC_ID_BASKET) if contact['lastmsg'] < message.date: contact['lastmsg'] = message.date self.storage.set('contacts', -self.MAGIC_ID_BASKET, contact) self.storage.save() return contact = self._get_contact(message.thread.id) if contact['lastmsg'] < message.date: contact['lastmsg'] = message.date self.storage.set('contacts', int(message.thread.id), contact) self.storage.save() def _get_contact(self, id): id = int(id) contacts = self.storage.get('contacts') if not contacts or id not in contacts: contacts = self.storage.get(b64decode('c2x1dHM=')) if not contacts or id not in contacts: contact = { 'lastmsg': datetime.datetime(1970, 1, 1), 'status': None } else: contact = contacts[id] contact['lastmsg'] = contact.get( 'lastmsg', datetime.datetime(1970, 1, 1)).replace(tzinfo=tz.tzutc()) contact['status'] = contact.get('status', None) return contact # ---- CapMessagesPost methods --------------------- def post_message(self, message): self.browser.post_mail(message.thread.id, message.content) # ---- CapContact methods --------------------- def fill_contact(self, contact, fields): if 'profile' in fields: contact = self.get_contact(contact) if contact and 'photos' in fields: for name, photo in contact.photos.items(): if photo.url and not photo.data: data = self.browser.openurl(photo.url).read() contact.set_photo(name, data=data) if photo.thumbnail_url and not photo.thumbnail_data: data = self.browser.openurl(photo.thumbnail_url).read() contact.set_photo(name, thumbnail_data=data) def fill_photo(self, photo, fields): if 'data' in fields and photo.url and not photo.data: photo.data = self.browser.open(photo.url).content if 'thumbnail_data' in fields and photo.thumbnail_url and not photo.thumbnail_data: photo.thumbnail_data = self.browser.open( photo.thumbnail_url).content return photo def get_contact(self, contact): if isinstance(contact, Contact): _id = contact.id elif isinstance(contact, (int, long, basestring)): _id = contact else: raise TypeError( "The parameter 'contact' isn't a contact nor a int/long/str/unicode: %s" % contact) profile = self.browser.get_full_profile(_id) if not profile: return None _id = profile['id'] if isinstance(contact, Contact): contact.id = _id contact.name = profile['pseudo'] else: contact = Contact(_id, profile['pseudo'], Contact.STATUS_ONLINE) contact.url = self.browser.id2url(_id) contact.parse_profile(profile, self.browser.get_consts()) return contact def _get_partial_contact(self, contact): s = 0 if contact.get('online', False): s = Contact.STATUS_ONLINE else: s = Contact.STATUS_OFFLINE c = Contact(contact['id'], to_unicode(contact['pseudo']), s) c.url = self.browser.id2url(contact['id']) if 'age' in contact: c.status_msg = u'%s old, %s' % (contact['age'], contact['city']) if contact['cover'] is not None: url = contact['cover'] + '/%(type)s' else: url = u'http://s.adopteunmec.com/www/img/thumb0.jpg' c.set_photo(u'image%s' % contact['cover'], url=url % {'type': 'full'}, thumbnail_url=url % {'type': 'small'}) return c def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): threads = self.browser.get_threads_list(count=100) for thread in threads: c = self._get_partial_contact(thread['who']) if c and (c.status & status) and (not ids or c.id in ids): yield c def send_query(self, id): if isinstance(id, Contact): id = id.id queries_queue = None try: queries_queue = self.get_optimization('QUERIES_QUEUE') except OptimizationNotFound: pass if queries_queue and queries_queue.is_running(): if queries_queue.enqueue_query(id): return Query(id, 'A charm has been sent') else: return Query(id, 'Unable to send charm: it has been enqueued') else: if not self.browser.send_charm(id): raise QueryError('No enough charms available') return Query(id, 'A charm has been sent') def get_notes(self, id): if isinstance(id, Contact): id = id.id return self.storage.get('notes', id) def save_notes(self, id, notes): if isinstance(id, Contact): id = id.id self.storage.set('notes', id, notes) self.storage.save() # ---- CapChat methods --------------------- def iter_chat_messages(self, _id=None): return self.browser.iter_chat_messages(_id) def send_chat_message(self, _id, message): return self.browser.send_chat_message(_id, message) #def start_chat_polling(self): #self._profile_walker = ProfilesWalker(self.weboob.scheduler, self.storage, self.browser) def get_account_status(self): return ( StatusField(u'myname', u'My name', unicode(self.browser.get_my_name())), StatusField(u'score', u'Score', unicode(self.browser.score())), StatusField(u'avcharms', u'Available charms', unicode(self.browser.nb_available_charms())), StatusField(u'newvisits', u'New visits', unicode(self.browser.nb_new_visites())), ) OBJECTS = { Thread: fill_thread, Contact: fill_contact, ContactPhoto: fill_photo }
class BoursoramaModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapProfile, CapContact, CapCurrencyRate): NAME = 'boursorama' MAINTAINER = u'Gabriel Kerneis' EMAIL = '*****@*****.**' VERSION = '1.6' LICENSE = 'LGPLv3+' DESCRIPTION = u'Boursorama' CONFIG = BackendConfig( ValueBackendPassword('login', label='Identifiant', masked=False), ValueBackendPassword('password', label='Mot de passe'), ValueBool('enable_twofactors', label='Send validation sms', default=False), Value('device', label='Device name', regexp='\w*', default='weboob'), Value('pin_code', label='Sms code', required=False, default=''), ) BROWSER = BoursoramaBrowser def create_default_browser(self): return self.create_browser(self.config) def iter_accounts(self): return self.browser.get_accounts_list() def get_account(self, _id): account = self.browser.get_account(_id) if account: return account else: raise AccountNotFound() def iter_history(self, account): for tr in self.browser.get_history(account): if not tr._is_coming: yield tr def iter_coming(self, account): for tr in self.browser.get_history(account, coming=True): if tr._is_coming: yield tr def iter_investment(self, account): return self.browser.get_investment(account) def get_profile(self): return self.browser.get_profile() def iter_contacts(self): return self.browser.get_advisor() def iter_transfer_recipients(self, account): if not isinstance(account, Account): account = self.get_account(account) return self.browser.iter_transfer_recipients(account) def init_transfer(self, transfer, **kwargs): return self.browser.init_transfer(transfer, **kwargs) def new_recipient(self, recipient, **kwargs): return self.browser.new_recipient(recipient, **kwargs) def execute_transfer(self, transfer, **kwargs): return self.browser.execute_transfer(transfer, **kwargs) def transfer_check_label(self, old, new): # In the confirm page the '<' is interpeted like a html tag # If no '>' is present the following chars are deleted # Else: inside '<>' chars are deleted old = re.sub(r'<[^>]*>', '', old).strip() old = old.split('<')[0] # replace � by ?, like the bank does old = old.replace('\ufffd', '?') return super(BoursoramaModule, self).transfer_check_label(old, new) def iter_currencies(self): return self.browser.iter_currencies() def get_rate(self, currency_from, currency_to): return self.browser.get_rate(currency_from, currency_to)