ovs: implement ICapContact.get_contact

This commit is contained in:
Vincent A 2013-11-11 15:32:10 +01:00 committed by Florent
commit e3d440d359
4 changed files with 77 additions and 12 deletions

View file

@ -21,6 +21,7 @@ from weboob.tools.backend import BaseBackend, BackendConfig
from weboob.tools.browser import BrowserForbidden from weboob.tools.browser import BrowserForbidden
from weboob.tools.value import Value, ValueBackendPassword from weboob.tools.value import Value, ValueBackendPassword
from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message
from weboob.capabilities.contact import ICapContact
from .browser import OvsBrowser 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'} 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' NAME = 'ovs'
DESCRIPTION = u'OnVaSortir website. Handles private messages only' DESCRIPTION = u'OnVaSortir website. Handles private messages only'
MAINTAINER = u'Vincent A' MAINTAINER = u'Vincent A'
@ -52,6 +53,7 @@ class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost):
self.config['password'].get(), self.config['password'].get(),
parser='raw') parser='raw')
# ICapMessages
def iter_threads(self): def iter_threads(self):
with self.browser: with self.browser:
for thread in self.browser.iter_threads_list(): 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.set('seen', message.full_id, True)
self.storage.save() self.storage.save()
# ICapMessagesPost
def post_message(self, message): def post_message(self, message):
if not self.browser.username: if not self.browser.username:
raise BrowserForbidden() raise BrowserForbidden()
@ -99,6 +102,10 @@ class OvsBackend(BaseBackend, ICapMessages, ICapMessagesPost):
# ovs.<recipient>@* # ovs.<recipient>@*
self.browser.create_thread(thread.id, message.title, message.content) 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 # 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 # TODO support outing comments, forum messages

View file

@ -63,6 +63,7 @@ class OvsBrowser(BaseBrowser):
kw['parser'] = SoupParser() kw['parser'] = SoupParser()
BaseBrowser.__init__(self, username, password, *a, **kw) BaseBrowser.__init__(self, username, password, *a, **kw)
self.city = city
def iter_threads_list(self): def iter_threads_list(self):
self.location('/vue_messages_recus.php') self.location('/vue_messages_recus.php')
@ -103,6 +104,11 @@ class OvsBrowser(BaseBrowser):
assert self.is_on_page(PageUserProfile) assert self.is_on_page(PageUserProfile)
self.page.create_thread(recipient, subject, body) 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): def get_french_cities(self):
self.location('http://www.onvasortir.com') self.location('http://www.onvasortir.com')
assert self.is_on_page(PageCityList) assert self.is_on_page(PageCityList)

View file

@ -19,7 +19,6 @@
import BeautifulSoup import BeautifulSoup
import datetime
def nearest_parent(node, expected): def nearest_parent(node, expected):
@ -39,15 +38,6 @@ def all_next_siblings(node):
node = node.nextSibling node = node.nextSibling
return ret 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): def image_to_text(src):
smileys = {'chat/e/grin.gif': ':D', smileys = {'chat/e/grin.gif': ':D',
'chat/e/unsure.gif': ':s', 'chat/e/unsure.gif': ':s',

View file

@ -24,6 +24,8 @@ import urllib
from urlparse import urlsplit from urlparse import urlsplit
from weboob.tools.browser import BasePage from weboob.tools.browser import BasePage
from weboob.capabilities.messages import Message, Thread from weboob.capabilities.messages import Message, Thread
from weboob.capabilities.contact import Contact, ProfileNode
from weboob.tools.date import parse_french_date
import ovsparse import ovsparse
@ -104,7 +106,7 @@ class PagePrivateThread(OvsPage):
# date will be used as id # date will be used as id
sitedate = profile_a.findParent('div').find(text=re.compile(',.*')).replace(', ', '') 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') compactdate = datetime.datetime.strftime(sysdate, '%Y%m%dT%H%M%S')
# but make it unique # but make it unique
@ -172,6 +174,66 @@ class PageUserProfile(OvsPage):
#~ self.browser['Message'] = body.encode(self.browser.ENCODING) #~ self.browser['Message'] = body.encode(self.browser.ENCODING)
#~ self.browser.submit() #~ 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): class PageCityList(DummyPage):
def get_cities(self, master_domain='onvasortir.com'): def get_cities(self, master_domain='onvasortir.com'):