diff --git a/modules/ovs/backend.py b/modules/ovs/backend.py index 16f233f9..9df95687 100644 --- a/modules/ovs/backend.py +++ b/modules/ovs/backend.py @@ -21,6 +21,7 @@ from weboob.tools.backend import BaseBackend, BackendConfig from weboob.tools.browser import BrowserForbidden from weboob.tools.value import Value, ValueBackendPassword from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message +from weboob.capabilities.contact import ICapContact from .browser import OvsBrowser @@ -31,7 +32,7 @@ __all__ = ['OvsBackend'] CITIES = {u'agen': u'Agen', u'ajaccio': u'Ajaccio', u'albi': u'Albi', u'amiens': u'Amiens', u'angers': u'Angers', u'angouleme': u'Angoul\xeame', u'annecy': u'Annecy', u'aurillac': u'Aurillac', u'auxerre': u'Auxerre', u'avignon': u'Avignon', u'bastia': u'Bastia', u'beauvais': u'Beauvais', u'belfort': u'Belfort', u'bergerac': u'Bergerac', u'besancon': u'Besan\xe7on', u'beziers': u'B\xe9ziers', u'biarritz': u'Biarritz', u'blois': u'Blois', u'bordeaux': u'Bordeaux', u'bourg-en-bresse': u'M\xe2con', u'bourges': u'Bourges', u'brest': u'Brest', u'brive-la-gaillarde': u'Brive', u'bruxelles': u'Bruxelles', u'caen': u'Caen', u'calais': u'Boulogne', u'carcassonne': u'Carcassonne', u'chalon-sur-saone': u'Chalon', u'chambery': u'Albertville', u'chantilly': u'Chantilly', u'charleroi': u'Charleroi', u'charleville-mezieres': u'Charleville', u'chartres': u'Chartres', u'chateauroux': u'Ch\xe2teauroux', u'cherbourg': u'Cherbourg', u'cholet': u'Cholet', u'clermont-ferrand': u'Clt-Ferrand', u'compiegne': u'Compi\xe8gne', u'dieppe': u'Dieppe', u'dijon': u'Dijon', u'dunkerque': u'Dunkerque', u'evreux': u'Evreux', u'frejus': u'Fr\xe9jus', u'gap': u'Gap', u'geneve': u'Gen\xe8ve', u'grenoble': u'Grenoble', u'la-roche-sur-yon': u'La Roche/Yon', u'la-rochelle': u'La Rochelle', u'lausanne': u'Lausanne', u'laval': u'Laval', u'le-havre': u'Le Havre', u'le-mans': u'Alen\xe7on', u'liege': u'Li\xe8ge', u'lille': u'Lille', u'limoges': u'Limoges', u'lorient': u'Lorient', u'luxembourg': u'Luxembourg', u'lyon': u'Lyon', u'marseille': u'Aix', u'metz': u'Metz', u'mons': u'Mons', u'mont-de-marsan': u'Mont de Marsan', u'montauban': u'Montauban', u'montlucon': u'Montlu\xe7on', u'montpellier': u'Montpellier', u'mulhouse': u'Colmar', u'namur': u'Namur', u'nancy': u'Nancy', u'nantes': u'Nantes', u'nevers': u'Nevers', u'nice': u'Cannes', u'nimes': u'N\xeemes', u'niort': u'Niort', u'orleans': u'Orl\xe9ans', u'paris': u'PARIS', u'pau': u'Pau', u'perigueux': u'P\xe9rigueux', u'perpignan': u'Perpignan', u'poitiers': u'Poitiers', u'quimper': u'Quimper', u'reims': u'Reims', u'rennes': u'Rennes', u'roanne': u'Roanne', u'rodez': u'Rodez', u'rouen': u'Rouen', u'saint-brieuc': u'St-Brieuc', u'saint-etienne': u'St-Etienne', u'saint-malo': u'St-Malo', u'saint-nazaire': u'St-Nazaire', u'saint-quentin': u'St-Quentin', u'saintes': u'Saintes', u'strasbourg': u'Strasbourg', u'tarbes': u'Tarbes', u'toulon': u'Toulon', u'toulouse': u'Toulouse', u'tours': u'Tours', u'troyes': u'Troyes', u'valence': u'Mont\xe9limar', u'vannes': u'Vannes', u'zurich': u'Zurich'} -class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost): +class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapContact): NAME = 'ovs' DESCRIPTION = u'OnVaSortir website. Handles private messages only' MAINTAINER = u'Vincent A' @@ -52,6 +53,7 @@ class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost): self.config['password'].get(), parser='raw') + # ICapMessages def iter_threads(self): with self.browser: for thread in self.browser.iter_threads_list(): @@ -85,6 +87,7 @@ class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost): self.storage.set('seen', message.full_id, True) self.storage.save() + # ICapMessagesPost def post_message(self, message): if not self.browser.username: raise BrowserForbidden() @@ -99,6 +102,10 @@ class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost): # ovs.@* self.browser.create_thread(thread.id, message.title, message.content) + # ICapContact + def get_contact(self, id): + return self.browser.get_contact(id) + # FIXME known bug: parsing is done in "boosted mode" which is automatically disable after some time, the "boosted mode" should be re-toggled often # TODO support outing comments, forum messages diff --git a/modules/ovs/browser.py b/modules/ovs/browser.py index 77ab10a4..041e4208 100644 --- a/modules/ovs/browser.py +++ b/modules/ovs/browser.py @@ -63,6 +63,7 @@ class OvsBrowser(BaseBrowser): kw['parser'] = SoupParser() BaseBrowser.__init__(self, username, password, *a, **kw) + self.city = city def iter_threads_list(self): self.location('/vue_messages_recus.php') @@ -103,6 +104,11 @@ class OvsBrowser(BaseBrowser): assert self.is_on_page(PageUserProfile) self.page.create_thread(recipient, subject, body) + def get_contact(self, id): + self.location('/profil_read.php?%s' % id.encode(self.ENCODING)) # FIXME + assert self.is_on_page(PageUserProfile) + return self.page.get_contact() + def get_french_cities(self): self.location('http://www.onvasortir.com') assert self.is_on_page(PageCityList) diff --git a/modules/ovs/ovsparse.py b/modules/ovs/ovsparse.py index a16f44cd..ad65c73a 100644 --- a/modules/ovs/ovsparse.py +++ b/modules/ovs/ovsparse.py @@ -19,7 +19,6 @@ import BeautifulSoup -import datetime def nearest_parent(node, expected): @@ -39,15 +38,6 @@ def all_next_siblings(node): node = node.nextSibling return ret -def parse_date_from_site(sitedate): - parts = sitedate.split() # [d, m, Y, '-', 'H:M:S'] - if len(parts[0]) == 1: - parts[0] = '0%s' % parts[0] - months = {'january': '01', 'february': '02', 'march': '03', 'april': '04', 'may': '05', 'june': '06', 'july': '07', 'august': '08', 'september': '09', 'october': '10', 'november': '11', 'december': '12'} - parts[1] = months[parts[1].lower()] - del parts[3] - return datetime.datetime.strptime(' '.join(parts), '%d %m %Y %H:%M:%S') - def image_to_text(src): smileys = {'chat/e/grin.gif': ':D', 'chat/e/unsure.gif': ':s', diff --git a/modules/ovs/pages.py b/modules/ovs/pages.py index 1357bac9..c45c0888 100644 --- a/modules/ovs/pages.py +++ b/modules/ovs/pages.py @@ -24,6 +24,8 @@ import urllib from urlparse import urlsplit from weboob.tools.browser import BasePage from weboob.capabilities.messages import Message, Thread +from weboob.capabilities.contact import Contact, ProfileNode +from weboob.tools.date import parse_french_date import ovsparse @@ -104,7 +106,7 @@ class PagePrivateThread(OvsPage): # date will be used as id sitedate = profile_a.findParent('div').find(text=re.compile(',.*')).replace(', ', '') - sysdate = ovsparse.parse_date_from_site(sitedate) + sysdate = parse_french_date(sitedate) compactdate = datetime.datetime.strftime(sysdate, '%Y%m%dT%H%M%S') # but make it unique @@ -172,6 +174,66 @@ class PageUserProfile(OvsPage): #~ self.browser['Message'] = body.encode(self.browser.ENCODING) #~ self.browser.submit() + def get_contact(self): + profile_a = self.document.find('a', href=re.compile(r'profil_read.php\?.*')) + _id = re.search(r'\?(.*)', profile_a['href']).group(1) + + # not available in the 'boosted' version + contact = Contact(_id, _id, Contact.STATUS_OFFLINE) + contact.url = self.url + contact.profile = {} + + thumbnail_url = 'http://photos.onvasortir.com/%s/photos/%s_resize.png' % (self.browser.city, _id) + if self.document.find('img', attrs={'src': thumbnail_url}): + photo_url = thumbnail_url.replace('_resize', '') + contact.set_photo('main', thumbnail_url=thumbnail_url, url=photo_url, hidden=False) + + location_a = self.document.find('a', href=re.compile(r'vue_profil_carte\.php\?')) + if location_a: + lat = float(re.search(r'Lat=([\d.]+)', location_a['href']).group(1)) + self._set_profile(contact, 'latitude', lat) + lng = float(re.search(r'Lng=([\d.]+)', location_a['href']).group(1)) + self._set_profile(contact, 'longitude', lng) + + div = self.document.find('div', attrs={'class': 'PADtitreBlanc_txt'}, text=re.compile('Personal Info')) + td = div.findParent('tr').findNextSibling('tr').td + infos_text = td.getText(separator='\n').strip() + it = iter(infos_text.split('\n')) + infos = dict(zip(it, it)) + if infos['Sex :'] == 'Man': + self._set_profile(contact, 'sex', 'M') + elif infos['Sex :'] == 'Woman': + self._set_profile(contact, 'sex', 'F') + if infos['Birthday :'] != 'Unknown': + self._set_profile(contact, 'birthday', parse_french_date(re.search(r'(\d+ \w+ \d+)', infos['Birthday :']).group(1))) + self._try_attr(contact, infos, 'First Name :', 'first_name') + self._try_attr(contact, infos, 'Status :', 'marriage') + self._try_attr(contact, infos, 'Area :', 'area') + + div = self.document.find('div', attrs={'class': 'PADtitreBlanc_txt'}, text=re.compile('A few words')) + td = div.findParent('tr').findNextSibling('tr').td + summary = td.getText(separator='\n').strip() + if summary == 'Unknown': + contact.summary = u'' + else: + contact.summary = summary + + div = self.document.find('div', style=re.compile('dashed')) + if div: + # TODO handle html, links and smileys + contact.status_msg = div.getText() + else: + contact.status_msg = u'' + + return contact + + def _set_profile(self, contact, key, value): + contact.profile[key] = ProfileNode(key, key.capitalize(), value) + + def _try_attr(self, contact, infos, html_attr, obj_attr): + if infos[html_attr] != 'Unknown': + self._set_profile(contact, obj_attr, infos[html_attr].strip()) + class PageCityList(DummyPage): def get_cities(self, master_domain='onvasortir.com'):