support repositories to manage backends (closes #747)
This commit is contained in:
parent
ef16a5b726
commit
14a7a1d362
410 changed files with 1079 additions and 297 deletions
|
|
@ -23,23 +23,35 @@ try:
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from pysqlite2 import dbapi2 as sqlite
|
from pysqlite2 import dbapi2 as sqlite
|
||||||
|
|
||||||
|
from weboob.core import Weboob
|
||||||
|
from weboob.core.modules import ModuleLoadError
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
level = logging.DEBUG
|
level = logging.DEBUG
|
||||||
logging.basicConfig(stream=sys.stdout, level=level)
|
logging.basicConfig(stream=sys.stdout, level=level)
|
||||||
|
|
||||||
from weboob.backends.hds.browser import HDSBrowser
|
|
||||||
|
|
||||||
def main(filename):
|
def main(filename):
|
||||||
|
weboob = Weboob()
|
||||||
|
try:
|
||||||
|
hds = weboob.modules_loader.load_module('hds')
|
||||||
|
except ModuleLoadError, e:
|
||||||
|
print >>sys.stderr, 'Unable to load "hds" module: %s' % e
|
||||||
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db = sqlite.connect(database=filename, timeout=10.0)
|
db = sqlite.connect(database=filename, timeout=10.0)
|
||||||
except sqlite.OperationalError, err:
|
except sqlite.OperationalError, err:
|
||||||
print 'Unable to open %s database: %s' % (filename, err)
|
print >>sys.stderr, 'Unable to open %s database: %s' % (filename, err)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
sys.stdout.write('Reading database... ')
|
sys.stdout.write('Reading database... ')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
try:
|
||||||
results = db.execute('SELECT id, author FROM stories')
|
results = db.execute('SELECT id, author FROM stories')
|
||||||
|
except sqlite.OperationalError, err:
|
||||||
|
print >>sys.stderr, 'fail!\nUnable to read database: %s' % err
|
||||||
|
return 1
|
||||||
|
|
||||||
stored = set()
|
stored = set()
|
||||||
authors = set()
|
authors = set()
|
||||||
for r in results:
|
for r in results:
|
||||||
|
|
@ -48,7 +60,7 @@ def main(filename):
|
||||||
stored_authors = set([s[0] for s in db.execute('SELECT name FROM authors')])
|
stored_authors = set([s[0] for s in db.execute('SELECT name FROM authors')])
|
||||||
sys.stdout.write('ok\n')
|
sys.stdout.write('ok\n')
|
||||||
|
|
||||||
br = HDSBrowser()
|
br = hds.browser.HDSBrowser()
|
||||||
to_fetch = set()
|
to_fetch = set()
|
||||||
sys.stdout.write('Getting stories list from website... ')
|
sys.stdout.write('Getting stories list from website... ')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
|
||||||
1
modules/__init__.py
Normal file
1
modules/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
22
modules/arte/__init__.py
Normal file
22
modules/arte/__init__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .backend import ArteBackend
|
||||||
|
|
||||||
|
__all__ = ['ArteBackend']
|
||||||
68
modules/arte/backend.py
Normal file
68
modules/arte/backend.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
from weboob.capabilities.video import ICapVideo
|
||||||
|
from weboob.tools.backend import BaseBackend, BackendConfig
|
||||||
|
from weboob.tools.value import Value
|
||||||
|
|
||||||
|
from .browser import ArteBrowser
|
||||||
|
from .video import ArteVideo
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['ArteBackend']
|
||||||
|
|
||||||
|
|
||||||
|
class ArteBackend(BaseBackend, ICapVideo):
|
||||||
|
NAME = 'arte'
|
||||||
|
MAINTAINER = 'Romain Bignon'
|
||||||
|
EMAIL = 'romain@weboob.org'
|
||||||
|
VERSION = '0.a'
|
||||||
|
DESCRIPTION = 'Arte french TV'
|
||||||
|
LICENSE = 'AGPLv3+'
|
||||||
|
CONFIG = BackendConfig(Value('lang', label='Lang of videos',
|
||||||
|
choices={'fr': 'French', 'de': 'Deutsch', 'en': 'English'}, default='fr'),
|
||||||
|
Value('quality', label='Quality of videos', choices=['hd', 'sd'], default='hd'))
|
||||||
|
BROWSER = ArteBrowser
|
||||||
|
|
||||||
|
def create_default_browser(self):
|
||||||
|
return self.create_browser(lang=self.config['lang'].get(), quality=self.config['quality'].get())
|
||||||
|
|
||||||
|
def get_video(self, _id):
|
||||||
|
with self.browser:
|
||||||
|
return self.browser.get_video(_id)
|
||||||
|
|
||||||
|
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False, max_results=None):
|
||||||
|
with self.browser:
|
||||||
|
return self.browser.iter_search_results(pattern)
|
||||||
|
|
||||||
|
def fill_video(self, video, fields):
|
||||||
|
if fields != ['thumbnail']:
|
||||||
|
# if we don't want only the thumbnail, we probably want also every fields
|
||||||
|
with self.browser:
|
||||||
|
video = self.browser.get_video(ArteVideo.id2url(video.id), video)
|
||||||
|
if 'thumbnail' in fields and video.thumbnail:
|
||||||
|
with self.browser:
|
||||||
|
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
|
||||||
|
|
||||||
|
return video
|
||||||
|
|
||||||
|
OBJECTS = {ArteVideo: fill_video}
|
||||||
60
modules/arte/browser.py
Normal file
60
modules/arte/browser.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from weboob.tools.browser import BaseBrowser
|
||||||
|
from weboob.tools.browser.decorators import id2url
|
||||||
|
|
||||||
|
from .pages import IndexPage, VideoPage
|
||||||
|
from .video import ArteVideo
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['ArteBrowser']
|
||||||
|
|
||||||
|
|
||||||
|
class ArteBrowser(BaseBrowser):
|
||||||
|
DOMAIN = u'videos.arte.tv'
|
||||||
|
ENCODING = None
|
||||||
|
PAGES = {r'http://videos.arte.tv/\w+/videos/arte7.*': IndexPage,
|
||||||
|
r'http://videos.arte.tv/\w+/do_search/videos/.*': IndexPage,
|
||||||
|
r'http://videos.arte.tv/\w+/videos/(?P<id>.+)\.html': VideoPage
|
||||||
|
}
|
||||||
|
|
||||||
|
SEARCH_LANG = {'fr': 'recherche', 'de':'suche', 'en': 'search'}
|
||||||
|
|
||||||
|
def __init__(self, lang, quality, *args, **kwargs):
|
||||||
|
BaseBrowser.__init__(self, *args, **kwargs)
|
||||||
|
self.lang = lang
|
||||||
|
self.quality = quality
|
||||||
|
|
||||||
|
@id2url(ArteVideo.id2url)
|
||||||
|
def get_video(self, url, video=None):
|
||||||
|
self.location(url)
|
||||||
|
return self.page.get_video(video, self.lang, self.quality)
|
||||||
|
|
||||||
|
def home(self):
|
||||||
|
self.location('http://videos.arte.tv/fr/videos/arte7')
|
||||||
|
|
||||||
|
def iter_search_results(self, pattern):
|
||||||
|
if not pattern:
|
||||||
|
self.home()
|
||||||
|
else:
|
||||||
|
self.location(self.buildurl('/%s/do_search/videos/%s' % (self.lang, self.SEARCH_LANG[self.lang]), q=pattern.encode('utf-8')))
|
||||||
|
assert self.is_on_page(IndexPage)
|
||||||
|
return self.page.iter_videos()
|
||||||
BIN
modules/arte/favicon.png
Normal file
BIN
modules/arte/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 819 B |
114
modules/arte/pages.py
Normal file
114
modules/arte/pages.py
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from weboob.tools.browser import BasePage, BrokenPageError
|
||||||
|
|
||||||
|
|
||||||
|
from .video import ArteVideo
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['IndexPage', 'VideoPage']
|
||||||
|
|
||||||
|
|
||||||
|
class IndexPage(BasePage):
|
||||||
|
def iter_videos(self):
|
||||||
|
videos = self.document.getroot().cssselect("div[class=video]")
|
||||||
|
for div in videos:
|
||||||
|
title = div.find('h2').find('a').text
|
||||||
|
m = re.match(r'/fr/videos/(.*)\.html', div.find('h2').find('a').attrib['href'])
|
||||||
|
_id = ''
|
||||||
|
if m:
|
||||||
|
_id = m.group(1)
|
||||||
|
rating = rating_max = 0
|
||||||
|
rates = self.parser.select(div, 'div[class=rateContainer]', 1)
|
||||||
|
for r in rates.findall('div'):
|
||||||
|
if 'star-rating-on' in r.attrib['class']:
|
||||||
|
rating += 1
|
||||||
|
rating_max += 1
|
||||||
|
|
||||||
|
video = ArteVideo(_id)
|
||||||
|
video.title = title
|
||||||
|
video.rating = rating
|
||||||
|
video.rating_max = rating_max
|
||||||
|
|
||||||
|
thumb = self.parser.select(div, 'img[class=thumbnail]', 1)
|
||||||
|
video.thumbnail_url = 'http://videos.arte.tv' + thumb.attrib['src']
|
||||||
|
|
||||||
|
try:
|
||||||
|
parts = self.parser.select(div, 'div.duration_thumbnail', 1).text.split(':')
|
||||||
|
if len(parts) == 2:
|
||||||
|
hours = 0
|
||||||
|
minutes, seconds = parts
|
||||||
|
elif len(parts) == 3:
|
||||||
|
hours, minutes, seconds = parts
|
||||||
|
else:
|
||||||
|
raise BrokenPageError('Unable to parse duration %r' % parts)
|
||||||
|
except BrokenPageError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
video.duration = datetime.timedelta(hours=int(hours), minutes=int(minutes), seconds=int(seconds))
|
||||||
|
|
||||||
|
yield video
|
||||||
|
|
||||||
|
class VideoPage(BasePage):
|
||||||
|
def get_video(self, video=None, lang='fr', quality='hd'):
|
||||||
|
if not video:
|
||||||
|
video = ArteVideo(self.group_dict['id'])
|
||||||
|
video.title = self.get_title()
|
||||||
|
video.url = self.get_url(lang, quality)
|
||||||
|
return video
|
||||||
|
|
||||||
|
def get_title(self):
|
||||||
|
return self.document.getroot().cssselect('h2')[0].text
|
||||||
|
|
||||||
|
def get_url(self, lang, quality):
|
||||||
|
obj = self.parser.select(self.document.getroot(), 'object', 1)
|
||||||
|
movie_url = self.parser.select(obj, 'param[name=movie]', 1)
|
||||||
|
xml_url = urllib.unquote(movie_url.attrib['value'].split('videorefFileUrl=')[-1])
|
||||||
|
|
||||||
|
doc = self.browser.get_document(self.browser.openurl(xml_url))
|
||||||
|
videos_list = self.parser.select(doc.getroot(), 'video')
|
||||||
|
videos = {}
|
||||||
|
for v in videos_list:
|
||||||
|
videos[v.attrib['lang']] = v.attrib['ref']
|
||||||
|
|
||||||
|
if lang in videos:
|
||||||
|
xml_url = videos[lang]
|
||||||
|
else:
|
||||||
|
xml_url = videos.popitem()[1]
|
||||||
|
|
||||||
|
doc = self.browser.get_document(self.browser.openurl(xml_url))
|
||||||
|
|
||||||
|
obj = self.parser.select(doc.getroot(), 'urls', 1)
|
||||||
|
videos_list = self.parser.select(obj, 'url')
|
||||||
|
urls = {}
|
||||||
|
for v in videos_list:
|
||||||
|
urls[v.attrib['quality']] = v.text
|
||||||
|
|
||||||
|
if quality in urls:
|
||||||
|
video_url = urls[quality]
|
||||||
|
else:
|
||||||
|
video_url = urls.popitem()[1]
|
||||||
|
|
||||||
|
return video_url
|
||||||
31
modules/arte/test.py
Normal file
31
modules/arte/test.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from weboob.tools.test import BackendTest
|
||||||
|
|
||||||
|
class ArteTest(BackendTest):
|
||||||
|
BACKEND = 'arte'
|
||||||
|
|
||||||
|
def test_arte(self):
|
||||||
|
l = list(self.backend.iter_search_results('arte'))
|
||||||
|
if len(l) > 0:
|
||||||
|
v = l[0]
|
||||||
|
self.backend.fillobj(v, ('url',))
|
||||||
|
self.assertTrue(v.url and v.url.startswith('rtmp://'), 'URL for video "%s" not found: %s' % (v.id, v.url))
|
||||||
30
modules/arte/video.py
Normal file
30
modules/arte/video.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Christophe Benz
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from weboob.capabilities.video import BaseVideo
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['ArteVideo']
|
||||||
|
|
||||||
|
|
||||||
|
class ArteVideo(BaseVideo):
|
||||||
|
@classmethod
|
||||||
|
def id2url(cls, _id):
|
||||||
|
return 'http://videos.arte.tv/fr/videos/%s.html' % _id
|
||||||
932
modules/aum/API.txt
Normal file
932
modules/aum/API.txt
Normal file
|
|
@ -0,0 +1,932 @@
|
||||||
|
Adopte un Mec API
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Constants:
|
||||||
|
|
||||||
|
APIKEY = fb0123456789abcd
|
||||||
|
URL = http://api.adopteunmec.com/api.php
|
||||||
|
|
||||||
|
|
||||||
|
ME Commands
|
||||||
|
===========
|
||||||
|
|
||||||
|
me.login
|
||||||
|
---------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- login
|
||||||
|
- pass
|
||||||
|
|
||||||
|
Errors:
|
||||||
|
- 1.1.1 : invalid login
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'baskets': u'384',
|
||||||
|
u'events': {u'lastBasket': {u'alert': u'1',
|
||||||
|
u'birthday': u'1989-02-04',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'14471939',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 13:56:36',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'kloo',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'9/3/9/1/7/4/4/',
|
||||||
|
u'pseudo': u'Kloolloo',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 9,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14471939/Kloolloo',
|
||||||
|
u'zip': u'75001'},
|
||||||
|
u'lastChat': {u'alert': u'1',
|
||||||
|
u'birthday': u'1987-10-10',
|
||||||
|
u'city': u'Paris 18e Arrondissement',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'0',
|
||||||
|
u'id': u'13280274',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2010-11-20 03:37:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'#c1a63bda81cc03ccdf080ca6e003919e',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'4/7/2/0/8/2/3/',
|
||||||
|
u'pseudo': u'Katie Lee',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 4,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls_coma',
|
||||||
|
u'url': u'/api.php?member/view/13280274/KatieLee',
|
||||||
|
u'zip': u'75018'},
|
||||||
|
u'lastFlash': {u'alert': u'1',
|
||||||
|
u'birthday': u'1983-12-20',
|
||||||
|
u'city': u'Longjumeau',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'13883475',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 18:52:13',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'5/7/4/3/8/8/3/',
|
||||||
|
u'pseudo': u'Pas Tjs Sage',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 5,
|
||||||
|
u'style': u'2',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13883475/PasTjsSage',
|
||||||
|
u'zip': u'91160'},
|
||||||
|
u'lastMail': {u'alert': u'1',
|
||||||
|
u'birthday': u'1989-04-28',
|
||||||
|
u'city': u'Paris 8e Arrondissement',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'5',
|
||||||
|
u'id': u'13268738',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 19:24:14',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@13268738',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'8/3/7/8/6/2/3/',
|
||||||
|
u'pseudo': u'Th\xe9na',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 8,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13268738/Thna',
|
||||||
|
u'zip': u'75008'},
|
||||||
|
u'lastVisit': {u'alert': u'1',
|
||||||
|
u'birthday': u'1988-02-27',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'14477637',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 18:31:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'7/3/6/7/7/4/4/',
|
||||||
|
u'pseudo': u'Lolly',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 7,
|
||||||
|
u'style': u'4',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14477637/Lolly',
|
||||||
|
u'zip': u'75005'}},
|
||||||
|
u'flashs': 10,
|
||||||
|
u'mails': u'731',
|
||||||
|
u'me': {u'about1': u"Je n'ai pas de temps à perdre, je n'ai ni MSN ni Facebook, je considère qu'on apprécie davantage la discussion face à face autour d'un verre que dans les yeux de son écran.\r<br>\r<br>Bon et ne venez que si vous avez quelque chose à me dire, je n'envoie jamais de charmes.",
|
||||||
|
u'about2': u'',
|
||||||
|
u'admin': u'0',
|
||||||
|
u'alert': u'0',
|
||||||
|
u'alert_add': u'6',
|
||||||
|
u'birthday': u'1986-08-13',
|
||||||
|
u'books': u"Orwell (1984, La ferme des animaux)<br>Barjavel (La nuit des temps, Le voyageur imprudent, Ravage, \x85)<br>Boris Vian (J'irai cracher sur vos tombes, L'écume des jours\x85)<br>Bukowski, Desproges, San Antonio<br>Sartre, Le Canard",
|
||||||
|
u'cat': u'1',
|
||||||
|
u'checks1': u'0',
|
||||||
|
u'checks2': u'16686',
|
||||||
|
u'checks3': u'0',
|
||||||
|
u'checks4': u'0',
|
||||||
|
u'checks5': u'0',
|
||||||
|
u'checks6': u'2',
|
||||||
|
u'checks7': u'0',
|
||||||
|
u'cinema': u'Le Grand Détournement \x97 La Classe Américaine<br>V pour Vendetta, Pulp Fiction, The Truman Show<br>Eternal Sunshine of the Spotless Mind, Match Point<br>Idiocracy, The Big Lebowski, La cité de la peur<br>Sin City, Orange Mecanique, Buffet Froid, L',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'5',
|
||||||
|
u'drink': u'2',
|
||||||
|
u'email': u'tesiruna@parano.me',
|
||||||
|
u'eyes': u'3',
|
||||||
|
u'f': u'',
|
||||||
|
u'first_cnx': u'2010-05-16 09:13:53',
|
||||||
|
u'first_ip': u'81.57.125.104',
|
||||||
|
u'food': u'1',
|
||||||
|
u'godfather': u'0',
|
||||||
|
u'hair_color': u'5',
|
||||||
|
u'hair_size': u'3',
|
||||||
|
u'hobbies': u'',
|
||||||
|
u'id': u'22450639',
|
||||||
|
u'img_count': u'5',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'job': u'Dieu',
|
||||||
|
u'last_chat': u'-0001-11-29 23:09:21',
|
||||||
|
u'last_cnx': u'2011-09-20 19:24:25',
|
||||||
|
u'last_ip': u'88.161.27.232',
|
||||||
|
u'lat': u'48.861961',
|
||||||
|
u'latR': u'0.852802098431',
|
||||||
|
u'list1': u'2',
|
||||||
|
u'list2': u'2',
|
||||||
|
u'list3': u'2',
|
||||||
|
u'list4': u'3',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'list6': u'0',
|
||||||
|
u'lng': u'2.33594',
|
||||||
|
u'lngR': u'0.040769844129',
|
||||||
|
u'login': u'@22450639',
|
||||||
|
u'mod_level': u'1',
|
||||||
|
u'music': u"Pink Floyd, Scorpions, Emperor<br>Metallica, Iron Maiden, Accept<br>Slash's Snakepit, Queen, Deep Purple<br>Led Zeppelin, Rolling Stones<br>Brassens, Souchon, Brel, Vian",
|
||||||
|
u'origins': u'1',
|
||||||
|
u'pass': u'8f3fa83cec9a243ae53c1337d2b5e1cf',
|
||||||
|
u'path': u'9/3/6/0/5/4/2/',
|
||||||
|
u'phone': u'-',
|
||||||
|
u'pictures': [{u'file': u'5',
|
||||||
|
u'height': u'427',
|
||||||
|
u'id': u'7315391',
|
||||||
|
u'md5': u'859fcd2e425617c33c16d6a1bc510ad3',
|
||||||
|
u'member': u'22450639',
|
||||||
|
u'rank': u'1',
|
||||||
|
u'valid': u'1578737',
|
||||||
|
u'width': u'500'}],
|
||||||
|
u'pseudo': u'Nazification',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 0,
|
||||||
|
u'shape': u'1',
|
||||||
|
u'shard': 9,
|
||||||
|
u'size': u'175',
|
||||||
|
u'smoke': u'2',
|
||||||
|
u'style': u'0',
|
||||||
|
u'subregion': u'76',
|
||||||
|
u'table': u'adopteun.boys',
|
||||||
|
u'texts1': u'',
|
||||||
|
u'texts2': u'',
|
||||||
|
u'texts3': u'',
|
||||||
|
u'texts4': u'',
|
||||||
|
u'texts5': u'',
|
||||||
|
u'texts6': u'',
|
||||||
|
u'title': u'',
|
||||||
|
u'tvs': u'Je ne possède pas la TV<br><br><br><br>',
|
||||||
|
u'url': u'/api.php?member/view/22450639/Nazification',
|
||||||
|
u'validated': True,
|
||||||
|
u'visites': u'0',
|
||||||
|
u'w': u'',
|
||||||
|
u'warn': u'0',
|
||||||
|
u'weight': u'55',
|
||||||
|
u'zip': u'75000'},
|
||||||
|
u'news': {u'newBaskets': 3, u'newMails': 1, u'newVisits': 113},
|
||||||
|
u'popu': u'71540',
|
||||||
|
u'subMobile': False,
|
||||||
|
u'subWebsite': False,
|
||||||
|
u'token': u'ea7bac8837f6e5bb1e2a3267d6a08b32e388d593',
|
||||||
|
u'visites': u'958'}}
|
||||||
|
|
||||||
|
me.[default]
|
||||||
|
------------
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'events': {u'lastBasket': {u'alert': u'1',
|
||||||
|
u'birthday': u'1989-02-04',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'14471939',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 13:56:36',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'kloo',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'9/3/9/1/7/4/4/',
|
||||||
|
u'pseudo': u'Kloolloo',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 9,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14471939/Kloolloo',
|
||||||
|
u'zip': u'75001'},
|
||||||
|
u'lastChat': {u'alert': u'1',
|
||||||
|
u'birthday': u'1987-10-10',
|
||||||
|
u'city': u'Paris 18e Arrondissement',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'0',
|
||||||
|
u'id': u'13280274',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2010-11-20 03:37:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'#c1a63bda81cc03ccdf080ca6e003919e',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'4/7/2/0/8/2/3/',
|
||||||
|
u'pseudo': u'Katie Lee',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 4,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls_coma',
|
||||||
|
u'url': u'/api.php?member/view/13280274/KatieLee',
|
||||||
|
u'zip': u'75018'},
|
||||||
|
u'lastFlash': {u'alert': u'1',
|
||||||
|
u'birthday': u'1983-12-20',
|
||||||
|
u'city': u'Longjumeau',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'13883475',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 18:52:13',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'5/7/4/3/8/8/3/',
|
||||||
|
u'pseudo': u'Pas Tjs Sage',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 5,
|
||||||
|
u'style': u'2',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13883475/PasTjsSage',
|
||||||
|
u'zip': u'91160'},
|
||||||
|
u'lastMail': {u'alert': u'1',
|
||||||
|
u'birthday': u'1989-04-28',
|
||||||
|
u'city': u'Paris 8e Arrondissement',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'5',
|
||||||
|
u'id': u'13268738',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 19:47:28',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@13268738',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'8/3/7/8/6/2/3/',
|
||||||
|
u'pseudo': u'Th\xe9na',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 8,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13268738/Thna',
|
||||||
|
u'zip': u'75008'},
|
||||||
|
u'lastVisit': {u'alert': u'1',
|
||||||
|
u'birthday': u'1988-02-27',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'id': u'14477637',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 19:09:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'7/3/6/7/7/4/4/',
|
||||||
|
u'pseudo': u'Lolly',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 7,
|
||||||
|
u'style': u'4',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14477637/Lolly',
|
||||||
|
u'zip': u'75005'}},
|
||||||
|
u'news': {u'newBaskets': 0, u'newMails': 1, u'newVisits': 113},
|
||||||
|
u'token': u'9a97a03774c9f440e676c78f48794a7221a67285'}}
|
||||||
|
|
||||||
|
me.basket
|
||||||
|
----------
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'popu': 71840,
|
||||||
|
u'result': {u'basket': [{u'alert': u'1',
|
||||||
|
u'birthday': u'1989-02-04',
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'date': u'2011-09-19 01:52:29',
|
||||||
|
u'id': u'14471939',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 13:56:36',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'kloo',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'9/3/9/1/7/4/4/',
|
||||||
|
u'pseudo': u'Kloolloo',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 9,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14471939/Kloolloo',
|
||||||
|
u'zip': u'75001'}],
|
||||||
|
u'inBasket': 57,
|
||||||
|
u'token': u'ea7bac8837f6e5bb1e2a3267d6a08b32e388d593'}}
|
||||||
|
|
||||||
|
me.flashs
|
||||||
|
---------
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'all': [{u'date': u'2011-10-19 10:50:43',
|
||||||
|
u'fid': u'41311269',
|
||||||
|
u'id': u'12656592',
|
||||||
|
u'member': {u'alert': u'1',
|
||||||
|
u'birthday': u'1986-08-08',
|
||||||
|
u'city': u'Armes',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'3',
|
||||||
|
u'id': u'12656592',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-10-19 17:28:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@12656592',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'2/9/5/6/5/6/2/',
|
||||||
|
u'pseudo': u'Yayanne',
|
||||||
|
u'region': u'5',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 2,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/12656592/Yayanne',
|
||||||
|
u'zip': u'58500'},
|
||||||
|
u'seen': u'2011-10-19 10:54:09'}],
|
||||||
|
u'count': 1467,
|
||||||
|
u'news': [],
|
||||||
|
u'olds': [],
|
||||||
|
u'popu': u'110350',
|
||||||
|
u'token': u'3af90dd563431fe6b4c65930d18337c997fac34e'}}
|
||||||
|
|
||||||
|
me.visits
|
||||||
|
---------
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'count': u'607',
|
||||||
|
u'news': [],
|
||||||
|
u'offset': 0,
|
||||||
|
u'olds': [{u'alert': u'0',
|
||||||
|
u'birthday': u'1985-01-29',
|
||||||
|
u'city': u'Albi',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'11',
|
||||||
|
u'date': u'2011-10-19 17:40:43',
|
||||||
|
u'id': u'13461054',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-10-19 17:47:25',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@13461054',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'4/5/0/1/6/4/3/',
|
||||||
|
u'pseudo': u'Bruume',
|
||||||
|
u'region': u'15',
|
||||||
|
u'seen': u'2011-10-19 17:42:55',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 4,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13461054/Bruume',
|
||||||
|
u'vid': u'199226074',
|
||||||
|
u'zip': u'81000'}],
|
||||||
|
u'popu': u'110350',
|
||||||
|
u'token': u'd7380871794008c94971acec82b7b679dd800307'}}
|
||||||
|
|
||||||
|
MESSAGE Commands
|
||||||
|
================
|
||||||
|
|
||||||
|
message.[default]
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- P=<NB_ENTRIES>,<START>
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'count': 1,
|
||||||
|
u'threads': [{u'cat': u'0',
|
||||||
|
u'date': u'2011-09-20 19:22:11',
|
||||||
|
u'id': u'11132125',
|
||||||
|
u'id_from': u'13268738',
|
||||||
|
u'id_to': u'22450639',
|
||||||
|
u'member': {u'alert': u'1',
|
||||||
|
u'birthday': u'1989-04-28',
|
||||||
|
u'city': u'Paris 8e Arrondissement',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'5',
|
||||||
|
u'id': u'13268738',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-20 19:54:15',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@13268738',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'8/3/7/8/6/2/3/',
|
||||||
|
u'pseudo': u'Th\xe9na',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 8,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13268738/Thna',
|
||||||
|
u'zip': u'75008'},
|
||||||
|
u'message': u'0',
|
||||||
|
u'status': u'0',
|
||||||
|
u'title': u"J'aime bien les \xe9l\xe9phants. Toi ?"}],
|
||||||
|
u'token': u'0f70c31bc6d05d45bee64e6f2eab9b537640f2f8'}}
|
||||||
|
|
||||||
|
message.thread
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- memberId
|
||||||
|
- count
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'result': {u'popu': u'15910',
|
||||||
|
u'thread': {u'isNew': False,
|
||||||
|
u'member': {u'alert': u'3',
|
||||||
|
u'birthday': u'1987-04-22',
|
||||||
|
u'city': u'Maisons-Alfort',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'6',
|
||||||
|
u'id': u'14022243',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 18:18:00',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'3/4/2/2/2/0/4/',
|
||||||
|
u'pseudo': u'Sophkipeut',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 3,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14022243/Sophkipeut',
|
||||||
|
u'zip': u'94700'},
|
||||||
|
u'messages': [{u'date': u'2011-09-19 17:52:03',
|
||||||
|
u'id': u'46583458',
|
||||||
|
u'id_from': u'14022243',
|
||||||
|
u'id_to': u'23185402',
|
||||||
|
u'message': u"Lol, moi j'ai fait attention, mais bon ça n'empêche pas son bidou, ceci dit c'est planqué par ses loooooooooooong poils ^^",
|
||||||
|
u'src': u'',
|
||||||
|
u'title': u"Lol, moi j'ai fait attention, mais bon \xe7a n'emp\xea..."},
|
||||||
|
{u'date': u'2011-09-19 17:49:39',
|
||||||
|
u'id': u'47467748',
|
||||||
|
u'id_from': u'23185402',
|
||||||
|
u'id_to': u'14022243',
|
||||||
|
u'message': u"Ah oui, justement le vétérinaire m'avait dit après la castration de Futex qu'il fallait faire attention à son poids, du coup ça m'a tellement vexé que j'ai fais attention au point qu'il est sans doute même trop maigre.",
|
||||||
|
u'src': u'',
|
||||||
|
u'title': u"Ah oui, justement le v\xe9t\xe9rinaire m'avait dit apr\xe8..."}],
|
||||||
|
u'remoteStatus': u'2',
|
||||||
|
u'status': u'1',
|
||||||
|
u'warning': 0},
|
||||||
|
u'token': u'dbeccf96256d4f11991626707881fdba28f54d73'}}
|
||||||
|
|
||||||
|
message.new
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- memberId
|
||||||
|
- message
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'thread': {u'isNew': False,
|
||||||
|
u'member': {u'alert': u'1',
|
||||||
|
u'birthday': u'1986-12-08',
|
||||||
|
u'city': u'Rosny-sous-Bois',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'6',
|
||||||
|
u'id': u'11099536',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'last_cnx': u'2011-09-20 16:26:32',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@11099536',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'6/3/5/9/9/0/1/',
|
||||||
|
u'pseudo': u'Debo',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 6,
|
||||||
|
u'style': u'0',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/11099536/Debo',
|
||||||
|
u'zip': u'93110'},
|
||||||
|
u'messages': [{u'date': u'2011-09-20 20:09:07',
|
||||||
|
u'id': u'46588573',
|
||||||
|
u'id_from': u'23185402',
|
||||||
|
u'id_to': u'11099536',
|
||||||
|
u'message': u'Coucou',
|
||||||
|
u'src': u'iphone',
|
||||||
|
u'title': u'Coucou'},
|
||||||
|
{u'date': u'2011-09-20 16:27:34',
|
||||||
|
u'id': u'46638469',
|
||||||
|
u'id_from': u'11099536',
|
||||||
|
u'id_to': u'23185402',
|
||||||
|
u'message': u"Coucou pour tout t'avouer je ne m'y etais pas connecté depuis septembre ! un peu moins de boulot alors j'y traine !!\r\n\r\nEt toi ça va? tu devais pas partir au canada? tu es deja revenu.?",
|
||||||
|
u'src': u'',
|
||||||
|
u'title': u"Coucou pour tout t'avouer je ne m'y etais pas co..."}],
|
||||||
|
u'remoteStatus': u'0',
|
||||||
|
u'status': u'2',
|
||||||
|
u'warning': 0},
|
||||||
|
u'token': u'2491f8ccb6741ceee2a461dc523939796904a0fa'}}
|
||||||
|
|
||||||
|
message.delete
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- id_user
|
||||||
|
|
||||||
|
Return Value:
|
||||||
|
Unknown
|
||||||
|
|
||||||
|
MEMBER Commands
|
||||||
|
===============
|
||||||
|
|
||||||
|
member.view
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- id
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'member': {u'about1': u"comment te dire.. j'ai autant envie te laisser boire dans ma bouteille que mettre ma langue dans ta bouche !",
|
||||||
|
u'about2': u"LES BISOUS C'EST BIEN LES BIJOUX C'EST MIEUX!\r<br>\r<br>Et l'humour encore plus, mouhaha\r<br>\r<br>PS: Si vous êtes le sosie de Pharell Williams, adoptez moi ;)",
|
||||||
|
u'admin': u'0',
|
||||||
|
u'alert': u'1',
|
||||||
|
u'alert_add': u'0',
|
||||||
|
u'birthday': u'1991-05-31',
|
||||||
|
u'books': u"L'Analphabète - Rendell<br>Etat limite - Assouline<br>Marie-Antoinette - Zweig<br><br>",
|
||||||
|
u'cat': u'2',
|
||||||
|
u'checks1': u'2',
|
||||||
|
u'checks2': u'1588',
|
||||||
|
u'checks3': u'0',
|
||||||
|
u'checks4': u'0',
|
||||||
|
u'checks5': u'64',
|
||||||
|
u'checks6': u'192',
|
||||||
|
u'checks7': u'0',
|
||||||
|
u'cinema': u"My Blueberry Night<br>Shinning - L'exorciste - Ester - Gothika<br>How High ! - Requiem for a dream<br>Remember Me - trainspotting<br>He gots game - Buffet froid",
|
||||||
|
u'city': u'Paris',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'1',
|
||||||
|
u'drink': u'2',
|
||||||
|
u'eyes': u'5',
|
||||||
|
u'f': u'',
|
||||||
|
u'first_cnx': u'2011-09-17 00:18:59',
|
||||||
|
u'first_ip': u'82.120.134.233',
|
||||||
|
u'food': u'3',
|
||||||
|
u'godfather': u'0',
|
||||||
|
u'hair_color': u'4',
|
||||||
|
u'hair_size': u'3',
|
||||||
|
u'hobbies': u'Le théâtre définitivement ! et le sport !',
|
||||||
|
u'id': u'14465370',
|
||||||
|
u'img_count': u'6',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': False,
|
||||||
|
u'job': u'etudiante',
|
||||||
|
u'last_chat': u'0000-00-00 00:00:00',
|
||||||
|
u'last_cnx': u'2011-09-24 00:09:29',
|
||||||
|
u'last_ip': u'92.151.177.164',
|
||||||
|
u'lat': u'48.8814',
|
||||||
|
u'latR': u'0.853141372984',
|
||||||
|
u'list1': u'0',
|
||||||
|
u'list2': u'42',
|
||||||
|
u'list3': u'0',
|
||||||
|
u'list4': u'0',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'list6': u'0',
|
||||||
|
u'lng': u'2.3365',
|
||||||
|
u'lngR': u'0.0407796179728',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mailable': True,
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'music': u'Daft Punk - Bloody Beetrots - Kid Cudi - Jamiroquai<br>Red Hot - Ray Charles - BEP - Gorillaz - Birdy nam nam<br>Citizen Cope - Angus & Julia Stone - Portishead<br>50cent - Sia - Ben Harper - Busta Rhymes<br>Musiques de gansta ! ET Debussy',
|
||||||
|
u'origins': u'1',
|
||||||
|
u'path': u'0/7/3/5/6/4/4/',
|
||||||
|
u'phone': u'-',
|
||||||
|
u'popu': {u'bonus': u'6',
|
||||||
|
u'contacts': u'1',
|
||||||
|
u'flashs': u'246',
|
||||||
|
u'id': u'14465370',
|
||||||
|
u'invits': u'0',
|
||||||
|
u'mails': u'39',
|
||||||
|
u'popu': u'11345',
|
||||||
|
u'visites': u'645'},
|
||||||
|
u'pseudo': u'Ruslana',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shape': u'1',
|
||||||
|
u'shard': 0,
|
||||||
|
u'size': u'170',
|
||||||
|
u'smoke': u'2',
|
||||||
|
u'style': u'4',
|
||||||
|
u'subregion': u'76',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'texts1': u'',
|
||||||
|
u'texts2': u'',
|
||||||
|
u'texts3': u'',
|
||||||
|
u'texts4': u'',
|
||||||
|
u'texts5': u'',
|
||||||
|
u'texts6': u'MON SOURIRE HAHAHA',
|
||||||
|
u'title': u'',
|
||||||
|
u'tvs': u'Dexter<br>OC<br>True blood<br>Envoyé spécial ;) - Arte !<br>',
|
||||||
|
u'url': u'/api.php?member/view/14465370/Ruslana',
|
||||||
|
u'visites': u'0',
|
||||||
|
u'w': u'',
|
||||||
|
u'warn': u'0',
|
||||||
|
u'weight': u'50',
|
||||||
|
u'zip': u'75008'},
|
||||||
|
u'token': u'e0247704012e01bc32756b357b010e5206ac9c76'}}
|
||||||
|
|
||||||
|
member.pictures
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- id
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'pictures': [{u'id': u'12363004',
|
||||||
|
u'rating': 4.4473684210500002,
|
||||||
|
u'url': u'http://s0.adopteunmec.com/0/6/0/1/6/image7.jpg'}],
|
||||||
|
u'token': u'e131f1b194f2a19337882398b10b79457a638252'}}
|
||||||
|
|
||||||
|
member.addBasket
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- id
|
||||||
|
|
||||||
|
Errors:
|
||||||
|
- 5.1.1 : member does not exist
|
||||||
|
- 5.1.5 : already sent charm to this one
|
||||||
|
- 5.1.6 : no enough charms available
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': u'0',
|
||||||
|
u'flashs': 4,
|
||||||
|
u'result': {u'token': u'55039d0557393bb7c5e4381792143d003f0e60c0'}}
|
||||||
|
|
||||||
|
SEARH Commands
|
||||||
|
==============
|
||||||
|
|
||||||
|
search.[default]
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'qsearch': {u'ageMax': u'25',
|
||||||
|
u'ageMin': u'18',
|
||||||
|
u'dist': u'0',
|
||||||
|
u'new': u'0',
|
||||||
|
u'query': u'{"sex":1,"ageMin":"18","ageMax":"25","region":"fr","new":"0","dist":"0"}',
|
||||||
|
u'region': u'fr',
|
||||||
|
u'sex': 1},
|
||||||
|
u'search': {u'ageMax': u'27',
|
||||||
|
u'ageMin': u'20',
|
||||||
|
u'checks1': u'0',
|
||||||
|
u'checks2': u'0',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'dist': u'50',
|
||||||
|
u'drink': u'0',
|
||||||
|
u'eyes': u'0',
|
||||||
|
u'food': u'0',
|
||||||
|
u'hair_color': u'0',
|
||||||
|
u'hair_size': u'0',
|
||||||
|
u'origins': u'0',
|
||||||
|
u'pseudo': u'',
|
||||||
|
u'query': u'{"ageMin":"20","ageMax":"27","country":"fr","region":"11","subregion":"0","dist":"50","pseudo":"","sex":"1","sizeMin":"0","sizeMax":"0","weightMin":"0","weightMax":"75","shape":"0","hair_size":"0","hair_color":"0","eyes":"0","origins":"0","style":"0","checks1":"0","checks2":"0","smoke":"0","drink":"0","food":"0","search":"true"}',
|
||||||
|
u'region': u'11',
|
||||||
|
u'search': u'true',
|
||||||
|
u'sex': u'1',
|
||||||
|
u'shape': u'0',
|
||||||
|
u'sizeMax': u'0',
|
||||||
|
u'sizeMin': u'0',
|
||||||
|
u'smoke': u'0',
|
||||||
|
u'style': u'0',
|
||||||
|
u'subregion': u'0',
|
||||||
|
u'weightMax': u'75',
|
||||||
|
u'weightMin': u'0'},
|
||||||
|
u'token': u'3196a3365d927f2ee8738ec8dfc4a5abd75e3ee3'}}
|
||||||
|
|
||||||
|
search.quick
|
||||||
|
------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- sex (int[0,1])
|
||||||
|
- ageMin (int)
|
||||||
|
- ageMax (int)
|
||||||
|
- region (str)
|
||||||
|
- new (int)
|
||||||
|
- dist (int)
|
||||||
|
|
||||||
|
Return Value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'result': {u'regions': {u'be': None,
|
||||||
|
u'be_24': u' - wallonie',
|
||||||
|
u'be_25': u' - bruxelles capitale',
|
||||||
|
u'be_26': u' - flandre',
|
||||||
|
u'ca': None,
|
||||||
|
u'ca_34': u' - canada',
|
||||||
|
u'ca_35': u' - quebec',
|
||||||
|
u'ch': None,
|
||||||
|
u'ch_27': u' - r\xe9gion l\xe9manique',
|
||||||
|
u'ch_28': u' - espace Mittelland',
|
||||||
|
u'ch_29': u' - suisse du nord-ouest',
|
||||||
|
u'ch_30': u' - zurich',
|
||||||
|
u'ch_31': u' - suisse orientale',
|
||||||
|
u'ch_32': u' - suisse centrale',
|
||||||
|
u'ch_33': u' - tessin',
|
||||||
|
u'fr': None,
|
||||||
|
u'fr_1': u' - alsace',
|
||||||
|
u'fr_10': u' - haute-normandie',
|
||||||
|
u'fr_11': u' - ile-de-france',
|
||||||
|
u'fr_12': u' - languedoc-roussillon',
|
||||||
|
u'fr_13': u' - limousin',
|
||||||
|
u'fr_14': u' - lorraine',
|
||||||
|
u'fr_15': u' - midi-pyr\xe9n\xe9es',
|
||||||
|
u'fr_16': u' - nord-pas-de-calais',
|
||||||
|
u'fr_17': u' - paca',
|
||||||
|
u'fr_18': u' - pays de la loire',
|
||||||
|
u'fr_19': u' - picardie',
|
||||||
|
u'fr_2': u' - aquitaine',
|
||||||
|
u'fr_20': u' - poitou-charentes',
|
||||||
|
u'fr_21': u' - rh\xf4ne-alpes',
|
||||||
|
u'fr_22': u' - corse',
|
||||||
|
u'fr_23': u' - dom+tom',
|
||||||
|
u'fr_3': u' - auvergne',
|
||||||
|
u'fr_4': u' - basse-normandie',
|
||||||
|
u'fr_5': u' - bourgogne',
|
||||||
|
u'fr_6': u' - bretagne',
|
||||||
|
u'fr_7': u' - centre',
|
||||||
|
u'fr_8': u' - champagne-ardenne',
|
||||||
|
u'fr_9': u' - franche-comt\xe9'},
|
||||||
|
u'search': [{u'alert': u'2',
|
||||||
|
u'birthday': u'1985-11-21',
|
||||||
|
u'city': u'Boulogne-Billancourt',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'12',
|
||||||
|
u'id': u'14252744',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-24 15:19:48',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'4/4/7/2/5/2/4/',
|
||||||
|
u'pseudo': u'Birdy',
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 4,
|
||||||
|
u'style': u'5',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/14252744/Birdy',
|
||||||
|
u'zip': u'92100'}],
|
||||||
|
u'token': u'ef78ac6d812ab15f2f8efd578f4da4ef2e23aa71'}}
|
||||||
|
|
||||||
|
search.advanced
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- ageMin (int)
|
||||||
|
- ageMax (int)
|
||||||
|
- country (str)
|
||||||
|
- region (int)
|
||||||
|
- subregion (int)
|
||||||
|
- dist (int)
|
||||||
|
- pseudo (str)
|
||||||
|
- sex (int[0,1])
|
||||||
|
- sizeMin (int)
|
||||||
|
- sizeMax (int)
|
||||||
|
- weightMin (int)
|
||||||
|
- weightMax (int)
|
||||||
|
- shape (int)
|
||||||
|
- hair_size (int)
|
||||||
|
- hair_color (int)
|
||||||
|
- eyes (int)
|
||||||
|
- origins (int)
|
||||||
|
- style (int)
|
||||||
|
- checks1 (int)
|
||||||
|
- checks2 (int)
|
||||||
|
- smoke (int)
|
||||||
|
- drink (int)
|
||||||
|
- food (int)
|
||||||
|
- search (bool)
|
||||||
|
|
||||||
|
Return Value:
|
||||||
|
{u'errors': [],
|
||||||
|
u'regions': {u'be': None,
|
||||||
|
u'be_24': u' - wallonie',
|
||||||
|
u'be_25': u' - bruxelles capitale',
|
||||||
|
u'be_26': u' - flandre',
|
||||||
|
u'ca': None,
|
||||||
|
u'ca_34': u' - canada',
|
||||||
|
u'ca_35': u' - quebec',
|
||||||
|
u'ch': None,
|
||||||
|
u'ch_27': u' - r\xe9gion l\xe9manique',
|
||||||
|
u'ch_28': u' - espace Mittelland',
|
||||||
|
u'ch_29': u' - suisse du nord-ouest',
|
||||||
|
u'ch_30': u' - zurich',
|
||||||
|
u'ch_31': u' - suisse orientale',
|
||||||
|
u'ch_32': u' - suisse centrale',
|
||||||
|
u'ch_33': u' - tessin',
|
||||||
|
u'fr': None,
|
||||||
|
u'fr_1': u' - alsace',
|
||||||
|
u'fr_10': u' - haute-normandie',
|
||||||
|
u'fr_11': u' - ile-de-france',
|
||||||
|
u'fr_12': u' - languedoc-roussillon',
|
||||||
|
u'fr_13': u' - limousin',
|
||||||
|
u'fr_14': u' - lorraine',
|
||||||
|
u'fr_15': u' - midi-pyr\xe9n\xe9es',
|
||||||
|
u'fr_16': u' - nord-pas-de-calais',
|
||||||
|
u'fr_17': u' - paca',
|
||||||
|
u'fr_18': u' - pays de la loire',
|
||||||
|
u'fr_19': u' - picardie',
|
||||||
|
u'fr_2': u' - aquitaine',
|
||||||
|
u'fr_20': u' - poitou-charentes',
|
||||||
|
u'fr_21': u' - rh\xf4ne-alpes',
|
||||||
|
u'fr_22': u' - corse',
|
||||||
|
u'fr_23': u' - dom+tom',
|
||||||
|
u'fr_3': u' - auvergne',
|
||||||
|
u'fr_4': u' - basse-normandie',
|
||||||
|
u'fr_5': u' - bourgogne',
|
||||||
|
u'fr_6': u' - bretagne',
|
||||||
|
u'fr_7': u' - centre',
|
||||||
|
u'fr_8': u' - champagne-ardenne',
|
||||||
|
u'fr_9': u' - franche-comt\xe9'},
|
||||||
|
u'result': {u'search': [{u'alert': u'1',
|
||||||
|
u'birthday': u'1988-04-07',
|
||||||
|
u'city': u'Dammartin',
|
||||||
|
u'country': u'fr',
|
||||||
|
u'cover': u'25',
|
||||||
|
u'id': u'13579115',
|
||||||
|
u'isBan': False,
|
||||||
|
u'isOnline': True,
|
||||||
|
u'last_cnx': u'2011-09-24 15:17:22',
|
||||||
|
u'list5': u'0',
|
||||||
|
u'login': u'@',
|
||||||
|
u'mod_level': u'0',
|
||||||
|
u'path': u'5/1/1/9/7/5/3/',
|
||||||
|
u'pseudo': u"S\xe9 's\xe9",
|
||||||
|
u'region': u'11',
|
||||||
|
u'sex': 1,
|
||||||
|
u'shard': 5,
|
||||||
|
u'style': u'1',
|
||||||
|
u'table': u'adopteun.girls',
|
||||||
|
u'url': u'/api.php?member/view/13579115/Ss',
|
||||||
|
u'zip': u'77230'}],
|
||||||
|
u'token': u'f92aede46118dcdba6d484146b4627777fbe7188'}}
|
||||||
|
|
||||||
24
modules/aum/__init__.py
Normal file
24
modules/aum/__init__.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from .browser import AuMBrowser
|
||||||
|
from .backend import AuMBackend
|
||||||
|
|
||||||
|
__all__ = ['AuMBrowser', 'AuMBackend']
|
||||||
135
modules/aum/antispam.py
Normal file
135
modules/aum/antispam.py
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['AntiSpam']
|
||||||
|
|
||||||
|
|
||||||
|
class AntiSpam(object):
|
||||||
|
def check_thread(self, thread):
|
||||||
|
resume = thread['title']
|
||||||
|
|
||||||
|
# Check if there is an email address in the offer.
|
||||||
|
if re.match('^[\w\d\.\-_]+@[\w\d\.]+ vous offre la pos', resume):
|
||||||
|
return False
|
||||||
|
if thread['member']['pseudo'] == 'Ekaterina':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_profile(self, profile):
|
||||||
|
# The name of profile is in form #123456789
|
||||||
|
if profile['pseudo'] == '':
|
||||||
|
return False
|
||||||
|
if profile['about1'].startswith('salut! je te donne mon msn'):
|
||||||
|
return False
|
||||||
|
if profile['about2'].startswith('cam to cam'):
|
||||||
|
return False
|
||||||
|
if profile['about2'].startswith('je suis une femme tres tres belle et je recherche un homme qui aime le sexe'):
|
||||||
|
return False
|
||||||
|
if profile['about2'].endswith('mmmmmmmmmmmmmmmm'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ipaddr is not available anymore.
|
||||||
|
for ipaddr in (profile['last_ip'], profile['first_ip']):
|
||||||
|
if ipaddr.startswith('41.202.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('41.250.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('41.251.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('41.141.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('194.177.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('41.85.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('41.86.'):
|
||||||
|
return False
|
||||||
|
if ipaddr.startswith('196.47.'):
|
||||||
|
return False
|
||||||
|
if re.match('105\.13\d.*', ipaddr):
|
||||||
|
return False
|
||||||
|
if ipaddr in ('62.157.186.18', '198.36.222.8', '212.234.67.61', '203.193.158.210', '41.189.34.180', '41.66.12.36', '196.47.137.21', '213.136.125.122', '41.191.87.188'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_contact(self, contact):
|
||||||
|
if contact.id == 1:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not self.check_profile(contact.aum_profile):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ipaddr is not available anymore.
|
||||||
|
first_ip = contact.profile['info']['IPaddr'].value.split(' ')[0]
|
||||||
|
last_ip = contact.profile['info']['IPaddr'].value.rstrip(')')
|
||||||
|
for ipaddr in (first_ip, last_ip):
|
||||||
|
if ipaddr.endswith('.afnet.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.iam.net.ma'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.amsterdam.ananoos.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.tedata.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('kupo.fr'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.static.virginmedia.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('frozenway.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.rev.bgtn.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('real-vpn.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.nl.ipodah.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.wanamaroc.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.ukservers.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.startdedicated.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.clients.your-server.de'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.cba.embratel.net.br'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.idstelcom.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('proxy.chg-support.com'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.sprintsvc.net'):
|
||||||
|
return False
|
||||||
|
if ipaddr.endswith('.relakks.com'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_mail(self, mail):
|
||||||
|
# Spambot with a long first-message.
|
||||||
|
if mail['message'].find('Je veux que vous m\'ayez ecrit directement sur le mon e-mail') >= 0:
|
||||||
|
return False
|
||||||
|
if mail['message'].find('ilusa12010@live.fr') >= 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
555
modules/aum/backend.py
Normal file
555
modules/aum/backend.py
Normal file
|
|
@ -0,0 +1,555 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
import email
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
from html2text import unescape
|
||||||
|
from dateutil import tz
|
||||||
|
from dateutil.parser import parse as _parse_dt
|
||||||
|
|
||||||
|
from weboob.capabilities.base import NotLoaded
|
||||||
|
from weboob.capabilities.chat import ICapChat
|
||||||
|
from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message, Thread
|
||||||
|
from weboob.capabilities.dating import ICapDating, OptimizationNotFound, Event
|
||||||
|
from weboob.capabilities.contact import ICapContact, ContactPhoto, Query, QueryError
|
||||||
|
from weboob.capabilities.account import ICapAccount, StatusField
|
||||||
|
from weboob.tools.backend import BaseBackend, BackendConfig
|
||||||
|
from weboob.tools.browser import BrowserUnavailable
|
||||||
|
from weboob.tools.value import Value, ValuesDict, ValueBool, ValueBackendPassword
|
||||||
|
from weboob.tools.log import getLogger
|
||||||
|
from weboob.tools.misc import local2utc
|
||||||
|
|
||||||
|
from .contact import Contact
|
||||||
|
from .captcha import CaptchaError
|
||||||
|
from .antispam import AntiSpam
|
||||||
|
from .browser import AuMBrowser
|
||||||
|
from .optim.profiles_walker import ProfilesWalker
|
||||||
|
from .optim.visibility import Visibility
|
||||||
|
from .optim.queries_queue import QueriesQueue
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['AuMBackend']
|
||||||
|
|
||||||
|
|
||||||
|
def parse_dt(s):
|
||||||
|
d = _parse_dt(s)
|
||||||
|
return local2utc(d)
|
||||||
|
|
||||||
|
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount):
|
||||||
|
NAME = 'aum'
|
||||||
|
MAINTAINER = 'Romain Bignon'
|
||||||
|
EMAIL = 'romain@weboob.org'
|
||||||
|
VERSION = '0.a'
|
||||||
|
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))
|
||||||
|
STORAGE = {'profiles_walker': {'viewed': []},
|
||||||
|
'queries_queue': {'queue': []},
|
||||||
|
'sluts': {},
|
||||||
|
'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())
|
||||||
|
|
||||||
|
def report_spam(self, id):
|
||||||
|
with self.browser:
|
||||||
|
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['baskets'] = (self.browser.get_baskets, 'You were put into %s\'s basket')
|
||||||
|
all_events['flashs'] = (self.browser.get_flashs, 'You sent a charm to %s')
|
||||||
|
all_events['visits'] = (self.browser.get_visits, 'Visited by %s')
|
||||||
|
for type, (events, message) in all_events.iteritems():
|
||||||
|
for event in events():
|
||||||
|
try:
|
||||||
|
e = Event(event['%sid' % type[0]])
|
||||||
|
except KeyError:
|
||||||
|
e = Event(event['id'])
|
||||||
|
|
||||||
|
e.date = parse_dt(event['date'])
|
||||||
|
e.type = type
|
||||||
|
if 'member' in event:
|
||||||
|
e.contact = self._get_partial_contact(event['member'])
|
||||||
|
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['member']['id'])
|
||||||
|
continue
|
||||||
|
t = Thread(int(thread['member']['id']))
|
||||||
|
t.flags = Thread.IS_DISCUSSION
|
||||||
|
t.title = 'Discussion with %s' % thread['member']['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
|
||||||
|
slut = self._get_slut(id)
|
||||||
|
if contacts is None:
|
||||||
|
contacts = {}
|
||||||
|
|
||||||
|
if not thread.title:
|
||||||
|
thread.title = u'Discussion with %s' % mails['member']['pseudo']
|
||||||
|
|
||||||
|
self.storage.set('sluts', thread.id, 'status', mails['status'])
|
||||||
|
self.storage.save()
|
||||||
|
|
||||||
|
for mail in mails['messages']:
|
||||||
|
flags = 0
|
||||||
|
if self.antispam and not self.antispam.check_mail(mail):
|
||||||
|
self.logger.info('Skipped a spam-mail from %s' % mails['member']['pseudo'])
|
||||||
|
self.report_spam(thread.id)
|
||||||
|
break
|
||||||
|
|
||||||
|
if parse_dt(mail['date']) > slut['lastmsg']:
|
||||||
|
flags |= Message.IS_UNREAD
|
||||||
|
|
||||||
|
if get_profiles:
|
||||||
|
if not mail['id_from'] in contacts:
|
||||||
|
with self.browser:
|
||||||
|
contacts[mail['id_from']] = self.get_contact(mail['id_from'])
|
||||||
|
if self.antispam and not self.antispam.check_contact(contacts[mail['id_from']]):
|
||||||
|
self.logger.info('Skipped a spam-mail-profile from %s' % mails['member']['pseudo'])
|
||||||
|
self.report_spam(thread.id)
|
||||||
|
break
|
||||||
|
|
||||||
|
if int(mail['id_from']) == self.browser.my_id:
|
||||||
|
if int(mails['remoteStatus']) == 0 and msg is None:
|
||||||
|
flags |= Message.IS_NOT_ACCUSED
|
||||||
|
else:
|
||||||
|
flags |= Message.IS_ACCUSED
|
||||||
|
|
||||||
|
signature = u''
|
||||||
|
if mail.get('src', None):
|
||||||
|
signature += u'Sent from my %s\n\n' % mail['src']
|
||||||
|
if mail['id_from'] in contacts:
|
||||||
|
signature += contacts[mail['id_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=my_name if int(mail['id_from']) == self.browser.my_id else mails['member']['pseudo'],
|
||||||
|
receivers=[my_name if int(mail['id_from']) != self.browser.my_id else mails['member']['pseudo']],
|
||||||
|
date=parse_dt(mail['date']),
|
||||||
|
content=unescape(mail['message']).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, thread=None):
|
||||||
|
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['member']['pseudo'])
|
||||||
|
self.report_spam(thread['member']['id'])
|
||||||
|
continue
|
||||||
|
slut = self._get_slut(thread['member']['id'])
|
||||||
|
if parse_dt(thread['date']) > slut['lastmsg'] or int(thread['status']) != int(slut['status']):
|
||||||
|
t = self.get_thread(thread['member']['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 slut adds me in her basket
|
||||||
|
# between the aum.nb_new_baskets() and aum.get_baskets().
|
||||||
|
with self.browser:
|
||||||
|
slut = 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 basket['isBan'] or parse_dt(basket['date']) <= slut['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
|
||||||
|
|
||||||
|
def set_message_read(self, message):
|
||||||
|
if message.id == self.MAGIC_ID_BASKET:
|
||||||
|
# Save the last baskets checks.
|
||||||
|
slut = self._get_slut(-self.MAGIC_ID_BASKET)
|
||||||
|
if slut['lastmsg'] < message.date:
|
||||||
|
slut['lastmsg'] = message.date
|
||||||
|
self.storage.set('sluts', -self.MAGIC_ID_BASKET, slut)
|
||||||
|
self.storage.save()
|
||||||
|
return
|
||||||
|
|
||||||
|
slut = self._get_slut(message.thread.id)
|
||||||
|
if slut['lastmsg'] < message.date:
|
||||||
|
slut['lastmsg'] = message.date
|
||||||
|
self.storage.set('sluts', message.thread.id, slut)
|
||||||
|
self.storage.save()
|
||||||
|
|
||||||
|
def _get_slut(self, id):
|
||||||
|
id = int(id)
|
||||||
|
sluts = self.storage.get('sluts')
|
||||||
|
if not sluts or not id in sluts:
|
||||||
|
slut = {'lastmsg': datetime.datetime(1970,1,1),
|
||||||
|
'status': 0}
|
||||||
|
else:
|
||||||
|
slut = self.storage.get('sluts', id)
|
||||||
|
|
||||||
|
slut['lastmsg'] = slut.get('lastmsg', datetime.datetime(1970,1,1)).replace(tzinfo=tz.tzutc())
|
||||||
|
slut['status'] = int(slut.get('status', 0))
|
||||||
|
return slut
|
||||||
|
|
||||||
|
# ---- 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_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):
|
||||||
|
if contact.get('isBan', contact.get('dead', False)):
|
||||||
|
with self.browser:
|
||||||
|
self.browser.delete_thread(int(contact['id']))
|
||||||
|
return None
|
||||||
|
|
||||||
|
s = 0
|
||||||
|
if contact.get('isOnline', False):
|
||||||
|
s = Contact.STATUS_ONLINE
|
||||||
|
else:
|
||||||
|
s = Contact.STATUS_OFFLINE
|
||||||
|
|
||||||
|
c = Contact(contact['id'], contact['pseudo'], s)
|
||||||
|
c.url = self.browser.id2url(contact['id'])
|
||||||
|
if 'birthday' in contact:
|
||||||
|
birthday = _parse_dt(contact['birthday'])
|
||||||
|
age = int((datetime.datetime.now() - birthday).days / 365.25)
|
||||||
|
c.status_msg = u'%s old, %s' % (age, contact['city'])
|
||||||
|
if contact['cover'].isdigit() and int(contact['cover']) > 0:
|
||||||
|
url = 'http://s%s.adopteunmec.com/%s%%(type)s%s.jpg' % (contact['shard'], contact['path'], contact['cover'])
|
||||||
|
else:
|
||||||
|
url = 'http://s.adopteunmec.com/www/img/thumb0.gif'
|
||||||
|
|
||||||
|
c.set_photo('image%s' % contact['cover'],
|
||||||
|
url=url % {'type': 'image'},
|
||||||
|
thumbnail_url=url % {'type': 'thumb0_'})
|
||||||
|
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['member'])
|
||||||
|
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('godchilds', 'Number of godchilds', self.browser.nb_godchilds()),
|
||||||
|
)
|
||||||
|
|
||||||
|
OBJECTS = {Thread: fill_thread,
|
||||||
|
Contact: fill_contact,
|
||||||
|
ContactPhoto: fill_photo
|
||||||
|
}
|
||||||
419
modules/aum/browser.py
Normal file
419
modules/aum/browser.py
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2008-2011 Romain Bignon, Christophe Benz
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
import random
|
||||||
|
import urllib
|
||||||
|
from htmlentitydefs import codepoint2name
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword, BrowserUnavailable, BrowserHTTPNotFound
|
||||||
|
|
||||||
|
from weboob.capabilities.chat import ChatException, ChatMessage
|
||||||
|
from weboob.capabilities.messages import CantSendMessage
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['AuMBrowser']
|
||||||
|
|
||||||
|
|
||||||
|
class AuMException(Exception):
|
||||||
|
ERRORS = {"0.0.0": "Bad signature",
|
||||||
|
"0.0.1": "Malformed request",
|
||||||
|
"0.0.2": "Not logged",
|
||||||
|
"1.1.1": "No member has this login",
|
||||||
|
"1.1.2": "Password don't match",
|
||||||
|
"1.1.3": "User has been banned",
|
||||||
|
"1.12.1": "Invalid country",
|
||||||
|
"1.12.1": "Invalid region",
|
||||||
|
"4.0.1": "Member not found",
|
||||||
|
"4.1.1": "Thread doesn't exist",
|
||||||
|
"4.1.2": "Cannot write to this member",
|
||||||
|
"5.1.1": "Member tergeted doesn't exist",
|
||||||
|
"5.1.2": "Sex member targeted is not the opposite of the member logged",
|
||||||
|
"5.1.3": "Not possible to send a charm",
|
||||||
|
"5.1.4": "Not possible to send a charm because the 5 charms has been already used",
|
||||||
|
"5.1.5": "Not possible because the guy has already send a charm to this girl",
|
||||||
|
"5.1.6": "No more money",
|
||||||
|
"5.1.7": "Not possible to add to basket",
|
||||||
|
"5.2.1": "Member doesn't exist",
|
||||||
|
"5.3.1": "Member doesn't exist",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, code):
|
||||||
|
Exception.__init__(self, self.ERRORS.get(code, code))
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
class AuMBrowser(BaseBrowser):
|
||||||
|
DOMAIN = 'api.adopteunmec.com'
|
||||||
|
APIKEY = 'fb0123456789abcd'
|
||||||
|
|
||||||
|
consts = None
|
||||||
|
search_query = None
|
||||||
|
my_sex = 0
|
||||||
|
my_id = 0
|
||||||
|
my_name = u''
|
||||||
|
my_coords = (0,0)
|
||||||
|
|
||||||
|
def id2url(self, id):
|
||||||
|
return 'http://www.adopteunmec.com/index.php/profile/%s' % id
|
||||||
|
|
||||||
|
def url2id(func):
|
||||||
|
def inner(self, id, *args, **kwargs):
|
||||||
|
m = re.match('^http://.*adopteunmec.com.*/(\d+)$', str(id))
|
||||||
|
if m:
|
||||||
|
id = int(m.group(1))
|
||||||
|
else:
|
||||||
|
m = re.match('^http://.*adopteunmec.com/index.php/profile/(\d+).*', str(id))
|
||||||
|
if m:
|
||||||
|
id = int(m.group(1))
|
||||||
|
return func(self, id, *args, **kwargs)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
def api_request(self, command, action, parameter='', data=None, nologin=False):
|
||||||
|
if data is None:
|
||||||
|
# Always do POST requests.
|
||||||
|
data = ''
|
||||||
|
elif isinstance(data, (list,tuple,dict)):
|
||||||
|
data = urllib.urlencode(data)
|
||||||
|
elif isinstance(data, unicode):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
url = self.buildurl(self.absurl('/api.php'), S=self.APIKEY,
|
||||||
|
C=command,
|
||||||
|
A=action,
|
||||||
|
P=parameter,
|
||||||
|
O='json')
|
||||||
|
buf = self.openurl(url, data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = json.load(buf)
|
||||||
|
except ValueError:
|
||||||
|
buf.seek(0)
|
||||||
|
raise ValueError(buf.read())
|
||||||
|
|
||||||
|
if 'errors' in r and r['errors'] != '0' and len(r['errors']) > 0:
|
||||||
|
code = r['errors'][0]
|
||||||
|
if code in (u'0.0.2', u'1.1.1', u'1.1.2'):
|
||||||
|
if not nologin:
|
||||||
|
self.login()
|
||||||
|
return self.api_request(command, action, parameter, data, nologin=True)
|
||||||
|
else:
|
||||||
|
raise BrowserIncorrectPassword(AuMException.ERRORS[code])
|
||||||
|
else:
|
||||||
|
raise AuMException(code)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
r = self.api_request('me', 'login', data={'login': self.username,
|
||||||
|
'pass': self.password,
|
||||||
|
}, nologin=True)
|
||||||
|
self.my_sex = r['result']['me']['sex']
|
||||||
|
self.my_id = int(r['result']['me']['id'])
|
||||||
|
self.my_name = r['result']['me']['pseudo']
|
||||||
|
self.my_coords = (float(r['result']['me']['lat']), float(r['result']['me']['lng']))
|
||||||
|
return r
|
||||||
|
|
||||||
|
#def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country, godfather=None):
|
||||||
|
# if not self.is_on_page(RegisterPage):
|
||||||
|
# self.location('http://www.adopteunmec.com/register2.php')
|
||||||
|
# self.page.register(password, sex, birthday_d, birthday_m, birthday_y, zipcode, country)
|
||||||
|
# if godfather:
|
||||||
|
# if not self.is_on_page(AccountPage):
|
||||||
|
# self.location('http://www.adopteunmec.com/account.php')
|
||||||
|
# self.page.set_godfather(godfather)
|
||||||
|
|
||||||
|
#@pageaccess
|
||||||
|
#def add_photo(self, name, f):
|
||||||
|
# if not self.is_on_page(EditPhotoPage):
|
||||||
|
# self.location('/edit.php?type=1')
|
||||||
|
# return self.page.add_photo(name, f)
|
||||||
|
|
||||||
|
#@pageaccess
|
||||||
|
#def set_nickname(self, nickname):
|
||||||
|
# if not self.is_on_page(EditAnnouncePage):
|
||||||
|
# self.location('/edit.php?type=2')
|
||||||
|
# return self.page.set_nickname(nickname)
|
||||||
|
|
||||||
|
#@pageaccess
|
||||||
|
#def set_announce(self, title=None, description=None, lookingfor=None):
|
||||||
|
# if not self.is_on_page(EditAnnouncePage):
|
||||||
|
# self.location('/edit.php?type=2')
|
||||||
|
# return self.page.set_announce(title, description, lookingfor)
|
||||||
|
|
||||||
|
#@pageaccess
|
||||||
|
#def set_description(self, **args):
|
||||||
|
# if not self.is_on_page(EditDescriptionPage):
|
||||||
|
# self.location('/edit.php?type=3')
|
||||||
|
# return self.page.set_description(**args)
|
||||||
|
|
||||||
|
def check_login(func):
|
||||||
|
def inner(self, *args, **kwargs):
|
||||||
|
if self.my_id == 0:
|
||||||
|
self.login()
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
def get_consts(self):
|
||||||
|
if self.consts is not None:
|
||||||
|
return self.consts
|
||||||
|
|
||||||
|
self.consts = []
|
||||||
|
for i in xrange(2):
|
||||||
|
r = self.api_request('me', 'all_values', data={'sex': i})
|
||||||
|
self.consts.append(r['result']['values'])
|
||||||
|
|
||||||
|
return self.consts
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def score(self):
|
||||||
|
r = self.api_request('member', 'view', data={'id': self.my_id})
|
||||||
|
return int(r['result']['member']['popu']['popu'])
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_my_name(self):
|
||||||
|
return self.my_name
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_my_id(self):
|
||||||
|
return self.my_id
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def nb_new_mails(self):
|
||||||
|
r = self.api_request('me', '[default]')
|
||||||
|
return r['result']['news']['newMails']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def nb_new_baskets(self):
|
||||||
|
r = self.api_request('me', '[default]')
|
||||||
|
return r['result']['news']['newBaskets']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def nb_new_visites(self):
|
||||||
|
r = self.api_request('me', '[default]')
|
||||||
|
return r['result']['news']['newVisits']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def nb_available_charms(self):
|
||||||
|
r = self.login()
|
||||||
|
return r['result']['flashs']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def nb_godchilds(self):
|
||||||
|
r = self.api_request('member', 'view', data={'id': self.my_id})
|
||||||
|
return int(r['result']['member']['popu']['invits'])
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_baskets(self):
|
||||||
|
r = self.api_request('me', 'basket')
|
||||||
|
return r['result']['basket']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_flashs(self):
|
||||||
|
r = self.api_request('me', 'flashs')
|
||||||
|
return r['result']['all']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_visits(self):
|
||||||
|
r = self.api_request('me', 'visits')
|
||||||
|
return r['result']['news'] + r['result']['olds']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
def get_threads_list(self, count=30):
|
||||||
|
r = self.api_request('message', '[default]', '%d,0' % count)
|
||||||
|
return r['result']['threads']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
@url2id
|
||||||
|
def get_thread_mails(self, id, count=30):
|
||||||
|
r = self.api_request('message', 'thread', data={'memberId': id, 'count': count})
|
||||||
|
return r['result']['thread']
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
@url2id
|
||||||
|
def post_mail(self, id, content):
|
||||||
|
new_content = u''
|
||||||
|
for c in content:
|
||||||
|
try:
|
||||||
|
new_content += '&%s;' % codepoint2name[ord(c)]
|
||||||
|
except KeyError:
|
||||||
|
new_content += c
|
||||||
|
|
||||||
|
content = new_content.replace('\n', '\r\n').encode('Windows-1252', 'replace')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.api_request('message', 'new', data={'memberId': id, 'message': content})
|
||||||
|
except AuMException, e:
|
||||||
|
raise CantSendMessage(unicode(e))
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
@url2id
|
||||||
|
def delete_thread(self, id):
|
||||||
|
r = self.api_request('message', 'delete', data={'id_user': id})
|
||||||
|
self.logger.debug('Thread deleted: %r' % r)
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
@url2id
|
||||||
|
def send_charm(self, id):
|
||||||
|
try:
|
||||||
|
self.api_request('member', 'addBasket', data={'id': id})
|
||||||
|
except AuMException:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@check_login
|
||||||
|
@url2id
|
||||||
|
def add_basket(self, id):
|
||||||
|
try:
|
||||||
|
self.api_request('member', 'addBasket', data={'id': id})
|
||||||
|
except AuMException:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@url2id
|
||||||
|
def deblock(self, id):
|
||||||
|
self.readurl('http://www.adopteunmec.com/fajax_postMessage.php?action=deblock&to=%s' % id)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@url2id
|
||||||
|
def report_fake(self, id):
|
||||||
|
return self.readurl('http://www.adopteunmec.com/fake.php', 'id=%s' % id)
|
||||||
|
|
||||||
|
@url2id
|
||||||
|
def rate(self, id, what, rating):
|
||||||
|
result = self.openurl('http://www.adopteunmec.com/fajax_vote.php', 'member=%s&what=%s&rating=%s' % (id, what, rating)).read()
|
||||||
|
return float(result)
|
||||||
|
|
||||||
|
def search_profiles(self, **kwargs):
|
||||||
|
if self.search_query is None:
|
||||||
|
r = self.api_request('searchs', '[default]')
|
||||||
|
self.search_query = r['result']['search']['query']
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
for key, value in json.loads(self.search_query).iteritems():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for k, v in value.iteritems():
|
||||||
|
params['%s%s' % (key, k.capitalize())] = v
|
||||||
|
else:
|
||||||
|
params[key] = value or ''
|
||||||
|
r = self.api_request('searchs', 'advanced', '30,0', params)
|
||||||
|
ids = [s['id'] for s in r['result']['search']]
|
||||||
|
return set(ids)
|
||||||
|
|
||||||
|
@url2id
|
||||||
|
def get_profile(self, id, with_pics=True):
|
||||||
|
r = self.api_request('member', 'view', data={'id': id})
|
||||||
|
if not 'result' in r:
|
||||||
|
print r
|
||||||
|
profile = r['result']['member']
|
||||||
|
|
||||||
|
|
||||||
|
# Calculate distance in km.
|
||||||
|
profile['dist'] = 0.0
|
||||||
|
if 'lat' in profile and 'lng' in profile:
|
||||||
|
coords = (float(profile['lat']), float(profile['lng']))
|
||||||
|
|
||||||
|
R = 6371
|
||||||
|
lat1 = math.radians(self.my_coords[0])
|
||||||
|
lat2 = math.radians(coords[0])
|
||||||
|
lon1 = math.radians(self.my_coords[1])
|
||||||
|
lon2 = math.radians(coords[1])
|
||||||
|
dLat = lat2 - lat1
|
||||||
|
dLong = lon2 - lon1
|
||||||
|
a= pow(math.sin(dLat/2), 2) + math.cos(lat1) * math.cos(lat2) * pow(math.sin(dLong/2), 2)
|
||||||
|
c= 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||||
|
profile['dist'] = R * c
|
||||||
|
|
||||||
|
if with_pics:
|
||||||
|
r = self.api_request('member', 'pictures', data={'id': id})
|
||||||
|
profile['pictures'] = []
|
||||||
|
for pic in r['result']['pictures']:
|
||||||
|
d = {'hidden': False}
|
||||||
|
d.update(pic)
|
||||||
|
profile['pictures'].append(d)
|
||||||
|
|
||||||
|
base_url = 'http://s%s.adopteunmec.com/%s' % (profile['shard'], profile['path'])
|
||||||
|
if len(profile['pictures']) > 0:
|
||||||
|
pic_regex = re.compile('(?P<base_url>http://.+\.adopteunmec\.com/.+/)image(?P<id>.+)\.jpg')
|
||||||
|
pic_max_id = max((int((lambda m: m and m.groupdict()['id'] or 0)(pic_regex.match(pic['url'])))) for pic in profile['pictures'])
|
||||||
|
for id in xrange(1, pic_max_id + 1):
|
||||||
|
url = u'%simage%s.jpg' % (base_url, id)
|
||||||
|
if not url in [pic['url'] for pic in profile['pictures']]:
|
||||||
|
profile['pictures'].append({'url': url, u'hidden': True, 'id': u'0', 'rating': 0.0})
|
||||||
|
else:
|
||||||
|
url = '%simage1.jpg' % base_url
|
||||||
|
try:
|
||||||
|
self.openurl(url)
|
||||||
|
except BrowserHTTPNotFound:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
profile['pictures'].append({'url': url, u'hidden': True, 'id': u'0', 'rating': 0.0})
|
||||||
|
|
||||||
|
return profile
|
||||||
|
|
||||||
|
def _get_chat_infos(self):
|
||||||
|
try:
|
||||||
|
data = json.load(self.openurl('http://www.adopteunmec.com/1.1_cht_get.php?anticache=%f' % random.random()))
|
||||||
|
except ValueError:
|
||||||
|
raise BrowserUnavailable()
|
||||||
|
|
||||||
|
if data['error']:
|
||||||
|
raise ChatException(u'Error while getting chat infos. json:\n%s' % data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def iter_contacts(self):
|
||||||
|
def iter_dedupe(contacts):
|
||||||
|
yielded_ids = set()
|
||||||
|
for contact in contacts:
|
||||||
|
if contact['id'] not in yielded_ids:
|
||||||
|
yield contact
|
||||||
|
yielded_ids.add(contact['id'])
|
||||||
|
|
||||||
|
data = self._get_chat_infos()
|
||||||
|
return iter_dedupe(data['contacts'])
|
||||||
|
|
||||||
|
def iter_chat_messages(self, _id=None):
|
||||||
|
data = self._get_chat_infos()
|
||||||
|
if data['messages'] is not None:
|
||||||
|
for message in data['messages']:
|
||||||
|
yield ChatMessage(id_from=message['id_from'], id_to=message['id_to'], message=message['message'], date=message['date'])
|
||||||
|
|
||||||
|
def send_chat_message(self, _id, message):
|
||||||
|
url = 'http://www.adopteunmec.com/1.1_cht_send.php?anticache=%f' % random.random()
|
||||||
|
data = dict(id=_id, message=message)
|
||||||
|
headers = {
|
||||||
|
'Content-type': 'application/x-www-form-urlencoded',
|
||||||
|
'Accept': 'text/plain',
|
||||||
|
'Referer': 'http://www.adopteunmec.com/chat.php',
|
||||||
|
'Origin': 'http://www.adopteunmec.com',
|
||||||
|
}
|
||||||
|
request = self.request_class(url, urllib.urlencode(data), headers)
|
||||||
|
response = self.openurl(request).read()
|
||||||
|
try:
|
||||||
|
datetime.datetime.strptime(response, '%Y-%m-%d %H:%M:%S')
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
204
modules/aum/captcha.py
Normal file
204
modules/aum/captcha.py
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
import Image
|
||||||
|
|
||||||
|
class CaptchaError(Exception): pass
|
||||||
|
|
||||||
|
class Tile:
|
||||||
|
hash = {
|
||||||
|
'bc8d52d96058478a6def26226145d53b': 'A',
|
||||||
|
'c62ecdfddb72b2feaed96cd9fe7c2802': 'A',
|
||||||
|
'8b61cda8a3240d8fa5f2610424271300': 'AD',
|
||||||
|
'f5dc63d37c7ea3375d86180f0ae62d05': 'AE',
|
||||||
|
'fd562be230da7f767f4454148632201d': 'AF',
|
||||||
|
'1860de576d8b0d1d87edc9dcb0b2a64c': 'AG',
|
||||||
|
'53afa108d36186e6bd23631711ec3d8c': 'AJ',
|
||||||
|
'6f2f9a1082a9230272c45117320f159d': 'AL',
|
||||||
|
'e14249a774d24bacc6e2bcadd7f3df65': 'AM',
|
||||||
|
'389330dbf3d85dea2dc40c6f9cf77d52': 'AN',
|
||||||
|
'17526a3c2261b55f9cd237c4aa195099': 'AQ',
|
||||||
|
'7e4820a9cc6c83a9fa60ff73ceb52157': 'AW',
|
||||||
|
'90690d1209753a2bcfeafa890082a585': 'B',
|
||||||
|
'2cf22e9ceace03a5f8ed3999e92d877e': 'C',
|
||||||
|
'a1d0bf1a29600a82a6aa2b8b21651b0f': 'D',
|
||||||
|
'9bb6909d647a0be3b2e7352d37374228': 'E',
|
||||||
|
'38120c8346f16cd07a9194283787ee5e': 'F',
|
||||||
|
'd41ff948fbc50a628c858b8e3e9e931c': 'G',
|
||||||
|
'4cc9322d3361eb3f9fea7fc83579e40f': 'H',
|
||||||
|
'837cd0f04e2d47ca6975745bdd0da640': 'I',
|
||||||
|
'da0204fa51b38414051376cc1c27ba72': 'J',
|
||||||
|
'199b1a9f9e1df1c2eddadcc4582957d7': 'JW',
|
||||||
|
'5e8d3d5bd5f683d84b089f2cecc1e196': 'JX',
|
||||||
|
'bc1fcf3546057d40d2db5454caacb3a5': 'JZ',
|
||||||
|
'c2f5866ba3bf799ece8b202492d199bf': 'K',
|
||||||
|
'7abe4091e11921afe6dac16509999010': 'KT',
|
||||||
|
'281ef08e623184e5621a73b9ccec7c9a': 'KX',
|
||||||
|
'b28e3fc06411de2ac7f53569bc3b42db': 'L',
|
||||||
|
'd58a6c26649926f1145fb4b7b42d0554': 'LT',
|
||||||
|
'4add630c6d124899fef814211975e344': 'M',
|
||||||
|
'9740cefe1629d6bc149a72d5f2a4586d': 'N',
|
||||||
|
'396f816f7e78e5c98de6404f8c4bd2ee': 'O',
|
||||||
|
'31ae7c9536b6c6a96e30a77b70e4b2fd': 'P',
|
||||||
|
'98ad9b1c32c05e6efc06637a166e4c42': 'PA',
|
||||||
|
'a05cce33683025fb2c6708ee06f6028e': 'Q',
|
||||||
|
'2852f51e8939bf9664fe064f7dacf310': 'R',
|
||||||
|
'3798513fe87e786faa67552a140fd86f': 'S',
|
||||||
|
'350b13811e34eeb63e3d7fb4b5eade5b': 'T',
|
||||||
|
'a01b186cbc767e17d948ed04eff114a1': 'U',
|
||||||
|
'8405f4d80ce80c4e6e9680fcfac4fe40': 'V',
|
||||||
|
'17ed80e9cb9a585098ae6a55d8d1f5c0': 'W',
|
||||||
|
'ae54ca77be5561330781a08dfbaff7a7': 'W',
|
||||||
|
'bbded6a2ba5f521bba276bb843bf4c98': 'WXT',
|
||||||
|
'ea662dd25fc528b84b832ce71ae3de61': 'WZ',
|
||||||
|
'4eb23916138e7c01714431dbecfe8b96': 'X',
|
||||||
|
'c02093d35d852339ff34f2b26873bf5a': 'XW',
|
||||||
|
'65744e0c6ce0c56d04873dfd732533a7': 'Y',
|
||||||
|
'315fb7dba7032004bd362cf0bb076733': 'YA',
|
||||||
|
'ce12a68a4f15657bc5297a6cf698bc0a': 'YAQ',
|
||||||
|
'275478ea2280351f7433a0606f962175': 'Z',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.map = []
|
||||||
|
|
||||||
|
def append(self, pxls):
|
||||||
|
self.map.append(pxls)
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
print '-' * (len(self.map) * 2 + 2)
|
||||||
|
for y in xrange(len(self.map[0])):
|
||||||
|
sys.stdout.write('|')
|
||||||
|
for x in xrange(len(self.map)):
|
||||||
|
sys.stdout.write('%s' % ('XX' if self.map[x][y] else ' '))
|
||||||
|
print '|'
|
||||||
|
print '-' * (len(self.map) * 2 + 2)
|
||||||
|
|
||||||
|
def checksum(self):
|
||||||
|
s = ''
|
||||||
|
for pxls in self.map:
|
||||||
|
for pxl in pxls:
|
||||||
|
s += '%d' % (1 if pxl else 0)
|
||||||
|
return hashlib.md5(s).hexdigest()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def letter(self):
|
||||||
|
checksum = self.checksum()
|
||||||
|
try:
|
||||||
|
return self.hash[checksum]
|
||||||
|
except KeyError:
|
||||||
|
print 'Unable te resolve:'
|
||||||
|
self.display()
|
||||||
|
print 'hash: %s' % checksum
|
||||||
|
raise CaptchaError()
|
||||||
|
|
||||||
|
class Captcha:
|
||||||
|
def __init__(self, f):
|
||||||
|
self.img = Image.open(f)
|
||||||
|
self.w, self.h = self.img.size
|
||||||
|
self.map = self.img.load()
|
||||||
|
|
||||||
|
self.tiles = []
|
||||||
|
|
||||||
|
tile = None
|
||||||
|
for x in xrange(self.w):
|
||||||
|
blank = True
|
||||||
|
pxls = []
|
||||||
|
for y in xrange(self.h):
|
||||||
|
pxls.append(self[x,y])
|
||||||
|
if self[x,y] != 0:
|
||||||
|
blank = False
|
||||||
|
|
||||||
|
if tile:
|
||||||
|
if blank:
|
||||||
|
tile = None
|
||||||
|
else:
|
||||||
|
tile.append(pxls)
|
||||||
|
elif not blank:
|
||||||
|
tile = Tile()
|
||||||
|
tile.append(pxls)
|
||||||
|
self.tiles.append(tile)
|
||||||
|
|
||||||
|
def __getitem__(self, (x, y)):
|
||||||
|
return self.map[x % self.w, y % self.h]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for tile in self.tiles:
|
||||||
|
yield tile
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
s = ''
|
||||||
|
for tile in self.tiles:
|
||||||
|
s += tile.letter
|
||||||
|
return s
|
||||||
|
|
||||||
|
class Decoder:
|
||||||
|
def __init__(self):
|
||||||
|
self.hash = {}
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
from aum.browser import AuMBrowser
|
||||||
|
browser = AuMBrowser('')
|
||||||
|
browser.openurl('/register2.php')
|
||||||
|
c = Captcha(browser.openurl('/captcha.php'))
|
||||||
|
|
||||||
|
for tile in c:
|
||||||
|
checksum = tile.checksum()
|
||||||
|
|
||||||
|
if checksum in self.hash:
|
||||||
|
print 'Skipping %s' % self.hash[checksum]
|
||||||
|
continue
|
||||||
|
|
||||||
|
tile.display()
|
||||||
|
print 'Checksum: %s' % checksum
|
||||||
|
ntry = 2
|
||||||
|
while ntry:
|
||||||
|
sys.stdout.write('Enter the letter: ')
|
||||||
|
l = sys.stdin.readline().strip()
|
||||||
|
|
||||||
|
ntry -= 1
|
||||||
|
if len(l) != 1:
|
||||||
|
print 'Error: please enter only one letter'
|
||||||
|
elif l in self.hash.itervalues():
|
||||||
|
print 'Warning! This letter has already been catched!'
|
||||||
|
else:
|
||||||
|
ntry = 0
|
||||||
|
|
||||||
|
self.hash[checksum] = l
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
self.process()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print ''
|
||||||
|
print 'hash = {'
|
||||||
|
l = sorted(self.hash.iteritems(), key=lambda (k,v): (v,k))
|
||||||
|
for hash, value in l:
|
||||||
|
print ' \'%s\': %s' % (hash, value)
|
||||||
|
|
||||||
|
print '}'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
d = Decoder()
|
||||||
|
d.main()
|
||||||
281
modules/aum/contact.py
Normal file
281
modules/aum/contact.py
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2008-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import socket
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil.parser import parse as parse_dt
|
||||||
|
|
||||||
|
from weboob.tools.ordereddict import OrderedDict
|
||||||
|
from weboob.capabilities.contact import Contact as _Contact, ProfileNode
|
||||||
|
from weboob.tools.misc import html2text
|
||||||
|
|
||||||
|
class FieldBase:
|
||||||
|
def __init__(self, key, key2=None):
|
||||||
|
self.key = key
|
||||||
|
self.key2 = key2
|
||||||
|
|
||||||
|
def get_value(self, value, consts):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class FieldStr(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
return html2text(unicode(profile[self.key])).strip()
|
||||||
|
|
||||||
|
class FieldBool(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
return bool(int(profile[self.key]))
|
||||||
|
|
||||||
|
class FieldDist(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
return '%.2f km' % float(profile[self.key])
|
||||||
|
|
||||||
|
class FieldIP(FieldBase):
|
||||||
|
def get_hostname(self, s):
|
||||||
|
try:
|
||||||
|
return socket.gethostbyaddr(s)[0]
|
||||||
|
except (socket.gaierror, socket.herror):
|
||||||
|
return s
|
||||||
|
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
s = self.get_hostname(profile[self.key])
|
||||||
|
if profile[self.key] != profile[self.key2]:
|
||||||
|
s += ' (first %s)' % self.get_hostname(profile[self.key2])
|
||||||
|
return s
|
||||||
|
|
||||||
|
class FieldProfileURL(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
id = int(profile[self.key])
|
||||||
|
if id > 0:
|
||||||
|
return 'http://www.adopteunmec.com/index.php/profile/%d' % id
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
class FieldPopu(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
return unicode(profile['popu'][self.key])
|
||||||
|
|
||||||
|
class FieldPopuRatio(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
v1 = float(profile['popu'][self.key])
|
||||||
|
v2 = float(profile['popu'][self.key2])
|
||||||
|
if v2 == 0.0:
|
||||||
|
return 'NaN'
|
||||||
|
else:
|
||||||
|
return '%.2f' % (v1 / v2)
|
||||||
|
|
||||||
|
class FieldOld(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
birthday = parse_dt(profile[self.key])
|
||||||
|
return int((datetime.now() - birthday).days / 365.25)
|
||||||
|
|
||||||
|
class FieldSplit(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
return [html2text(s).strip() for s in profile[self.key].split(self.key2) if len(s.strip()) > 0]
|
||||||
|
|
||||||
|
class FieldBMI(FieldBase):
|
||||||
|
def __init__(self, key, key2, fat=False):
|
||||||
|
FieldBase.__init__(self, key, key2)
|
||||||
|
self.fat = fat
|
||||||
|
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
height = int(profile[self.key])
|
||||||
|
weight = int(profile[self.key2])
|
||||||
|
if height == 0 or weight == 0:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
bmi = (weight/float(pow(height/100.0, 2)))
|
||||||
|
if not self.fat:
|
||||||
|
return bmi
|
||||||
|
elif bmi < 15.5:
|
||||||
|
return 'severely underweight'
|
||||||
|
elif bmi < 18.4:
|
||||||
|
return 'underweight'
|
||||||
|
elif bmi < 24.9:
|
||||||
|
return 'normal'
|
||||||
|
elif bmi < 30:
|
||||||
|
return 'overweight'
|
||||||
|
else:
|
||||||
|
return 'obese'
|
||||||
|
|
||||||
|
class FieldFlags(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
i = int(profile[self.key])
|
||||||
|
labels = []
|
||||||
|
for d in consts[self.key]:
|
||||||
|
if i & (1 << int(d['value'])):
|
||||||
|
labels.append(html2text(d['label']).strip())
|
||||||
|
return labels
|
||||||
|
|
||||||
|
class FieldList(FieldBase):
|
||||||
|
def get_value(self, profile, consts):
|
||||||
|
i = int(profile[self.key])
|
||||||
|
for d in consts[self.key]:
|
||||||
|
if i == int(d['value']):
|
||||||
|
return html2text(d['label']).strip()
|
||||||
|
return ''
|
||||||
|
|
||||||
|
class Contact(_Contact):
|
||||||
|
TABLE = OrderedDict((
|
||||||
|
('_info', OrderedDict((
|
||||||
|
('title', FieldStr('title')),
|
||||||
|
# ipaddr is not available anymore.
|
||||||
|
#('IPaddr', FieldIP('last_ip', 'first_ip')),
|
||||||
|
('admin', FieldBool('admin')),
|
||||||
|
('ban', FieldBool('isBan')),
|
||||||
|
('first', FieldStr('first_cnx')),
|
||||||
|
('godfather', FieldProfileURL('godfather')),
|
||||||
|
))),
|
||||||
|
('_stats', OrderedDict((
|
||||||
|
('mails', FieldPopu('mails')),
|
||||||
|
('baskets', FieldPopu('contacts')),
|
||||||
|
('charms', FieldPopu('flashs')),
|
||||||
|
('visites', FieldPopu('visites')),
|
||||||
|
('invits', FieldPopu('invits')),
|
||||||
|
('bonus', FieldPopu('bonus')),
|
||||||
|
('score', FieldPopu('popu')),
|
||||||
|
('ratio', FieldPopuRatio('mails', 'flashs')),
|
||||||
|
))),
|
||||||
|
('details', OrderedDict((
|
||||||
|
('old', FieldOld('birthday')),
|
||||||
|
('birthday', FieldStr('birthday')),
|
||||||
|
('zipcode', FieldStr('zip')),
|
||||||
|
('location', FieldStr('city')),
|
||||||
|
('distance', FieldDist('dist')),
|
||||||
|
('country', FieldStr('country')),
|
||||||
|
('phone', FieldStr('phone')),
|
||||||
|
('eyes', FieldList('eyes')),
|
||||||
|
('hair_color', FieldList('hair_color')),
|
||||||
|
('hair_size', FieldList('hair_size')),
|
||||||
|
('height', FieldList('size')),
|
||||||
|
('weight', FieldList('weight')),
|
||||||
|
('BMI', FieldBMI('size', 'weight')),
|
||||||
|
('fat', FieldBMI('size', 'weight', fat=True)),
|
||||||
|
('shape', FieldList('shape')),
|
||||||
|
('origins', FieldList('origins')),
|
||||||
|
('signs', FieldFlags('checks1')),
|
||||||
|
('job', FieldStr('job')),
|
||||||
|
('style', FieldList('style')),
|
||||||
|
('food', FieldList('food')),
|
||||||
|
('drink', FieldList('drink')),
|
||||||
|
('smoke', FieldList('smoke')),
|
||||||
|
))),
|
||||||
|
('tastes', OrderedDict((
|
||||||
|
('hobbies', FieldStr('hobbies')),
|
||||||
|
('music', FieldSplit('music', '<br>')),
|
||||||
|
('cinema', FieldSplit('cinema', '<br>')),
|
||||||
|
('books', FieldSplit('books', '<br>')),
|
||||||
|
('tv', FieldSplit('tvs', '<br>')),
|
||||||
|
))),
|
||||||
|
('sex', OrderedDict((
|
||||||
|
('underwear', FieldFlags('checks7')),
|
||||||
|
('practices', FieldFlags('checks5')),
|
||||||
|
('favorite', FieldFlags('checks3')),
|
||||||
|
('toys', FieldFlags('checks6')),
|
||||||
|
))),
|
||||||
|
('personality', OrderedDict((
|
||||||
|
('snap', FieldStr('texts1')),
|
||||||
|
('exciting', FieldStr('texts2')),
|
||||||
|
('hate', FieldStr('texts3')),
|
||||||
|
('vices', FieldStr('texts4')),
|
||||||
|
('assets', FieldStr('texts6')),
|
||||||
|
('fantasies', FieldStr('texts5')),
|
||||||
|
('is', FieldFlags('checks2')),
|
||||||
|
)))
|
||||||
|
))
|
||||||
|
|
||||||
|
def parse_profile(self, profile, consts):
|
||||||
|
cat = int(profile.get('cat', 3))
|
||||||
|
if cat == 1:
|
||||||
|
self.status = Contact.STATUS_ONLINE
|
||||||
|
self.status_msg = u'online'
|
||||||
|
self.status_msg = u'since %s' % profile['last_cnx']
|
||||||
|
elif cat == 2:
|
||||||
|
self.status = Contact.STATUS_AWAY
|
||||||
|
self.status_msg = u'away'
|
||||||
|
self.status_msg = u'connection at %s' % profile['last_cnx']
|
||||||
|
elif cat == 3:
|
||||||
|
self.status = Contact.STATUS_OFFLINE
|
||||||
|
self.status_msg = u'last connection %s' % profile['last_cnx']
|
||||||
|
|
||||||
|
self.summary = html2text(profile.get('about1', '')).strip().replace('\n\n', '\n')
|
||||||
|
if len(profile.get('about2', '')) > 0:
|
||||||
|
self.summary += u'\n\nLooking for:\n%s' % html2text(profile['about2']).strip().replace('\n\n', '\n')
|
||||||
|
|
||||||
|
for photo in profile['pictures']:
|
||||||
|
self.set_photo(photo['url'].split('/')[-1],
|
||||||
|
url=photo['url'],
|
||||||
|
thumbnail_url=photo['url'].replace('image', 'thumb1_'),
|
||||||
|
hidden=photo['hidden'])
|
||||||
|
self.profile = OrderedDict()
|
||||||
|
|
||||||
|
if 'sex' in profile:
|
||||||
|
for section, d in self.TABLE.iteritems():
|
||||||
|
flags = ProfileNode.SECTION
|
||||||
|
if section.startswith('_'):
|
||||||
|
flags |= ProfileNode.HEAD
|
||||||
|
section = section.lstrip('_')
|
||||||
|
s = ProfileNode(section, section.capitalize(), OrderedDict(), flags=flags)
|
||||||
|
|
||||||
|
for key, builder in d.iteritems():
|
||||||
|
value = builder.get_value(profile, consts[int(profile['sex'])])
|
||||||
|
s.value[key] = ProfileNode(key, key.capitalize().replace('_', ' '), value)
|
||||||
|
|
||||||
|
self.profile[section] = s
|
||||||
|
|
||||||
|
self.aum_profile = profile
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
def print_node(node, level=1):
|
||||||
|
result = u''
|
||||||
|
if node.flags & node.SECTION:
|
||||||
|
result += u'\t' * level + node.label + '\n'
|
||||||
|
for sub in node.value.itervalues():
|
||||||
|
result += print_node(sub, level+1)
|
||||||
|
else:
|
||||||
|
if isinstance(node.value, (tuple,list)):
|
||||||
|
value = ', '.join(unicode(v) for v in node.value)
|
||||||
|
elif isinstance(node.value, float):
|
||||||
|
value = '%.2f' % node.value
|
||||||
|
else:
|
||||||
|
value = node.value
|
||||||
|
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
|
||||||
|
return result
|
||||||
|
|
||||||
|
result = u'Nickname: %s\n' % self.name
|
||||||
|
if self.status & Contact.STATUS_ONLINE:
|
||||||
|
s = 'online'
|
||||||
|
elif self.status & Contact.STATUS_OFFLINE:
|
||||||
|
s = 'offline'
|
||||||
|
elif self.status & Contact.STATUS_AWAY:
|
||||||
|
s = 'away'
|
||||||
|
else:
|
||||||
|
s = 'unknown'
|
||||||
|
result += u'Status: %s (%s)\n' % (s, self.status_msg)
|
||||||
|
result += u'URL: %s\n' % self.url
|
||||||
|
result += u'Photos:\n'
|
||||||
|
for name, photo in self.photos.iteritems():
|
||||||
|
result += u'\t%s%s\n' % (photo, ' (hidden)' if photo.hidden else '')
|
||||||
|
result += u'\nProfile:\n'
|
||||||
|
for head in self.profile.itervalues():
|
||||||
|
result += print_node(head)
|
||||||
|
result += u'Description:\n'
|
||||||
|
for s in self.summary.split('\n'):
|
||||||
|
result += u'\t%s\n' % s
|
||||||
|
return result
|
||||||
BIN
modules/aum/favicon.png
Normal file
BIN
modules/aum/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
0
modules/aum/optim/__init__.py
Normal file
0
modules/aum/optim/__init__.py
Normal file
185
modules/aum/optim/priority_connection.py
Normal file
185
modules/aum/optim/priority_connection.py
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from weboob.tools.browser import BrowserUnavailable, BrowserIncorrectPassword
|
||||||
|
from weboob.capabilities.dating import Optimization
|
||||||
|
from weboob.capabilities.account import AccountRegisterError
|
||||||
|
from weboob.tools.log import getLogger
|
||||||
|
from weboob.tools.value import Value, ValuesDict, ValueInt
|
||||||
|
|
||||||
|
from aum.captcha import CaptchaError
|
||||||
|
from aum.exceptions import AdopteWait, AdopteBanned
|
||||||
|
from aum.browser import AuMBrowser
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['PriorityConnection']
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityConnection(Optimization):
|
||||||
|
CONFIG = ValuesDict(ValueInt('minimal', label='Minimal of godchilds', default=5),
|
||||||
|
Value('domain', label='Domain to use for fake accounts emails', default='aum.example.com'),
|
||||||
|
ValueInt('interval', label='Interval of checks (seconds)', default=3600)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, sched, storage, browser):
|
||||||
|
self.sched = sched
|
||||||
|
self.storage = storage
|
||||||
|
self.browser = browser
|
||||||
|
self.logger = getLogger('priorityconn', browser.logger)
|
||||||
|
|
||||||
|
self.config = storage.get('priority_connection', 'config', default=None)
|
||||||
|
if self.config == {}:
|
||||||
|
self.config = None
|
||||||
|
|
||||||
|
self.check_cron = None
|
||||||
|
self.activity_cron = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
if self.config is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.check_cron = self.sched.repeat(int(self.config['interval']), self.check_godchilds)
|
||||||
|
self.activity_cron = self.sched.repeat(600, self.activity_fakes)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.sched.cancel(self.check_cron)
|
||||||
|
self.check_cron = None
|
||||||
|
self.sched.cancel(self.activity_cron)
|
||||||
|
self.activity_cron = None
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_running(self):
|
||||||
|
return self.check_cron is not None
|
||||||
|
|
||||||
|
def set_config(self, params):
|
||||||
|
self.config = params
|
||||||
|
self.storage.set('priority_connection', 'config', self.config)
|
||||||
|
self.storage.save()
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
return self.config
|
||||||
|
|
||||||
|
def generate_name(self):
|
||||||
|
login = u''
|
||||||
|
for x in xrange(8):
|
||||||
|
if x % 2:
|
||||||
|
login += random.choice(u'aeiou')
|
||||||
|
else:
|
||||||
|
login += random.choice(u'bcdfghjklmnprstv')
|
||||||
|
|
||||||
|
fakes = self.storage.get('priority_connection', 'fakes')
|
||||||
|
while ('%s@%s' % (login, self.config['domain'])) in fakes.iterkeys():
|
||||||
|
login += '_'
|
||||||
|
return login
|
||||||
|
|
||||||
|
def generate_password(self):
|
||||||
|
return '%08x' % random.randint(1, int('ffffffff', 16))
|
||||||
|
|
||||||
|
def check_godchilds(self):
|
||||||
|
with self.browser:
|
||||||
|
try:
|
||||||
|
my_id = self.browser.get_my_id()
|
||||||
|
nb_godchilds = self.browser.nb_godchilds()
|
||||||
|
except AdopteWait:
|
||||||
|
nb_godchilds = 0
|
||||||
|
except BrowserUnavailable:
|
||||||
|
# We'll check later
|
||||||
|
return
|
||||||
|
|
||||||
|
missing_godchilds = int(self.config['minimal']) - nb_godchilds
|
||||||
|
|
||||||
|
self.logger.info('Missing godchilds: %s' % missing_godchilds)
|
||||||
|
|
||||||
|
if missing_godchilds <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in xrange(missing_godchilds):
|
||||||
|
registered = False
|
||||||
|
while not registered:
|
||||||
|
name = self.generate_name()
|
||||||
|
password = self.generate_password()
|
||||||
|
|
||||||
|
browser = AuMBrowser('%s@%s' % (name, self.config['domain']), proxy=self.browser.proxy)
|
||||||
|
try:
|
||||||
|
browser.register(password= password,
|
||||||
|
sex= 1, #slut
|
||||||
|
birthday_d= random.randint(1,28),
|
||||||
|
birthday_m= random.randint(1,12),
|
||||||
|
birthday_y= random.randint(1975, 1990),
|
||||||
|
zipcode= 75001,
|
||||||
|
country= 'fr',
|
||||||
|
godfather= my_id)
|
||||||
|
except AccountRegisterError, e:
|
||||||
|
self.logger.warning('Unable to register account: %s' % e)
|
||||||
|
except CaptchaError:
|
||||||
|
self.logger.warning('Unable to solve captcha... Retrying')
|
||||||
|
else:
|
||||||
|
registered = True
|
||||||
|
|
||||||
|
# set nickname
|
||||||
|
browser.set_nickname(name.strip('_').capitalize())
|
||||||
|
# rate my own profile with good score
|
||||||
|
for i in xrange(4):
|
||||||
|
browser.rate(my_id, i, 5.0)
|
||||||
|
|
||||||
|
# save fake in storage
|
||||||
|
fake = {'username': browser.username,
|
||||||
|
'password': password}
|
||||||
|
self.storage.set('priority_connection', 'fakes', name, fake)
|
||||||
|
self.storage.save()
|
||||||
|
self.logger.info('Fake account "%s" created (godfather=%s)' % (name, my_id))
|
||||||
|
|
||||||
|
def activity_fakes(self):
|
||||||
|
try:
|
||||||
|
fakes = self.storage.get('priority_connection', 'fakes', default={})
|
||||||
|
if len(fakes) == 0:
|
||||||
|
return
|
||||||
|
while 1:
|
||||||
|
name = random.choice(fakes.keys())
|
||||||
|
fake = fakes[name]
|
||||||
|
try:
|
||||||
|
browser = AuMBrowser(fake['username'], fake['password'], proxy=self.browser.proxy)
|
||||||
|
except (AdopteBanned,BrowserIncorrectPassword), e:
|
||||||
|
self.logger.warning('Fake %s can\'t login: %s' % (name, e))
|
||||||
|
continue
|
||||||
|
|
||||||
|
profiles = browser.search_profiles(country="fr",
|
||||||
|
dist='10',
|
||||||
|
save=True)
|
||||||
|
|
||||||
|
if not profiles:
|
||||||
|
continue
|
||||||
|
|
||||||
|
id = profiles.pop()
|
||||||
|
profile = browser.get_profile(id)
|
||||||
|
# bad rate
|
||||||
|
for i in xrange(4):
|
||||||
|
browser.rate(profile.get_id(), i, 0.6)
|
||||||
|
# deblock
|
||||||
|
browser.deblock(profile.get_id())
|
||||||
|
return
|
||||||
|
except BrowserUnavailable:
|
||||||
|
# don't care
|
||||||
|
pass
|
||||||
104
modules/aum/optim/profiles_walker.py
Normal file
104
modules/aum/optim/profiles_walker.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright(C) 2010-2011 Romain Bignon, Christophe Benz
|
||||||
|
#
|
||||||
|
# This file is part of weboob.
|
||||||
|
#
|
||||||
|
# weboob is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# weboob is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
from weboob.tools.browser import BrowserUnavailable
|
||||||
|
from weboob.capabilities.dating import Optimization
|
||||||
|
from weboob.tools.log import getLogger
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['ProfilesWalker']
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilesWalker(Optimization):
|
||||||
|
def __init__(self, sched, storage, browser):
|
||||||
|
self.sched = sched
|
||||||
|
self.storage = storage
|
||||||
|
self.browser = browser
|
||||||
|
self.logger = getLogger('walker', browser.logger)
|
||||||
|
|
||||||
|
self.walk_cron = None
|
||||||
|
self.view_cron = None
|
||||||
|
self.visited_profiles = set(storage.get('profiles_walker', 'viewed'))
|
||||||
|
self.logger.info(u'Loaded %d already visited profiles from storage.' % len(self.visited_profiles))
|
||||||
|
self.profiles_queue = set()
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.storage.set('profiles_walker', 'viewed', list(self.visited_profiles))
|
||||||
|
self.storage.save()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.walk_cron = self.sched.repeat(60, self.enqueue_profiles)
|
||||||
|
self.view_cron = self.sched.schedule(randint(10,40), self.view_profile)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.sched.cancel(self.walk_cron)
|
||||||
|
self.sched.cancel(self.view_cron)
|
||||||
|
self.walk_cron = None
|
||||||
|
self.view_cron = None
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_running(self):
|
||||||
|
return self.walk_cron is not None
|
||||||
|
|
||||||
|
def enqueue_profiles(self):
|
||||||
|
try:
|
||||||
|
with self.browser:
|
||||||
|
profiles_to_visit = self.browser.search_profiles().difference(self.visited_profiles)
|
||||||
|
self.logger.info(u'Enqueuing profiles to visit: %s' % profiles_to_visit)
|
||||||
|
self.profiles_queue = set(profiles_to_visit)
|
||||||
|
self.save()
|
||||||
|
except BrowserUnavailable:
|
||||||
|
return
|
||||||
|
|
||||||
|
def view_profile(self):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
id = self.profiles_queue.pop()
|
||||||
|
except KeyError:
|
||||||
|
return # empty queue
|
||||||
|
|
||||||
|
try:
|
||||||
|
with self.browser:
|
||||||
|
profile = self.browser.get_profile(id)
|
||||||
|
self.logger.info(u'Visited profile %s (%s)' % (profile['pseudo'], id))
|
||||||
|
|
||||||
|
# Get score from the aum_score module
|
||||||
|
#d = self.nucentral_core.callService(context.Context.fromComponent(self), 'aum_score', 'score', profile)
|
||||||
|
# d.addCallback(self.score_cb, profile.getID())
|
||||||
|
# deferredlist.append(d)
|
||||||
|
|
||||||
|
# do not forget that we visited this profile, to avoid re-visiting it.
|
||||||
|
self.visited_profiles.add(id)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
except BrowserUnavailable:
|
||||||
|
# We consider this profil hasn't been [correctly] analysed
|
||||||
|
self.profiles_queue.add(id)
|
||||||
|
return
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
finally:
|
||||||
|
if self.view_cron is not None:
|
||||||
|
self.view_cron = self.sched.schedule(randint(10,40), self.view_profile)
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue