new version of aum backend
This commit is contained in:
parent
416e449d07
commit
d714250cc8
21 changed files with 1408 additions and 2000 deletions
|
|
@ -42,7 +42,7 @@ class ProfileFormatter(IFormatter):
|
|||
result += self.print_node(sub, level+1)
|
||||
else:
|
||||
if isinstance(node.value, (tuple,list)):
|
||||
value = ','.join(unicode(v) for v in node.value)
|
||||
value = ', '.join(unicode(v) for v in node.value)
|
||||
else:
|
||||
value = node.value
|
||||
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
|
||||
|
|
|
|||
861
weboob/backends/aum/API.txt
Normal file
861
weboob/backends/aum/API.txt
Normal file
|
|
@ -0,0 +1,861 @@
|
|||
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.baskets
|
||||
----------
|
||||
|
||||
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'}}
|
||||
|
||||
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'}}
|
||||
|
||||
|
|
@ -19,117 +19,30 @@
|
|||
|
||||
import re
|
||||
|
||||
from .pages.contact_list import ContactItem
|
||||
from .pages.profile import ProfilePage
|
||||
from .pages.contact_thread import MailParser
|
||||
|
||||
|
||||
__all__ = ['AntiSpam']
|
||||
|
||||
|
||||
class AntiSpam(object):
|
||||
def check(self, obj):
|
||||
for key, value in self.OBJECTS.iteritems():
|
||||
if isinstance(obj, key):
|
||||
return value(self, obj)
|
||||
|
||||
raise TypeError('Unsupported object %r' % obj)
|
||||
|
||||
def check_contact(self, contact):
|
||||
resume = contact.get_resume()
|
||||
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 contact.get_name() == 'Ekaterina':
|
||||
if thread['member']['pseudo'] == 'Ekaterina':
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def check_profile(self, profile):
|
||||
# The name of profile is in form #123456789
|
||||
if re.match('^#\d+$', profile.get_name()):
|
||||
return False
|
||||
if profile.get_name().strip().lower() in ('ajoute moi', 'a jeute moi', 'ajouter moi'):
|
||||
return False
|
||||
if profile.description.find('h o t m a i l') >= 0:
|
||||
return False
|
||||
if profile.description.find('l i v e f r') >= 0:
|
||||
return False
|
||||
# This pattern in bad french is in several spambots description.
|
||||
if re.match('.*chercher? un m.c tres ch..d.*', profile.description):
|
||||
return False
|
||||
if profile.description.find('ajouter moi plan cam') >= 0:
|
||||
return False
|
||||
if profile.description.find('plan cam sexy') >= 0:
|
||||
return False
|
||||
if profile.description.find('belle dans la cam') >= 0:
|
||||
return False
|
||||
if profile.description.find('pour montre ma cam') >= 0:
|
||||
return False
|
||||
if profile.description.find('show sex') >= 0:
|
||||
return False
|
||||
if profile.description.find('un mec tres chaud') >= 0:
|
||||
return False
|
||||
if profile.description.find('bale chatt') >= 0:
|
||||
return False
|
||||
if profile.description.find('slt tt les mec chaud') >= 0:
|
||||
return False
|
||||
if profile.description.find('tres choud') >= 0:
|
||||
return False
|
||||
if profile.description.find('plan cam') == 0:
|
||||
return False
|
||||
if profile.description.find('cc moi ') >= 0:
|
||||
return False
|
||||
if profile.description.find('une fille tres chaud') >= 0:
|
||||
return False
|
||||
if profile.description.find(u'tré chau') == 0:
|
||||
return False
|
||||
if profile.description.find('sa va bb') == 0:
|
||||
return False
|
||||
if profile.description.startswith('msn\n\n'):
|
||||
return False
|
||||
if profile.description.endswith('Moi la bonne jeune fille gaie'):
|
||||
return False
|
||||
# Her 'Shopping-list' begins with 'hummm'
|
||||
if profile.description.endswith('Sa shopping-list :\nhummm') or \
|
||||
profile.description.endswith('Sa shopping-list :\nhummmm'):
|
||||
return False
|
||||
if profile.description.strip().endswith('Sa shopping-list :\nEMAIL:'):
|
||||
return False
|
||||
# Part of an email address (camiliasexy1live.fr)
|
||||
if profile.description.find('sexy1live') >= 0:
|
||||
return False
|
||||
# Strange thing...
|
||||
if re.match('.*je suis tres cho\w+d.*', profile.description):
|
||||
return False
|
||||
if re.match('.*je suis tr.s chaud', profile.description):
|
||||
return False
|
||||
# Strange thing...
|
||||
if re.match('.*ma croissance de \d+ sm.*', profile.description):
|
||||
return False
|
||||
if re.match('.*mon\s{2,}msn\s{2,}moi\s{2,}ok\s{2,}.*', profile.description):
|
||||
return False
|
||||
if re.match('.*voila\s{2,}mon\s{2,}msn.*', profile.description):
|
||||
return False
|
||||
if re.match('.*cava tout+ ami.*', profile.description):
|
||||
return False
|
||||
if re.match('.*site\s{2,}de\s{2,}chat\s{2,}et mon msn.*', profile.description):
|
||||
return False
|
||||
# "ajouter moi : alussiahotmail.fr"
|
||||
if re.match('^ajouter moi :\s+\w+\.\w+\n', profile.description):
|
||||
return False
|
||||
if profile.description.find('ajouter moi Oki') >= 0:
|
||||
if profile['pseudo'] == '':
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_mail(self, mail):
|
||||
# Spambot with a long first-message.
|
||||
if mail.content.find('Je veux que vous m\'ayez ecrit directement sur le mon e-mail') >= 0:
|
||||
if mail['message'].find('Je veux que vous m\'ayez ecrit directement sur le mon e-mail') >= 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
OBJECTS = {ContactItem: check_contact,
|
||||
ProfilePage: check_profile,
|
||||
MailParser: check_mail,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,34 +20,40 @@
|
|||
from __future__ import with_statement
|
||||
|
||||
import email
|
||||
import time
|
||||
import re
|
||||
import datetime
|
||||
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
|
||||
from weboob.capabilities.contact import ICapContact, Contact, ContactPhoto, ProfileNode, Query, QueryError
|
||||
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 html2text, local2utc
|
||||
|
||||
from .contact import Contact
|
||||
from .captcha import CaptchaError
|
||||
from .antispam import AntiSpam
|
||||
from .browser import AuMBrowser
|
||||
from .exceptions import AdopteWait
|
||||
from .optim.profiles_walker import ProfilesWalker
|
||||
from .optim.visibility import Visibility
|
||||
from .optim.priority_connection import PriorityConnection
|
||||
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'
|
||||
|
|
@ -60,7 +66,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
ValueBool('antispam', label='Enable anti-spam', default=False),
|
||||
ValueBool('baskets', label='Get baskets with new messages', default=True))
|
||||
STORAGE = {'profiles_walker': {'viewed': []},
|
||||
'priority_connection': {'config': {}, 'fakes': {}},
|
||||
'queries_queue': {'queue': []},
|
||||
'sluts': {},
|
||||
}
|
||||
|
|
@ -89,7 +94,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
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('PRIORITY_CONNECTION', PriorityConnection(self.weboob.scheduler, self.storage, self.browser))
|
||||
self.add_optimization('QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser))
|
||||
|
||||
# ---- ICapMessages methods ---------------------
|
||||
|
|
@ -99,25 +103,25 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
|
||||
def iter_threads(self):
|
||||
with self.browser:
|
||||
contacts = self.browser.get_threads_list()
|
||||
threads = self.browser.get_threads_list()
|
||||
|
||||
for contact in contacts:
|
||||
if not contact.get_id():
|
||||
for thread in threads:
|
||||
if thread['member'].get('isBan', True):
|
||||
continue
|
||||
if self.antispam and not self.antispam.check(contact):
|
||||
self.logger.info('Skipped a spam-thread from %s' % contact.get_name())
|
||||
self.report_spam(contact.get_id(), contact.get_suppr_id())
|
||||
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'], thread['id'])
|
||||
continue
|
||||
thread = Thread(contact.get_id())
|
||||
thread.flags = Thread.IS_DISCUSSION
|
||||
thread.title = 'Discussion with %s' % contact.get_name()
|
||||
yield thread
|
||||
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, profiles=None, contact=None):
|
||||
def get_thread(self, id, contacts=None):
|
||||
"""
|
||||
Get a thread and its messages.
|
||||
|
||||
The 'profiles' and 'contact' parameters are only used for internal calls.
|
||||
The 'contacts' parameters is only used for internal calls.
|
||||
"""
|
||||
thread = None
|
||||
if isinstance(id, Thread):
|
||||
|
|
@ -125,57 +129,60 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
id = thread.id
|
||||
|
||||
if not thread:
|
||||
thread = Thread(id)
|
||||
thread = Thread(int(id))
|
||||
thread.flags = Thread.IS_DISCUSSION
|
||||
full = False
|
||||
else:
|
||||
full = True
|
||||
|
||||
with self.browser:
|
||||
mails = self.browser.get_thread_mails(id, full)
|
||||
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 not profiles:
|
||||
profiles = {}
|
||||
for mail in mails:
|
||||
if contacts is None:
|
||||
contacts = {}
|
||||
|
||||
if not thread.title:
|
||||
thread.title = u'Discussion with %s' % mails['member']['pseudo']
|
||||
|
||||
for mail in mails['messages']:
|
||||
flags = 0
|
||||
if self.antispam and not self.antispam.check(mail):
|
||||
self.logger.info('Skipped a spam-mail from %s' % mail.sender)
|
||||
self.report_spam(thread.id, contact and contact.get_suppr_id())
|
||||
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, int(mail['id']))
|
||||
break
|
||||
|
||||
if mail.date > slut['lastmsg']:
|
||||
if parse_dt(mail['date']) > slut['lastmsg']:
|
||||
flags |= Message.IS_UNREAD
|
||||
|
||||
if not mail.profile_link in profiles:
|
||||
if not mail['id_from'] in contacts:
|
||||
with self.browser:
|
||||
profiles[mail.profile_link] = self.browser.get_profile(mail.profile_link)
|
||||
if self.antispam and not self.antispam.check(profiles[mail.profile_link]):
|
||||
self.logger.info('Skipped a spam-mail-profile from %s' % mail.sender)
|
||||
self.report_spam(thread.id, contact and contact.get_suppr_id())
|
||||
contacts[mail['id_from']] = self.get_contact(mail['id_from'])
|
||||
if self.antispam and not self.antispam.check_profile(contacts[mail['id_from']].aum_profile):
|
||||
self.logger.info('Skipped a spam-mail-profile from %s' % mails['member']['pseudo'])
|
||||
self.report_spam(thread.id, mails['id'])
|
||||
break
|
||||
mail.signature += u'\n%s' % profiles[mail.profile_link].get_profile_text()
|
||||
|
||||
if mail.sender == my_name:
|
||||
if mail.new:
|
||||
if int(mail['id_from']) == self.browser.my_id:
|
||||
if int(mails['remoteStatus']) == 0:
|
||||
flags |= Message.IS_NOT_ACCUSED
|
||||
else:
|
||||
flags |= Message.IS_ACCUSED
|
||||
|
||||
if not thread.title:
|
||||
thread.title = mail.title
|
||||
|
||||
self.storage.set('sluts', thread.id, 'status', mails['remoteStatus'])
|
||||
|
||||
msg = Message(thread=thread,
|
||||
id=mail.message_id,
|
||||
title=mail.title,
|
||||
sender=mail.sender,
|
||||
receivers=[mail.name if mail.sender == my_name else my_name], # TODO: me
|
||||
date=mail.date,
|
||||
content=mail.content,
|
||||
signature=mail.signature,
|
||||
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=html2text(mail['message'].replace("\r", "<br>")).strip(),
|
||||
signature=contacts[mail['id_from']].get_text() if mail['id_from'] in contacts else None,
|
||||
children=[],
|
||||
flags=flags)
|
||||
if child:
|
||||
|
|
@ -198,20 +205,20 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
|
||||
def iter_unread_messages(self, thread=None):
|
||||
try:
|
||||
profiles = {}
|
||||
contacts = {}
|
||||
with self.browser:
|
||||
contacts = self.browser.get_threads_list()
|
||||
for contact in contacts:
|
||||
if not contact.get_id():
|
||||
threads = self.browser.get_threads_list()
|
||||
for thread in threads:
|
||||
if thread['member'].get('isBan', True):
|
||||
continue
|
||||
if self.antispam and not self.antispam.check(contact):
|
||||
self.logger.info('Skipped a spam-unread-thread from %s' % contact.get_name())
|
||||
self.report_spam(contact.get_id(), contact.get_suppr_id())
|
||||
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'], thread['id'])
|
||||
continue
|
||||
slut = self._get_slut(contact.get_id())
|
||||
if contact.get_lastmsg_date() > slut['lastmsg']:
|
||||
thread = self.get_thread(contact.get_id(), profiles, contact)
|
||||
for m in thread.iter_all_messages():
|
||||
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)
|
||||
for m in t.iter_all_messages():
|
||||
if m.flags & m.IS_UNREAD:
|
||||
yield m
|
||||
|
||||
|
|
@ -222,31 +229,31 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
# 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:
|
||||
ids = self.browser.get_baskets()
|
||||
while new_baskets > 0 and len(ids) > new_baskets:
|
||||
new_baskets -= 1
|
||||
if ids[new_baskets] == '-1':
|
||||
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
|
||||
profile = self.browser.get_profile(ids[new_baskets])
|
||||
if not profile or profile.get_id() == 0:
|
||||
continue
|
||||
if self.antispam and not self.antispam.check(profile):
|
||||
self.logger.info('Skipped a spam-basket from %s' % profile.get_name())
|
||||
self.report_spam(profile.get_id())
|
||||
contact = self.get_contact(basket['id'])
|
||||
if self.antispam and not self.antispam.check_profile(contact.aum_profile):
|
||||
self.logger.info('Skipped a spam-basket from %s' % contact.name)
|
||||
self.report_spam(basket['id'])
|
||||
continue
|
||||
|
||||
thread = Thread(profile.get_id())
|
||||
thread.title = 'Basket of %s' % profile.get_name()
|
||||
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=profile.get_name(),
|
||||
receivers=[self.browser.get_my_name()],
|
||||
date=None, # now
|
||||
sender=contact.name,
|
||||
receivers=[my_name],
|
||||
date=parse_dt(basket['date']),
|
||||
content='You are taken in her basket!',
|
||||
signature=profile.get_profile_text(),
|
||||
signature=contact.get_text(),
|
||||
children=[],
|
||||
flags=Message.IS_UNREAD)
|
||||
yield thread.root
|
||||
|
|
@ -256,25 +263,31 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
|
||||
def set_message_read(self, message):
|
||||
if message.id == self.MAGIC_ID_BASKET:
|
||||
# We don't save baskets.
|
||||
# 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
|
||||
#slut['msgstatus'] = contact.get_status()
|
||||
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),
|
||||
'msgstatus': ''}
|
||||
'status': 0}
|
||||
else:
|
||||
slut = self.storage.get('sluts', id)
|
||||
|
||||
slut['lastmsg'] = slut['lastmsg'].replace(tzinfo=tz.tzutc())
|
||||
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 ---------------------
|
||||
|
|
@ -319,59 +332,29 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
if not profile:
|
||||
return None
|
||||
|
||||
_id = profile.id
|
||||
|
||||
if profile.is_online():
|
||||
s = Contact.STATUS_ONLINE
|
||||
else:
|
||||
s = Contact.STATUS_OFFLINE
|
||||
_id = profile['id']
|
||||
|
||||
if isinstance(contact, Contact):
|
||||
contact.id = _id
|
||||
contact.name = profile.get_name()
|
||||
contact.status = s
|
||||
contact.name = profile['pseudo']
|
||||
else:
|
||||
contact = Contact(_id, profile.get_name(), s)
|
||||
contact = Contact(_id, profile['pseudo'], Contact.STATUS_ONLINE)
|
||||
contact.url = self.browser.id2url(_id)
|
||||
contact.status_msg = profile.get_status()
|
||||
contact.summary = profile.description
|
||||
for photo in profile.photos:
|
||||
contact.set_photo(photo['url'].split('/')[-1],
|
||||
url=photo['url'],
|
||||
thumbnail_url=photo['url'].replace('image', 'thumb1_'),
|
||||
hidden=photo['hidden'])
|
||||
contact.profile = []
|
||||
|
||||
stats = ProfileNode('stats', 'Stats', [], flags=ProfileNode.HEAD|ProfileNode.SECTION)
|
||||
for label, value in profile.get_stats().iteritems():
|
||||
stats.value.append(ProfileNode(label, label.capitalize(), value))
|
||||
contact.profile.append(stats)
|
||||
|
||||
for section, d in profile.get_table().iteritems():
|
||||
s = ProfileNode(section, section.capitalize(), [], flags=ProfileNode.SECTION)
|
||||
for key, value in d.iteritems():
|
||||
s.value.append(ProfileNode(key, key.capitalize(), value))
|
||||
contact.profile.append(s)
|
||||
|
||||
contact.parse_profile(profile, self.browser.get_consts())
|
||||
return contact
|
||||
|
||||
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
|
||||
with self.browser:
|
||||
for contact in self.browser.iter_contacts():
|
||||
s = 0
|
||||
if contact['cat'] == 1:
|
||||
if contact['isOnline']:
|
||||
s = Contact.STATUS_ONLINE
|
||||
elif contact['cat'] == 3:
|
||||
s = Contact.STATUS_OFFLINE
|
||||
elif contact['cat'] == 2:
|
||||
s = Contact.STATUS_AWAY
|
||||
else:
|
||||
self.logger.warning('Unknown AuM contact status: %s' % contact['cat'])
|
||||
s = Contact.STATUS_OFFLINE
|
||||
|
||||
if not status & s or ids and contact['id'] in ids:
|
||||
if not status & s or (ids and not contact['id'] in ids):
|
||||
continue
|
||||
|
||||
# TODO age in contact['birthday']
|
||||
c = Contact(contact['id'], contact['pseudo'], s)
|
||||
c.url = self.browser.id2url(contact['id'])
|
||||
c.status_msg = u'%s old' % contact['birthday']
|
||||
|
|
@ -490,15 +473,12 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
|
|||
|
||||
def get_account_status(self):
|
||||
with self.browser:
|
||||
try:
|
||||
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()),
|
||||
)
|
||||
except AdopteWait:
|
||||
return (StatusField('notice', '', u'<h3>You are currently waiting 1am to be able to connect with this account</h3>', StatusField.FIELD_HTML|StatusField.FIELD_TEXT))
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -18,308 +18,252 @@
|
|||
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import re
|
||||
import datetime
|
||||
import time
|
||||
import random
|
||||
import urllib
|
||||
try:
|
||||
import simplejson
|
||||
import json
|
||||
except ImportError:
|
||||
# Python 2.6+ has a module similar to simplejson
|
||||
import json as simplejson
|
||||
import simplejson as json
|
||||
|
||||
from weboob.tools.browser import BaseBrowser, BrowserUnavailable
|
||||
from weboob.tools.parsers.html5libparser import Html5libParser
|
||||
|
||||
from weboob.backends.aum.exceptions import AdopteWait
|
||||
|
||||
from weboob.backends.aum.pages.account import AccountPage
|
||||
from weboob.backends.aum.pages.home import HomePage
|
||||
from weboob.backends.aum.pages.contact_list import ContactListPage
|
||||
from weboob.backends.aum.pages.contact_thread import ContactThreadPage
|
||||
from weboob.backends.aum.pages.baskets import BasketsPage
|
||||
from weboob.backends.aum.pages.profile import ProfilePage
|
||||
from weboob.backends.aum.pages.search import SearchPage
|
||||
from weboob.backends.aum.pages.login import LoginPage, RedirectPage, BanPage, ErrPage, RegisterPage, \
|
||||
RegisterWaitPage, RegisterConfirmPage, ShopPage, InvitePage
|
||||
from weboob.backends.aum.pages.edit import EditPhotoPage, EditPhotoCbPage, EditAnnouncePage, \
|
||||
EditDescriptionPage, EditSexPage, EditPersonalityPage
|
||||
from weboob.backends.aum.pages.wait import WaitPage
|
||||
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword, BrowserUnavailable
|
||||
|
||||
from weboob.capabilities.chat import ChatException, ChatMessage
|
||||
from weboob.capabilities.messages import CantSendMessage
|
||||
|
||||
|
||||
__all__ = ['AuMBrowser']
|
||||
|
||||
|
||||
class AuMBrowser(BaseBrowser):
|
||||
DOMAIN = 'www.adopteunmec.com'
|
||||
ENCODING = 'iso-8859-1'
|
||||
PAGES = {'http://www.adopteunmec.com/': LoginPage,
|
||||
'http://www.adopteunmec.com/index.html': LoginPage,
|
||||
'http://www.adopteunmec.com/index.php': LoginPage,
|
||||
'http://www.adopteunmec.com/loginErr.php.*': ErrPage,
|
||||
'http://www.adopteunmec.com/bans.php.*': BanPage,
|
||||
'http://www.adopteunmec.com/redirect.php\?action=login': RedirectPage,
|
||||
'http://www.adopteunmec.com/wait.php': WaitPage,
|
||||
'http://www.adopteunmec.com/invits.php': InvitePage,
|
||||
'http://www.adopteunmec.com/register2.php': RegisterPage,
|
||||
'http://www.adopteunmec.com/register3.php.*': RegisterWaitPage,
|
||||
'http://www.adopteunmec.com/register4.php.*': RegisterConfirmPage,
|
||||
'http://www.adopteunmec.com/home.php': HomePage,
|
||||
'http://www.adopteunmec.com/shop2c?.php': ShopPage,
|
||||
'http[s]://www.adopteunmec.com/register-pay.php': ShopPage,
|
||||
'http://www.adopteunmec.com/mails.php': ContactListPage,
|
||||
'http://www.adopteunmec.com/mail.php': ContactListPage,
|
||||
'http://www.adopteunmec.com/mails.php\?type=1': BasketsPage,
|
||||
'http://www.adopteunmec.com/mail.php\?type=1': BasketsPage,
|
||||
'http://www.adopteunmec.com/thread.php\?id=([0-9]+)(&see=all)?': ContactThreadPage,
|
||||
'http://www.adopteunmec.com/edit.php\?type=1': EditPhotoPage,
|
||||
'http://s\d+.adopteunmec.com/upload\d.php\?.*': EditPhotoCbPage,
|
||||
'http://www.adopteunmec.com/edit.php\?type=2': EditAnnouncePage,
|
||||
'http://www.adopteunmec.com/edit.php\?type=3': EditDescriptionPage,
|
||||
'http://www.adopteunmec.com/edit.php\?type=4': EditSexPage,
|
||||
'http://www.adopteunmec.com/edit.php\?type=5': EditPersonalityPage,
|
||||
'http://www.adopteunmec.com/search.php.*': SearchPage,
|
||||
'http://www.adopteunmec.com/searchRes.php.*': SearchPage,
|
||||
'http://www.adopteunmec.com/rencontres-femmes/(.*)/([0-9]+)': ProfilePage,
|
||||
'http://www.adopteunmec.com/catalogue-hommes/(.*)/([0-9]+)': ProfilePage,
|
||||
'http://www.adopteunmec.com/view2.php': ProfilePage, # my own profile
|
||||
'http://www.adopteunmec.com/(\w+)': ProfilePage, # a custom profile url
|
||||
'http://www.adopteunmec.com/account.php': AccountPage,
|
||||
}
|
||||
DOMAIN = 'api.adopteunmec.com'
|
||||
APIKEY = 'fb0123456789abcd'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['parser'] = Html5libParser(api='dom')
|
||||
BaseBrowser.__init__(self, *args, **kwargs)
|
||||
self.my_id = 0
|
||||
consts = None
|
||||
my_sex = 0
|
||||
my_id = 0
|
||||
my_name = u''
|
||||
|
||||
def id2url(self, _id):
|
||||
return u'%s://%s/%s' % (self.PROTOCOL, self.DOMAIN, _id)
|
||||
def id2url(self, id):
|
||||
return 'http://www.adopteunmec.com/%s' % id
|
||||
|
||||
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 len(r['errors']) > 0 and r['errors'][0] in (u'0.0.2', u'1.1.1'):
|
||||
if not nologin:
|
||||
self.login()
|
||||
return self.api_request(command, action, parameter, data, nologin=True)
|
||||
else:
|
||||
raise BrowserIncorrectPassword()
|
||||
return r
|
||||
|
||||
def login(self):
|
||||
if not self.is_on_page(LoginPage):
|
||||
self.home()
|
||||
self.page.login(self.username, self.password)
|
||||
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']
|
||||
return r
|
||||
|
||||
def is_logged(self):
|
||||
return self.page and self.page.is_logged()
|
||||
#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)
|
||||
|
||||
def home(self):
|
||||
return self.location('http://www.adopteunmec.com/index.php')
|
||||
#@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)
|
||||
|
||||
def pageaccess(func):
|
||||
#@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.is_on_page(WaitPage):
|
||||
if not self.page.check():
|
||||
raise AdopteWait(u'Could not connect between 6pm and 1am.')
|
||||
self.home()
|
||||
if not self.page or self.is_on_page(LoginPage) and self.password:
|
||||
self.home()
|
||||
|
||||
if self.my_id == 0:
|
||||
self.login()
|
||||
return func(self, *args, **kwargs)
|
||||
return inner
|
||||
|
||||
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)
|
||||
def get_consts(self):
|
||||
if self.consts is not None:
|
||||
return self.consts
|
||||
|
||||
@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)
|
||||
self.consts = []
|
||||
for i in xrange(2):
|
||||
r = self.api_request('me', 'all_values', data={'sex': i})
|
||||
self.consts.append(r['result']['values'])
|
||||
|
||||
@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)
|
||||
return self.consts
|
||||
|
||||
@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)
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def score(self):
|
||||
if time.time() - self.last_update > 60:
|
||||
self.home()
|
||||
return self.page.score()
|
||||
r = self.api_request('member', 'view', data={'id': self.my_id})
|
||||
return int(r['result']['member']['popu']['popu'])
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def get_my_name(self):
|
||||
if time.time() - self.last_update > 60:
|
||||
self.home()
|
||||
return self.page.get_my_name()
|
||||
return self.my_name
|
||||
|
||||
@check_login
|
||||
def get_my_id(self):
|
||||
if self.my_id:
|
||||
return self.my_id
|
||||
|
||||
try:
|
||||
if not self.is_on_page(HomePage):
|
||||
self.home()
|
||||
except AdopteWait:
|
||||
self.location('/invits.php')
|
||||
|
||||
self.my_id = self.page.get_my_id()
|
||||
return self.my_id
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def nb_new_mails(self):
|
||||
if time.time() - self.last_update > 60:
|
||||
self.home()
|
||||
return self.page.nb_new_mails()
|
||||
r = self.api_request('me', '[default]')
|
||||
return r['result']['news']['newMails']
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def nb_new_baskets(self):
|
||||
if time.time() - self.last_update > 60:
|
||||
self.home()
|
||||
return self.page.nb_new_baskets()
|
||||
r = self.api_request('me', '[default]')
|
||||
return r['result']['news']['newBaskets']
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def nb_new_visites(self):
|
||||
if time.time() - self.last_update > 60:
|
||||
self.home()
|
||||
return self.page.nb_new_visites()
|
||||
r = self.api_request('me', '[default]')
|
||||
return r['result']['news']['newVisits']
|
||||
|
||||
@pageaccess
|
||||
def nb_available_charms(self, reload=False):
|
||||
if reload or not self.is_on_page(HomePage):
|
||||
self.home()
|
||||
return self.page.nb_available_charms()
|
||||
@check_login
|
||||
def nb_available_charms(self):
|
||||
r = self.login()
|
||||
return r['result']['flashs']
|
||||
|
||||
@pageaccess
|
||||
def nb_godchilds(self, reload=False):
|
||||
if reload or not self.is_on_page(HomePage):
|
||||
self.home()
|
||||
return self.page.nb_godchilds()
|
||||
@check_login
|
||||
def nb_godchilds(self):
|
||||
r = self.api_request('member', 'view', data={'id': self.my_id})
|
||||
return int(r['result']['member']['popu']['invits'])
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def get_baskets(self):
|
||||
self.location('/mail.php?type=1')
|
||||
return self.page.get_profiles_ids_list()
|
||||
r = self.api_request('me', 'basket')
|
||||
return r['result']['basket']
|
||||
|
||||
@pageaccess
|
||||
def flush_visits(self):
|
||||
""" Does nothing, only flush new visits to increase my score """
|
||||
self.openurl('/mail.php?type=3')
|
||||
@check_login
|
||||
def get_threads_list(self, count=30):
|
||||
r = self.api_request('message', '[default]', '%d,0' % count)
|
||||
return r['result']['threads']
|
||||
|
||||
@pageaccess
|
||||
def get_threads_list(self):
|
||||
if not self.is_on_page(ContactListPage):
|
||||
self.location('/mail.php')
|
||||
@check_login
|
||||
def get_thread_mails(self, id, count=30):
|
||||
r = self.api_request('message', 'thread', data={'memberId': id, 'count': count})
|
||||
return r['result']['thread']
|
||||
|
||||
return self.page.get_contact_list()
|
||||
|
||||
@pageaccess
|
||||
def get_thread_mails(self, id, full=False):
|
||||
if not self.is_on_page(ContactThreadPage) or self.page.id != int(id) or full:
|
||||
self.page.open_thread_page(id, full)
|
||||
return self.page.mails
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def post_mail(self, id, content):
|
||||
if not self.is_on_page(ContactThreadPage) or self.page.id != int(id):
|
||||
self.page.open_thread_page(id)
|
||||
self.page.post(content)
|
||||
r = self.api_request('message', 'new', data={'memberId': id, 'message': content.encode('utf-8')})
|
||||
if len(r['errors']) > 0:
|
||||
raise CantSendMessage(r['errors'][0])
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def delete_thread(self, id):
|
||||
data = 'delete=true&suppr%%5B%%5D=%s' % id
|
||||
url = 'http://www.adopteunmec.com/mail.php'
|
||||
self.openurl(url, data).read()
|
||||
r = self.api_request('message', 'delete', data={'id_user': id})
|
||||
self.logger.debug('Thread deleted: %r' % r)
|
||||
|
||||
return True
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def send_charm(self, id):
|
||||
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read()
|
||||
self.logger.debug('Charm: %s' % result)
|
||||
return result.find('noMoreFlashes') < 0
|
||||
r = self.api_request('member', 'addBasket', data={'id': id})
|
||||
return r['errors'] == '0'
|
||||
|
||||
@pageaccess
|
||||
@check_login
|
||||
def add_basket(self, id):
|
||||
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read()
|
||||
self.logger.debug('Basket: %s' % result)
|
||||
# TODO check if it works (but it should)
|
||||
return True
|
||||
r = self.api_request('member', 'addBasket', data={'id': id})
|
||||
return r['errors'] == '0'
|
||||
|
||||
@pageaccess
|
||||
def deblock(self, id):
|
||||
self.readurl('http://www.adopteunmec.com/fajax_postMessage.php?action=deblock&to=%s' % id)
|
||||
return True
|
||||
|
||||
@pageaccess
|
||||
def report_fake(self, id):
|
||||
return self.readurl('http://www.adopteunmec.com/fake.php', 'id=%s' % id)
|
||||
|
||||
@pageaccess
|
||||
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)
|
||||
|
||||
@pageaccess
|
||||
def search_profiles(self, **kwargs):
|
||||
self.location('/search.php')
|
||||
self.page.search(**kwargs)
|
||||
return self.page.get_profiles_ids()
|
||||
r = self.api_request('searchs', '[default]')
|
||||
params = r['result']['search']
|
||||
params.pop('query', None)
|
||||
params.update(kwargs)
|
||||
|
||||
@pageaccess
|
||||
def get_profile(self, link):
|
||||
if isinstance(link, basestring):
|
||||
link = link.replace('http://www.adopteunmec.com/', '')
|
||||
if link.startswith('/'):
|
||||
link = link[1:]
|
||||
self.location('/%s' % link)
|
||||
return self.page
|
||||
r = self.api_request('searchs', 'advanced', '30,0', params)
|
||||
ids = [s['id'] for s in r['result']['search']]
|
||||
return set(ids)
|
||||
|
||||
@pageaccess
|
||||
def get_slut_state(self, id):
|
||||
result = self.openurl('http://www.adopteunmec.com/%s' % id).read()
|
||||
if result.find('<td align="right" style="font-size:12px;font-weight:bold">en ligne</td>') >= 0:
|
||||
r = 'online'
|
||||
elif result.find('Cet utilisateur a quitt\xe9 le site<br />') >= 0:
|
||||
r = 'removed'
|
||||
elif result.find('ce profil a \xe9t\xe9 bloqu\xe9 par l\'\xe9quipe de mod\xe9ration<br />') >= 0:
|
||||
r = 'removed'
|
||||
elif result.find('<div align=center style="color:#ff0000;font-size:16px"><br /><br />Cette personne<br>vous a bloqu\xe9</div>') >= 0:
|
||||
r = 'blocked'
|
||||
else:
|
||||
r = 'offline'
|
||||
def get_profile(self, id, with_pics=True):
|
||||
r = self.api_request('member', 'view', data={'id': id})
|
||||
profile = r['result']['member']
|
||||
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)
|
||||
|
||||
print 'getSlutState(%s) = %s' % (id, r)
|
||||
return r
|
||||
if len(profile['pictures']) > 0:
|
||||
pic_regex = re.compile('(?P<base_url>http://.+\.adopteunmec\.com/.+/)image(?P<id>.+)\.jpg')
|
||||
pic_max_id = max(int(pic_regex.match(pic['url']).groupdict()['id']) for pic in profile['pictures'])
|
||||
base_url = pic_regex.match(profile['pictures'][0]['url']).groupdict()['base_url']
|
||||
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})
|
||||
|
||||
@pageaccess
|
||||
def is_slut_online(self, id):
|
||||
result = self.openurl('http://www.adopteunmec.com/%s' % id).read()
|
||||
r = result.find('<td align="right" style="font-size:12px;font-weight:bold">en ligne</td>') >= 0
|
||||
print 'isSlutOnline(%s) = %s' % (id, r)
|
||||
return r
|
||||
return profile
|
||||
|
||||
def _get_chat_infos(self):
|
||||
try:
|
||||
json = simplejson.load(self.openurl('http://www.adopteunmec.com/1.1_cht_get.php?anticache=%f' % random.random()))
|
||||
data = json.load(self.openurl('http://www.adopteunmec.com/1.1_cht_get.php?anticache=%f' % random.random()))
|
||||
except ValueError:
|
||||
raise BrowserUnavailable()
|
||||
|
||||
if json['error']:
|
||||
raise ChatException(u'Error while getting chat infos. json:\n%s' % json)
|
||||
return json
|
||||
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):
|
||||
|
|
@ -329,13 +273,13 @@ class AuMBrowser(BaseBrowser):
|
|||
yield contact
|
||||
yielded_ids.add(contact['id'])
|
||||
|
||||
json = self._get_chat_infos()
|
||||
return iter_dedupe(json['contacts'])
|
||||
data = self._get_chat_infos()
|
||||
return iter_dedupe(data['contacts'])
|
||||
|
||||
def iter_chat_messages(self, _id=None):
|
||||
json = self._get_chat_infos()
|
||||
if json['messages'] is not None:
|
||||
for message in json['messages']:
|
||||
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):
|
||||
|
|
|
|||
251
weboob/backends/aum/contact.py
Normal file
251
weboob/backends/aum/contact.py
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
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 FieldIP(FieldBase):
|
||||
def get_value(self, profile, consts):
|
||||
s = profile[self.key]
|
||||
if profile[self.key] != profile[self.key2]:
|
||||
s += ' (first %s)' % 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/%d' % id
|
||||
else:
|
||||
return ''
|
||||
|
||||
class FieldPopu(FieldBase):
|
||||
def get_value(self, profile, consts):
|
||||
return unicode(profile['popu'][self.key])
|
||||
|
||||
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((
|
||||
('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')),
|
||||
))),
|
||||
('details', OrderedDict((
|
||||
('old', FieldOld('birthday')),
|
||||
('birthday', FieldStr('birthday')),
|
||||
('zipcode', FieldStr('zip')),
|
||||
('location', FieldStr('city')),
|
||||
('country', FieldStr('country')),
|
||||
('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)),
|
||||
('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):
|
||||
if int(profile['cat']) == 1:
|
||||
self.status = Contact.STATUS_ONLINE
|
||||
self.status_msg = u'online'
|
||||
elif int(profile['cat']) == 2:
|
||||
self.status = Contact.STATUS_AWAY
|
||||
self.status_msg = u'away'
|
||||
elif int(profile['cat']) == 3:
|
||||
self.status = Contact.STATUS_OFFLINE
|
||||
self.status_msg = u'last connexion %s' % profile['last_cnx']
|
||||
|
||||
self.summary = html2text(profile['about1']).strip().replace('\n\n', '\n')
|
||||
if len(profile['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 = []
|
||||
|
||||
for section, d in self.TABLE.iteritems():
|
||||
flags = ProfileNode.SECTION
|
||||
if section.startswith('_'):
|
||||
flags |= ProfileNode.HEAD
|
||||
section = section.lstrip('_')
|
||||
s = ProfileNode(section, section.capitalize(), [], flags=flags)
|
||||
|
||||
for key, builder in d.iteritems():
|
||||
value = builder.get_value(profile, consts[int(profile['sex'])])
|
||||
s.value.append(ProfileNode(key, key.capitalize().replace('_', ' '), value))
|
||||
|
||||
self.profile.append(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:
|
||||
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\n' % photo
|
||||
result += u'\nProfile:\n'
|
||||
for head in self.profile:
|
||||
result += print_node(head)
|
||||
result += u'Description:\n'
|
||||
for s in self.summary.split('\n'):
|
||||
result += u'\t%s\n' % s
|
||||
return result
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.tools.browser import BrowserUnavailable
|
||||
|
||||
class AdopteWait(BrowserUnavailable):
|
||||
pass
|
||||
|
||||
class AdopteBanned(BrowserUnavailable):
|
||||
pass
|
||||
|
||||
class AdopteCantPostMail(Exception):
|
||||
pass
|
||||
|
|
@ -82,7 +82,7 @@ class ProfilesWalker(Optimization):
|
|||
try:
|
||||
with self.browser:
|
||||
profile = self.browser.get_profile(id)
|
||||
self.logger.info(u'Visited profile %s (%s)' % (profile.get_name(), 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)
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright(C) 2008-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.backends.aum.pages.base import PageBase
|
||||
|
||||
class AccountPage(PageBase):
|
||||
def set_godfather(self, _id):
|
||||
self.browser.select_form(name='setGodfather')
|
||||
self.browser['godfather'] = _id
|
||||
self.browser.submit()
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
# -*- 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 re
|
||||
from weboob.tools.browser import BasePage, BrowserUnavailable
|
||||
from weboob.backends.aum.exceptions import AdopteBanned
|
||||
|
||||
class PageBase(BasePage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
BasePage.__init__(self, *args, **kwargs)
|
||||
|
||||
# Check the 'oops' error message when adopteunmec guys are gay.
|
||||
b = self.document.getElementsByTagName('body')[0]
|
||||
for div in b.getElementsByTagName('div'):
|
||||
if div.getAttribute('id') == 'oops':
|
||||
raise BrowserUnavailable('Oops')
|
||||
|
||||
# Check when the account is temporarily blocked.
|
||||
for img in self.document.getElementsByTagName('img'):
|
||||
if img.getAttribute('src') == 'http://s.adopteunmec.com/img/exemple.jpg':
|
||||
raise AdopteBanned('Your account is blocked. You have to unblock by yourself but we can\'t help you.')
|
||||
|
||||
def is_logged(self):
|
||||
for form in self.browser.forms():
|
||||
if form.name == 'form_login':
|
||||
return False
|
||||
return True
|
||||
|
||||
def open_contact_list_page(self):
|
||||
self.browser.follow_link(url_regex=r"/mail.php$")
|
||||
|
||||
def open_thread_page(self, id, all_messages=False):
|
||||
if all_messages:
|
||||
self.browser.location('/thread.php?id=%d&see=all' % int(id))
|
||||
else:
|
||||
self.browser.location('/thread.php?id=%d' % int(id))
|
||||
|
||||
def score(self):
|
||||
"""
|
||||
<table width="220">
|
||||
<tr>
|
||||
<td align=left class=header>popularité</td>
|
||||
<td align=right class=header><big style="color:#ff0198;" id=popScore>7.230</big> pts</td>
|
||||
</tr>
|
||||
</table>
|
||||
"""
|
||||
|
||||
l = self.document.getElementsByTagName('table')
|
||||
for tag in l:
|
||||
if tag.getAttribute('width') == '220':
|
||||
# <table><tbody(implicit)><tr><td>
|
||||
child = tag.childNodes[0].childNodes[0].childNodes[3]
|
||||
return int(child.childNodes[0].childNodes[1].data.replace(' ', '').strip())
|
||||
|
||||
self.logger.error("Error: I can't find the score :(")
|
||||
return '0'
|
||||
|
||||
def __get_indicator(self, elementName):
|
||||
""" <span id=mailsCounter><blink>1</blink></span> """
|
||||
|
||||
l = self.document.getElementsByTagName('span')
|
||||
for tag in l:
|
||||
if tag.getAttribute('id') == elementName:
|
||||
child = tag.childNodes[0]
|
||||
|
||||
if not hasattr(child, 'data'):
|
||||
if child.tagName != u'blink':
|
||||
self.logger.warning("Warning: %s counter isn't a blink and hasn't data" % elementName)
|
||||
child = child.childNodes[0]
|
||||
if not hasattr(child, 'data'):
|
||||
break
|
||||
|
||||
return int(child.data)
|
||||
|
||||
self.logger.error("Error: I can't find the %s counter :(" % elementName)
|
||||
return 0
|
||||
|
||||
MYNAME_REGEXP = re.compile("Bonjour (.*)")
|
||||
def get_my_name(self):
|
||||
""" <span class=header2>Bonjour Romain</span> """
|
||||
|
||||
tags = self.document.getElementsByTagName('span')
|
||||
for tag in tags:
|
||||
if hasattr(tag.firstChild, 'data'):
|
||||
m = self.MYNAME_REGEXP.match(tag.firstChild.data)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
self.logger.warning('Warning: Unable to fetch name')
|
||||
return '?'
|
||||
|
||||
def nb_new_mails(self):
|
||||
return self.__get_indicator(u'mailsCounter')
|
||||
|
||||
def nb_new_baskets(self):
|
||||
return self.__get_indicator(u'flashsCounter')
|
||||
|
||||
def nb_new_visites(self):
|
||||
return self.__get_indicator(u'visitesCounter')
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.profileslist_base import ProfilesListBase
|
||||
|
||||
class BasketsPage(ProfilesListBase):
|
||||
|
||||
pass
|
||||
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
# -*- 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 re
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil import tz
|
||||
from logging import warning
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
|
||||
class ContactItem:
|
||||
u"""
|
||||
<tr bgcolor="#ff7ccb" height=1><td colspan=10></td></tr>
|
||||
<tr style="background:#ffd5ee" height=74 onmouseover="this.style.background='#f3d5e7'" onmouseout="this.style.background='#ffd5ee'">
|
||||
<td class=a onclick="window.location='/thread.php?id=110921'" width=4></td>
|
||||
<td class=a onclick="window.location='/rencontres-femmes/france/ile-de-france/Hen/110921'" width=70 align=left>
|
||||
<table><tr valign="bottom"><td style="background:url(http://p1.adopteunmec.com/1/2/9/0/1/thumb0_7.jpg);width:66px;height:66px" align="right"> </td></tr></table>
|
||||
</td>
|
||||
<td class=a onclick="window.location='/thread.php?id=110921'" width=150 align=left><big><b>Hen</b></big><br>
|
||||
19ans, Montreuil</td>
|
||||
<td class=a onclick="window.location='/thread.php?id=110921'" width=320 align=left><b>Comme ça, on est deux.</b><br>
|
||||
il y a 1 heure</td>
|
||||
<td class=a onclick="window.location='/thread.php?id=110921'" width=100 align=right>nouveau <img src="http://img.adopteunmec.com/img/ico_mail0.gif" />
|
||||
</td>
|
||||
<td class=a onclick="window.location='/thread.php?id=110921'" width=30 align=left></td>
|
||||
<td width=20 align=right><input id='fcc_suppr_545615' name='suppr[]' type='hidden' /><img id='cc_suppr_545615' src='http://img.adopteunmec.com/img/i/check0.png' onclick='customCheckClick(this)' align="absmiddle"/> </td>
|
||||
<script>supprs[supprs.length] = 'cc_suppr_545615';</script>
|
||||
|
||||
</td>
|
||||
<td width=7></td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
fields = ['thread_link', 'photo', 'useless3', 'name', 'resume', 'status', 'useless', 'remove', 'useless2']
|
||||
|
||||
def __init__(self, tr):
|
||||
self.tr = tr
|
||||
self.id = 0
|
||||
|
||||
def __get_element(self, id):
|
||||
return self.tr.getElementsByTagName('td')[self.fields.index(id)]
|
||||
|
||||
def get_name(self):
|
||||
tag = self.__get_element('name')
|
||||
node = tag.getElementsByTagName('b')[0].firstChild
|
||||
if node:
|
||||
name = node.data
|
||||
else:
|
||||
# it is possible if the user has left site and hasn't any nickname
|
||||
name = ''
|
||||
|
||||
return name
|
||||
|
||||
def get_status(self):
|
||||
tag = self.__get_element('status')
|
||||
|
||||
return tag.firstChild.data
|
||||
|
||||
def is_new(self):
|
||||
return self.get_status() == u'nouveau'
|
||||
|
||||
def is_answered(self):
|
||||
return self.get_status() == u'répondu'
|
||||
|
||||
def get_resume(self):
|
||||
tag = self.__get_element('resume')
|
||||
return tag.getElementsByTagName('b')[0].firstChild.data.strip()
|
||||
|
||||
def get_suppr_id(self):
|
||||
tag = self.__get_element('remove')
|
||||
return tag.getElementsByTagName('input')[0].getAttribute('id').split('_')[-1]
|
||||
|
||||
LASTMSG_RE = re.compile('il y a (\d+) (\w+)')
|
||||
def get_lastmsg_date(self):
|
||||
tag = self.__get_element('resume')
|
||||
s = tag.childNodes[3].data
|
||||
m = self.LASTMSG_RE.match(s)
|
||||
if m:
|
||||
d = {'secondes': 1,
|
||||
'seconde': 1,
|
||||
'minutes': 60,
|
||||
'minute': 60,
|
||||
'heures': 3600,
|
||||
'heure': 3600,
|
||||
'jours': 24*3600,
|
||||
'jour': 24*3600,
|
||||
'mois': 24*3600*30,
|
||||
}
|
||||
try:
|
||||
i = int(m.group(1)) * d[m.group(2)]
|
||||
except KeyError:
|
||||
warning('Unable to parse lastmsg ("%s" is not a valid unit)' % m.group(2))
|
||||
return None
|
||||
else:
|
||||
return datetime.now(tz=tz.tzutc()) - timedelta(seconds=i)
|
||||
else:
|
||||
warning('Unable to parse lastmsg [%s]' % s)
|
||||
return None
|
||||
|
||||
def get_id(self):
|
||||
if self.id:
|
||||
return self.id
|
||||
|
||||
tag = self.__get_element('thread_link')
|
||||
|
||||
text = tag.getAttribute('onclick')
|
||||
|
||||
regexp = re.compile("window.location='/thread.php\?id=(\d+)'")
|
||||
m = regexp.match(text)
|
||||
|
||||
if m:
|
||||
self.id = int(m.group(1))
|
||||
return self.id
|
||||
|
||||
warning('Unable to parse ID (%s)' % text)
|
||||
return 0
|
||||
|
||||
|
||||
class ContactListPage(PageBase):
|
||||
def on_loaded(self):
|
||||
self.items = []
|
||||
|
||||
form = self.document.getElementsByTagName('form')
|
||||
if not form:
|
||||
return
|
||||
|
||||
tags = form[0].childNodes[3].childNodes[1].childNodes
|
||||
|
||||
for tag in tags:
|
||||
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
|
||||
continue
|
||||
|
||||
if tag.hasAttribute('bgcolor'):
|
||||
continue
|
||||
|
||||
self.items += [ContactItem(tag)]
|
||||
|
||||
def get_contact_list(self):
|
||||
return self.items
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
# -*- 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 re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
from logging import error, warning
|
||||
from mechanize import FormNotFoundError
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
from weboob.backends.aum.exceptions import AdopteCantPostMail
|
||||
|
||||
class MailParser(object):
|
||||
|
||||
"""
|
||||
<td>
|
||||
<table width="100%">
|
||||
<tr valign="top">
|
||||
<td width="1" bgcolor="black">
|
||||
<td width="88" align="center">
|
||||
<table class=a onclick="window.location='romainn'" style="background:url(http://img.adopteunmec.com/img/bg_mini.png);width:74px;height:85px">
|
||||
<tr height=4>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr valign=top height=66>
|
||||
<td width=74 align=center style="-moz-opacity:0.5;opacity:0.5">
|
||||
<table class=a onclick="window.location='romainn'" style="width:66px;background-image:url(http://p7.adopteunmec.com/7/7/0/8/7/4/thumb0_3.jpg);background-repeat:no-repeat">
|
||||
<tr height=2><td></td></tr>
|
||||
<tr height=31 valign=top>
|
||||
<td width=2></td>
|
||||
<td align=left><img width=7 heght=7 src='http://img.adopteunmec.com/img/i/null.png'id=status_ /></td>
|
||||
</tr>
|
||||
<tr height=31 valign=bottom>
|
||||
<td width=2></td>
|
||||
<td width=63 align=right id=online_>
|
||||
<blnk><blink><img src="http://img.adopteunmec.com/img/online0.png" width=7 height=7 /></blink></blnk>
|
||||
<img src="http://img.adopteunmec.com/img/online1.png" width=25 height=10 />
|
||||
</td>
|
||||
<td width=3></td>
|
||||
</tr>
|
||||
<tr height=2><td></td></tr>
|
||||
<tr><td colspan=10 align=center style="font-size:10px;font-weight:bold;letter-spacing:-1px">Romain</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<p style="color:#444444;font-size:10px">27 octobre 2008, 01:11:32, nouveau
|
||||
<br />
|
||||
</p>
|
||||
<br>Moi en g<EFBFBD>n<EFBFBD>ral j'aime sortir tout simplement dans des bars, pour discuter avec mes amis et/ou coll<6C>gues, et rencontrer des gens. Sinon je fais de la guitare, et je d<>veloppe des projets perso.</p>
|
||||
</td>
|
||||
<td width="1" bgcolor="black">
|
||||
</tr>
|
||||
<tr height="6"><td width="1" bgcolor="black"><td colspan="2"></td><td width="1" bgcolor="black"></tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
"""
|
||||
|
||||
DATETIME_REGEXP = re.compile(u'([0-9]{1,2}) ([a-zéû]*) ([0-9]{4}), ([0-9]{2}):([0-9]{2}):([0-9]{2})(.*)')
|
||||
months = [u'', u'janvier', u'février', u'mars', u'avril', u'mai', u'juin', u'juillet', u'août', u'septembre', u'octobre', u'novembre', u'décembre']
|
||||
SMILEY_REGEXP = re.compile('http://s.adopteunmec.com/www/img/smile/([0-9]+).gif')
|
||||
smileys = {0: ':)',
|
||||
1: ':D',
|
||||
2: ':o',
|
||||
3: ':p',
|
||||
4: ';)',
|
||||
5: ':(',
|
||||
6: ':$',
|
||||
7: ':\'(',
|
||||
11: ':#',
|
||||
14: ':\\',
|
||||
15: ':|',
|
||||
10: '(L)',
|
||||
}
|
||||
|
||||
def __init__(self, thread_id, name, tr):
|
||||
# <td> <table> implicit<tbody> <tr>
|
||||
self.thread_id = thread_id
|
||||
self.date = None
|
||||
self.message_id = 0
|
||||
self.title = 'Discussion with %s' % name
|
||||
self.sender = name
|
||||
self.name = name
|
||||
self.tr = tr.childNodes[0].childNodes[1].childNodes[1].childNodes[0]
|
||||
|
||||
self.new = False
|
||||
|
||||
tds = self.tr.childNodes
|
||||
|
||||
counter = 0
|
||||
for td in tds:
|
||||
if not hasattr(td, 'tagName') or td.tagName != u'td':
|
||||
continue
|
||||
|
||||
counter += 1
|
||||
|
||||
if counter == 3:
|
||||
date = u''
|
||||
for child in td.childNodes[1].childNodes:
|
||||
if hasattr(child, 'data'):
|
||||
date += child.data
|
||||
self.parse_date(date)
|
||||
content = ''
|
||||
for c in td.childNodes[3].childNodes:
|
||||
if hasattr(c, 'data'):
|
||||
content += ''.join(c.data.split('\n')) # to strip \n
|
||||
elif hasattr(c, 'tagName'):
|
||||
if c.tagName == 'br':
|
||||
content += '\n'
|
||||
elif c.tagName == 'img' and c.hasAttribute('src'):
|
||||
m = self.SMILEY_REGEXP.match(c.getAttribute('src'))
|
||||
if m and self.smileys.has_key(int(m.group(1))):
|
||||
try:
|
||||
content += self.smileys[int(m.group(1))]
|
||||
except KeyError:
|
||||
error('Mail: unable to find this smiley: %s' % c.getAttribute('src'))
|
||||
self.content = content.strip()
|
||||
break
|
||||
|
||||
self.parse_profile_link()
|
||||
self.parse_from()
|
||||
|
||||
@property
|
||||
def date_int(self):
|
||||
return int(time.strftime('%Y%m%d%H%M%S', self.date.timetuple()))
|
||||
|
||||
def set_parent_message_id(self, date):
|
||||
self.parent_message_id = date
|
||||
|
||||
def parse_date(self, date_str):
|
||||
# To match regexp, we have to remove any return chars in string
|
||||
# before the status ('nouveau', 'lu', etc)
|
||||
date_str = date_str.strip()
|
||||
|
||||
m = self.DATETIME_REGEXP.match(date_str)
|
||||
if m:
|
||||
day = int(m.group(1))
|
||||
month = self.months.index(m.group(2))
|
||||
year = int(m.group(3))
|
||||
hour = int(m.group(4))
|
||||
minute = int(m.group(5))
|
||||
sec = int(m.group(6))
|
||||
# build datetime object with local timezone
|
||||
d = datetime(year, month, day, hour, minute, sec, tzinfo=tz.tzlocal())
|
||||
# then, convert it to UTC timezone
|
||||
d = d.astimezone(tz.tzutc())
|
||||
# and get timestamp
|
||||
self.date = d
|
||||
self.message_id = self.date_int
|
||||
self.id = '%s.%s' % (self.thread_id, self.message_id)
|
||||
|
||||
if m.group(7).find('nouveau') >= 0:
|
||||
self.new = True
|
||||
else:
|
||||
error('Error: unable to parse the datetime string "%s"' % date_str)
|
||||
|
||||
def parse_profile_link(self):
|
||||
tables = self.tr.getElementsByTagName('div')
|
||||
|
||||
for table in tables:
|
||||
if table.hasAttribute('class') and table.getAttribute('class') == 'mini' and table.hasAttribute('onclick'):
|
||||
|
||||
text = table.getAttribute('onclick')
|
||||
|
||||
regexp = re.compile("window.location='(.*)'")
|
||||
m = regexp.match(text)
|
||||
|
||||
if m:
|
||||
self.profile_link = m.group(1)
|
||||
self.signature = u'Profile: http://www.adopteunmec.com%s' % self.profile_link
|
||||
return
|
||||
|
||||
warning('Unable to find the profile URL in the message %s@%s' % (self.sender, self.id))
|
||||
|
||||
def parse_from(self):
|
||||
tds = self.tr.getElementsByTagName('div')
|
||||
|
||||
for td in tds:
|
||||
if not td.hasAttribute('class') or td.getAttribute('class') != 'mini_pseudo':
|
||||
continue
|
||||
|
||||
if td.childNodes:
|
||||
self.sender = td.childNodes[0].data
|
||||
|
||||
return
|
||||
|
||||
warning('Warning: unable to find from in the mail %s' % self.id)
|
||||
|
||||
class ContactThreadPage(PageBase):
|
||||
|
||||
"""
|
||||
<form name=sendMsg method="post" action="/thread.php?id=1567992">
|
||||
<table align=center width=700>
|
||||
<tr valign="top"><td width=370 align="left">
|
||||
<table width=370>
|
||||
<tr height=25 style="background:url(http://img.adopteunmec.com/img/bg_list1.gif)"><td colspan=4> <font color=#ff0198>Ecrire <20> <big><b>zoe</b></big></font></td></tr>
|
||||
<tr height=193 valign=top><td width=1 bgcolor=black></td><td colspan=2 bgcolor=white><textarea id=message name=message style="width:366px;height:190px;border:none"></textarea></td><td width=1 bgcolor=black></td></tr>
|
||||
<tr height=1><td width=1 bgcolor=black></td><td bgcolor="#d15c91" colspan=2></td><td width=1 bgcolor=black></td></tr>
|
||||
<tr height=1><td width=1 bgcolor=black></td><td bgcolor="#ffffff" colspan=2></td><td width=1 bgcolor=black></td></tr>
|
||||
<tr><td width=1 bgcolor=black></td><td bgcolor="#ffe8f3">
|
||||
<table><tr>
|
||||
<td width=8></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/0.gif" style="cursor:pointer" onclick="emote(':)')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/1.gif" style="cursor:pointer" onclick="emote(':D')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/2.gif" style="cursor:pointer" onclick="emote(':o')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/3.gif" style="cursor:pointer" onclick="emote(':p')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/4.gif" style="cursor:pointer" onclick="emote(';)')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/5.gif" style="cursor:pointer" onclick="emote(':(')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/6.gif" style="cursor:pointer" onclick="emote(':$')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/7.gif" style="cursor:pointer" onclick="emote(':\'(')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/11.gif" style="cursor:pointer" onclick="emote(':#')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/14.gif" style="cursor:pointer" onclick="emote(':\\')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/15.gif" style="cursor:pointer" onclick="emote(':|')"></td>
|
||||
<td width=23><img src="http://img.adopteunmec.com/img/smile/10.gif" style="cursor:pointer" onclick="emote('(L)')"></td>
|
||||
</tr></table>
|
||||
</td><td bgcolor="#ffe8f3" align=right><input type="image" src="http://img.adopteunmec.com/img/buttonSendMail.jpg" style="border:none"></td><td width=1 bgcolor=black></td></tr>
|
||||
<tr height=1><td width=1 bgcolor=black colspan=4></tr>
|
||||
</table>
|
||||
</td><td width=304 align="right">
|
||||
<iframe frameborder=0 scrolling=NO style='width:300px;height:250px;boder:0' src='http://graphs.adopteunmec.com/ads/index.php?i=5'></iframe>
|
||||
</td></tr>
|
||||
</table>
|
||||
<input type=hidden id=link name=link>
|
||||
</form>
|
||||
"""
|
||||
|
||||
def post(self, content):
|
||||
if isinstance(content, unicode):
|
||||
# LOL the data is Windows-1252 encoded, even if the charset label
|
||||
# of the page is ISO-8859-1.
|
||||
# That's crappy, but necessary if we want to use special chars.
|
||||
content = content.encode('Windows-1252', 'replace')
|
||||
|
||||
if len(content) < 3:
|
||||
raise AdopteCantPostMail("Your message is too short (minimum 3 chars)")
|
||||
|
||||
try:
|
||||
self.browser.select_form(name="sendMsg")
|
||||
self.browser['message'] = content
|
||||
|
||||
self.browser.submit() # submit current form
|
||||
except FormNotFoundError, e:
|
||||
error = u'Unknown error (%s)' % e
|
||||
p_list = self.document.getElementsByTagName('p')
|
||||
for p in p_list:
|
||||
if p.hasAttribute('align') and p.getAttribute('align') == 'center':
|
||||
error = p.childNodes[-1].data.encode('utf-8').strip()
|
||||
break
|
||||
|
||||
raise AdopteCantPostMail(error)
|
||||
|
||||
"""
|
||||
<table align=center style="width:700px;background:black">
|
||||
<tr style="height:1px">
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr bgcolor="#ffe9f6" valign="top">
|
||||
content
|
||||
</tr>
|
||||
...
|
||||
<tr height="25" style="background:url(http://img.adopteunmec.com/img/bg_list1.gif)"><td> <a href="/thread.php?id=1567992&see=all">voir tous les messages</a></td></tr>
|
||||
</table>
|
||||
|
||||
"""
|
||||
|
||||
id_regexp = re.compile("/thread.php\?id=([0-9]+)")
|
||||
|
||||
def on_loaded(self):
|
||||
self.mails = []
|
||||
|
||||
a_list = self.document.getElementsByTagName('a')
|
||||
self.id = 0
|
||||
for a in a_list:
|
||||
if a.hasAttribute('href'):
|
||||
m = self.id_regexp.match(a.getAttribute('href'))
|
||||
if m:
|
||||
self.id = int(m.group(1))
|
||||
break
|
||||
|
||||
self.name = ''
|
||||
big_list = self.document.getElementsByTagName('big')
|
||||
for big in big_list:
|
||||
for child in big.childNodes:
|
||||
if hasattr(child, 'tagName') and child.tagName == u'b':
|
||||
self.name = child.childNodes[0].data.strip()
|
||||
break
|
||||
|
||||
tables = self.document.getElementsByTagName('table')
|
||||
table = None
|
||||
for t in tables:
|
||||
if t.hasAttribute('style') and t.getAttribute('style') == 'width:700px;background:black':
|
||||
table = t
|
||||
break
|
||||
|
||||
if not table:
|
||||
# It is probably the 'sent' page
|
||||
return
|
||||
|
||||
for tag in table.childNodes[1].childNodes[1:]:
|
||||
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
|
||||
continue
|
||||
|
||||
if not tag.hasAttribute('valign'):
|
||||
continue
|
||||
|
||||
mail = MailParser(self.id, self.name, tag)
|
||||
|
||||
if self.mails:
|
||||
self.mails[-1].set_parent_message_id(mail.date_int)
|
||||
self.mails += [mail]
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
|
||||
class EditPhotoPage(PageBase):
|
||||
def add_photo(self, name, f):
|
||||
self.browser.select_form(name="form")
|
||||
self.browser.find_control('uploaded').add_file(f, 'image/jpeg', name)
|
||||
self.browser.submit()
|
||||
self.browser.openurl('http://www.adopteunmec.com/home.php')
|
||||
|
||||
class EditPhotoCbPage(PageBase):
|
||||
# Do nothing
|
||||
pass
|
||||
|
||||
class EditAnnouncePage(PageBase):
|
||||
def set_nickname(self, nickname):
|
||||
self.browser.select_form(name="form")
|
||||
self.browser['pseudo'] = nickname
|
||||
self.browser.submit()
|
||||
|
||||
def set_announce(self, **kwargs):
|
||||
self.browser.select_form(name="form")
|
||||
self.browser.set_field(kwargs, 'title')
|
||||
self.browser.set_field(kwargs, 'description', field='about1')
|
||||
self.browser.set_field(kwargs, 'lookingfor', field='about2')
|
||||
|
||||
self.browser.submit()
|
||||
|
||||
class EditDescriptionPage(PageBase):
|
||||
SHAPES = ['--', 'svelte', 'sportive', u'équilibrée', 'pulpeuse', u'généreuse', 'normale']
|
||||
HAIR_COLORS = ['--', 'blancs', 'gris', 'noirs', 'bruns', 'chatains', 'roux', 'blonds', 'platines', u'colorés']
|
||||
HAIR_SIZES = ['--', u'rasés', 'courts', 'mi-longs', 'longs']
|
||||
EYES = ['--', 'noirs', 'marrons', 'noisettes', 'bleus', 'verts', 'gris']
|
||||
ORIGINS = ['--', u'européennes', 'afro', 'maghrebines', 'asiatiques', u'métisses', 'eurasiennes', 'latines']
|
||||
STYLES = ['--', 'fashion', 'chic', 'sport', u'décontracté', 'rock', u'bohème', 'masculin', 'dark', 'excentrique', 'electro', 'skate']
|
||||
FOODS = ['--', 'mange de tout', 'piscovore', u'végétarien', u'végétalien', 'bio']
|
||||
DRINKS = ['--', 'jamais', 'de temps en temps', 'souvent', 'pilier de bar']
|
||||
SMOKES = ['--', u'ne tolère pas la fumée', u'tolère la fumée', 'fume de temps en temps', 'fume souvent']
|
||||
|
||||
def set_description(self, **kwargs):
|
||||
self.browser.select_form(name='form')
|
||||
|
||||
self.browser.set_field(kwargs, 'height', field='size', is_list=True)
|
||||
self.browser.set_field(kwargs, 'weight', is_list=True)
|
||||
self.browser.set_field(kwargs, 'shape', is_list=self.SHAPES)
|
||||
self.browser.set_field(kwargs, 'hair_color', is_list=self.HAIR_COLORS)
|
||||
self.browser.set_field(kwargs, 'hair_size', is_list=self.HAIR_SIZES)
|
||||
self.browser.set_field(kwargs, 'eyes', is_list=self.EYES)
|
||||
self.browser.set_field(kwargs, 'origins', is_list=self.ORIGINS)
|
||||
self.browser.set_field(kwargs, 'style', is_list=self.STYLES)
|
||||
self.browser.set_field(kwargs, 'food', is_list=self.FOODS)
|
||||
self.browser.set_field(kwargs, 'drink', is_list=self.DRINKS)
|
||||
self.browser.set_field(kwargs, 'smoke', is_list=self.SMOKES)
|
||||
|
||||
self.browser.submit()
|
||||
|
||||
class EditSexPage(PageBase):
|
||||
pass
|
||||
|
||||
class EditPersonalityPage(PageBase):
|
||||
pass
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# -*- 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 re
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
|
||||
class HomePage(PageBase):
|
||||
MYID_REGEXP = re.compile("http://www.adopteunmec.com/\?mid=(\d+)")
|
||||
|
||||
def get_my_id(self):
|
||||
fonts = self.document.getElementsByTagName('font')
|
||||
for font in fonts:
|
||||
m = self.MYID_REGEXP.match(font.firstChild.data)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
self.browser.logger.error("Error: Unable to find my ID")
|
||||
return 0
|
||||
|
||||
def __get_home_indicator(self, pos, what):
|
||||
tables = self.document.getElementsByTagName('table')
|
||||
for table in tables:
|
||||
if table.hasAttribute('style') and table.getAttribute('style') == 'background-color:black;background-image:url(http://s.adopteunmec.com/img/barmec.gif);background-repeat:no-repeat':
|
||||
fonts = table.getElementsByTagName('font')
|
||||
i = 0
|
||||
for font in fonts:
|
||||
if font.hasAttribute('color') and font.getAttribute('color') == '#ff0198':
|
||||
i += 1
|
||||
if i == pos:
|
||||
return int(font.firstChild.data)
|
||||
self.browser.logger.error(u'Could not parse number of %s' % what)
|
||||
return 0
|
||||
|
||||
def nb_available_charms(self):
|
||||
return self.__get_home_indicator(3, 'available charms')
|
||||
|
||||
def nb_godchilds(self):
|
||||
return self.__get_home_indicator(2, 'godchilds')
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
# -*- 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 re
|
||||
import mechanize
|
||||
|
||||
from weboob.tools.mech import ClientForm
|
||||
from weboob.tools.browser import BrowserIncorrectPassword
|
||||
from weboob.backends.aum.exceptions import AdopteBanned
|
||||
from weboob.capabilities.account import AccountRegisterError
|
||||
|
||||
from .base import PageBase
|
||||
from .home import HomePage
|
||||
from ..captcha import Captcha
|
||||
|
||||
class LoginPage(HomePage):
|
||||
def login(self, login, password):
|
||||
try:
|
||||
self.browser.select_form(name="form_login")
|
||||
except mechanize._mechanize.FormNotFoundError:
|
||||
return
|
||||
self.browser['login'] = login
|
||||
self.browser['password'] = password
|
||||
|
||||
self.browser.submit() # submit current form
|
||||
|
||||
class RegisterPage(PageBase):
|
||||
def on_loaded(self):
|
||||
display_errors = False
|
||||
for div in self.document.getElementsByTagName('div'):
|
||||
if div.getAttribute('class') == 'balloon':
|
||||
display_errors = True
|
||||
break
|
||||
|
||||
if not display_errors:
|
||||
return
|
||||
|
||||
errors = []
|
||||
for script in self.document.getElementsByTagName('script'):
|
||||
for child in script.childNodes:
|
||||
if child and child.data.find('dispErrors') >= 0:
|
||||
for m in re.finditer('"(\w+)": "(.*)",', child.data):
|
||||
errors.append(m.group(2))
|
||||
|
||||
raise AccountRegisterError(u'Unable to register account: %s' % ', '.join(errors))
|
||||
|
||||
def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country):
|
||||
"""
|
||||
Form name=register (#1)
|
||||
## ## __Name__________________ __Type___ __ID________ __Value__________________
|
||||
1 sent1time hidden (None)
|
||||
2 sex radio sex-0 [] of ['0', '1']
|
||||
3 birthday0 select birthday0 ['0'] of ['0', '1', '2', '3', '4', ' ...
|
||||
4 birthday1 select birthday1 ['0'] of ['0', '1', '2', '3', '4', ' ...
|
||||
5 birthday2 select birthday2 ['0'] of ['0', '1992', '1991', '1990 ...
|
||||
6 country select country ['0'] of ['0', 'fr', 'be', 'ch', 'ca']
|
||||
7 zip text zip
|
||||
8 email text email
|
||||
9 pass password pass
|
||||
10 pass_retype password pass_retype
|
||||
11 captcha text captcha
|
||||
12 swear_adult checkbox swear_adult [] of ['on']
|
||||
13 want_goods checkbox want_goods [] of ['on']
|
||||
"""
|
||||
c = Captcha(self.browser.openurl('/captcha.php'))
|
||||
|
||||
self.browser.select_form(name='register')
|
||||
self.browser.set_all_readonly(False)
|
||||
|
||||
try:
|
||||
self.browser['sex'] = [str(sex)]
|
||||
except ClientForm.ItemNotFoundError:
|
||||
raise AccountRegisterError('Please give a right sex! (1 or 0)')
|
||||
try:
|
||||
self.browser['birthday0'] = [str(birthday_d)]
|
||||
self.browser['birthday1'] = [str(birthday_m)]
|
||||
self.browser['birthday2'] = [str(birthday_y)]
|
||||
except ClientForm.ItemNotFoundError:
|
||||
raise AccountRegisterError('Please give a right birthday date!')
|
||||
try:
|
||||
self.browser['country'] = [str(country)]
|
||||
except ClientForm.ItemNotFoundError:
|
||||
raise AccountRegisterError('Please select a right country!')
|
||||
self.browser['zip'] = str(zipcode)
|
||||
self.browser['email'] = self.browser.username
|
||||
self.browser['pass'] = password
|
||||
self.browser['pass_retype'] = password
|
||||
self.browser['captcha'] = c.text
|
||||
self.browser['swear_adult'] = ['on']
|
||||
self.browser['want_goods'] = []
|
||||
|
||||
self.browser.submit()
|
||||
|
||||
class RegisterWaitPage(PageBase):
|
||||
pass
|
||||
|
||||
class RegisterConfirmPage(PageBase):
|
||||
pass
|
||||
|
||||
class RedirectPage(PageBase):
|
||||
def on_loaded(self):
|
||||
for link in self.browser.links():
|
||||
print link
|
||||
self.browser.location('/wait.php')
|
||||
|
||||
class BanPage(PageBase):
|
||||
def on_loaded(self):
|
||||
raise AdopteBanned('Your IP address is banned.')
|
||||
|
||||
class ShopPage(PageBase):
|
||||
def on_loaded(self):
|
||||
self.browser.location('http://www.adopteunmec.com/account.php')
|
||||
|
||||
class ErrPage(PageBase):
|
||||
def on_loaded(self):
|
||||
raise BrowserIncorrectPassword('Incorrect login/password')
|
||||
|
||||
class InvitePage(PageBase):
|
||||
MYID_REGEXP = re.compile("http://www.adopteunmec.com/\?mid=(\d+)")
|
||||
|
||||
def get_my_id(self):
|
||||
fonts = self.document.getElementsByTagName('font')
|
||||
for font in fonts:
|
||||
m = self.MYID_REGEXP.match(font.firstChild.data)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
self.browser.logger.error("Error: Unable to find my ID")
|
||||
return 0
|
||||
|
|
@ -1,446 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
from weboob.tools.ordereddict import OrderedDict
|
||||
|
||||
from copy import deepcopy
|
||||
from logging import warning
|
||||
import re
|
||||
|
||||
class FieldBase:
|
||||
|
||||
def __init__(self, key):
|
||||
self.key = key
|
||||
|
||||
def put_value(self, d, value):
|
||||
raise NotImplementedError
|
||||
|
||||
class FieldString(FieldBase):
|
||||
def put_value(self, d, value):
|
||||
d[self.key] = unicode(value)
|
||||
|
||||
class FieldList(FieldBase):
|
||||
def put_value(self, d, value):
|
||||
d[self.key] = value.split(', ')
|
||||
|
||||
class FieldWideList(FieldBase):
|
||||
|
||||
def put_value(self, d, value):
|
||||
d[self.key] += [value]
|
||||
|
||||
class FieldOld(FieldBase):
|
||||
regexp = re.compile(u'([0-9]+) ans( \(Née le ([0-9]+) ([^ ]+) ([0-9]+)\))?')
|
||||
month2i = ['', 'janvier', u'février', 'mars', 'avril', 'mai', 'juin', 'juillet', u'août', 'septembre', 'octobre', 'novembre', u'décembre']
|
||||
|
||||
def put_value(self, d, value):
|
||||
m = self.regexp.match(value)
|
||||
if not m:
|
||||
return
|
||||
|
||||
d[self.key] = int(m.group(1))
|
||||
if not m.group(2):
|
||||
return
|
||||
|
||||
try:
|
||||
d['birthday'] = (int(m.group(3)),
|
||||
self.month2i.index(m.group(4)),
|
||||
int(m.group(5)))
|
||||
except ValueError, e:
|
||||
print str(e)
|
||||
|
||||
class FieldLocation(FieldBase):
|
||||
location = re.compile('(?P<location>.+?)( \((?P<zipcode>[0-9]{5})\))?, (?P<country>.*)')
|
||||
|
||||
def __init__(self):
|
||||
FieldBase.__init__(self, '')
|
||||
def put_value(self, d, value):
|
||||
# TODO: determine distance, or something like
|
||||
m = self.location.match(value)
|
||||
if m:
|
||||
for field in ('country', 'location', 'zipcode'):
|
||||
d[field] = m.groupdict().get(field)
|
||||
else:
|
||||
warning('Unable to parse the location "%s"' % value)
|
||||
d['location'] = unicode(value)
|
||||
|
||||
class FieldMeasurements(FieldBase):
|
||||
height = re.compile('([0-9]{1,3}) cm')
|
||||
weight = re.compile('([0-9]{1,3}) kg')
|
||||
# TODO: parse third parameter
|
||||
|
||||
def __init__(self):
|
||||
FieldBase.__init__(self, '')
|
||||
def put_value(self, d, value):
|
||||
for s in value.split(', '):
|
||||
m = self.height.match(s)
|
||||
if m:
|
||||
d['height'] = int(m.group(1))
|
||||
continue
|
||||
m = self.weight.match(s)
|
||||
if m:
|
||||
d['weight'] = int(m.group(1))
|
||||
continue
|
||||
if d['height'] and d['weight']:
|
||||
bmi = (d['weight']/float(pow(d['height']/100.0, 2)))
|
||||
if bmi < 15.5:
|
||||
d['fat'] = 'severely underweight'
|
||||
elif bmi < 18.4:
|
||||
d['fat'] = 'underweight'
|
||||
elif bmi < 24.9:
|
||||
d['fat'] = 'normal'
|
||||
elif bmi < 30:
|
||||
d['fat'] = 'overweight'
|
||||
else:
|
||||
d['fat'] = 'obese'
|
||||
d['BMI'] = bmi
|
||||
|
||||
class FieldParticularSignes(FieldBase):
|
||||
def __init__(self): FieldBase.__init__(self, '')
|
||||
def put_value(self, d, value):
|
||||
for s in value.split(', '):
|
||||
if s.find('tatouages') >= 0:
|
||||
d['tatoos'] = True
|
||||
elif s.find('piercing') >= 0:
|
||||
d['piercing'] = True
|
||||
elif s.find('lunettes') >= 0:
|
||||
d['glasses'] = True
|
||||
elif s.find('rousseur') >= 0:
|
||||
d['freckle'] = True
|
||||
|
||||
class ProfilePage(PageBase):
|
||||
empty_table = OrderedDict((
|
||||
('details', OrderedDict((
|
||||
('old', 0),
|
||||
('birthday', (0,0,0)),
|
||||
('zipcode', 0),
|
||||
('location', ''),
|
||||
('country', ''),
|
||||
('eyes', ''),
|
||||
('hairs', []),
|
||||
('height', 0),
|
||||
('weight', 0),
|
||||
('BMI', 0),
|
||||
('fat', ''),
|
||||
('from', ''),
|
||||
('tatoos', False),
|
||||
('piercing', False),
|
||||
('freckle', False),
|
||||
('glasses', False),
|
||||
('job', ''),
|
||||
('style', ''),
|
||||
('alimentation', ''),
|
||||
('alcool', ''),
|
||||
('tabac', ''),
|
||||
))),
|
||||
('liking', OrderedDict((
|
||||
('activities', ''),
|
||||
('music', []),
|
||||
('cinema', []),
|
||||
('books', []),
|
||||
('tv', []),
|
||||
))),
|
||||
('sex', OrderedDict((
|
||||
('underwear', []),
|
||||
('top', []),
|
||||
('bottom', []),
|
||||
('interval', ''),
|
||||
('favorite', []),
|
||||
('practices', []),
|
||||
('toys', []),
|
||||
))),
|
||||
('personality', OrderedDict((
|
||||
('snap', ''),
|
||||
('exciting', ''),
|
||||
('hate', ''),
|
||||
('vices', ''),
|
||||
('assets', ''),
|
||||
('fantasies', ''),
|
||||
('is', []),
|
||||
))),
|
||||
))
|
||||
|
||||
tables = {'tab_0': 'details',
|
||||
'tab_1': 'liking',
|
||||
'tab_2': 'sex',
|
||||
'tab_3': 'personality'
|
||||
}
|
||||
|
||||
fields = {'details': {'Age': FieldOld('old'),
|
||||
u'Réside à': FieldLocation(),
|
||||
'Yeux': FieldString('eyes'),
|
||||
'Cheveux': FieldList('hairs'),
|
||||
'Mensurations ': FieldMeasurements(),
|
||||
'Origines ': FieldString('from'),
|
||||
'Signes particuliers ': FieldParticularSignes(),
|
||||
'Style ': FieldString('style'),
|
||||
'Profession ': FieldString('job'),
|
||||
'Alimentation': FieldString('alimentation'),
|
||||
'Alcool': FieldString('alcool'),
|
||||
'Tabac': FieldString('tabac'),
|
||||
},
|
||||
'liking': {'Hobbies ': FieldString('activities'),
|
||||
'Musique ': FieldWideList('music'),
|
||||
u'Cinéma': FieldWideList('cinema'),
|
||||
'Livres ': FieldWideList('books'),
|
||||
u'Télé': FieldWideList('tv'),
|
||||
},
|
||||
'sex': {u'Sous-v\xeatements ': FieldList('underwear'),
|
||||
'... en haut ': FieldList('top'),
|
||||
'... en bas ': FieldList('bottom'),
|
||||
u'Fréquence idéale des rapports sexuels ':
|
||||
FieldString('interval'),
|
||||
u'Position favorite ': FieldList('favorite'),
|
||||
'Pratiques sexuelles ': FieldList('practices'),
|
||||
u'Accessoires préférés ':FieldList('toys'),
|
||||
u'Ce qui se cache en dessous ':
|
||||
FieldList('underwear'),
|
||||
u"Ce qui m'\xe9moustille ":
|
||||
FieldList('favorite'),
|
||||
u"Au lit j'aime ": FieldList('practices'),
|
||||
u'Mes accessoires ': FieldList('toys'),
|
||||
},
|
||||
'personality': {u'Ça la fait craquer ': FieldString('snap'),
|
||||
u'Ça l\'excite ': FieldString('exciting'),
|
||||
u'Elle déteste ': FieldString('hate'),
|
||||
'Ses vices ': FieldString('vices'),
|
||||
'Ses atouts ': FieldString('assets'),
|
||||
'Ses fantasmes ': FieldString('fantasies'),
|
||||
'Elle est ': FieldList('is'),
|
||||
},
|
||||
}
|
||||
|
||||
ID_REGEXP = re.compile('(charm|addBasket|openAlbum)\(([0-9]+)(,[\s\'\d]+)?\)')
|
||||
PHOTO_REGEXP = re.compile('http://(s|p)([0-9]+)\.adopteunmec\.com/(.*)')
|
||||
|
||||
STATS2ID = {'visites': 'visits',
|
||||
'charmes': 'charms',
|
||||
'paniers': 'baskets',
|
||||
'mails': 'mails',
|
||||
'POPULARIT': 'score',
|
||||
}
|
||||
STATS_VALUE_REGEXP = re.compile('([0-9\s]+).*')
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.name, unicode):
|
||||
name = self.name.encode('utf-8', 'backslashreplace')
|
||||
else:
|
||||
name = self.name
|
||||
return '<Profile name="%s">' % name
|
||||
|
||||
def on_loaded(self):
|
||||
self.name = u''
|
||||
self.description = u''
|
||||
self.table = deepcopy(self.empty_table)
|
||||
self.id = 0
|
||||
self.photos = []
|
||||
self.status = ''
|
||||
self.stats = OrderedDict((
|
||||
('score', 0),
|
||||
('visits', 0),
|
||||
('charms', 0),
|
||||
('baskets', 0),
|
||||
('mails', 0),
|
||||
))
|
||||
|
||||
divs = self.document.getElementsByTagName('td')
|
||||
for div in divs:
|
||||
if (div.hasAttribute('style') and
|
||||
div.getAttribute('style') == "color:#ffffff;font-size:32px;font-weight:bold;letter-spacing:-2px" and
|
||||
hasattr(div.firstChild, 'data')):
|
||||
if len(div.childNodes) > 1:
|
||||
self.name = div.childNodes[1].data
|
||||
if (div.hasAttribute('style') and
|
||||
div.getAttribute('style') == "font-size:12px;font-weight:bold" and
|
||||
hasattr(div.firstChild, 'data')):
|
||||
self.status = div.childNodes[-1].data.strip()
|
||||
if div.hasAttribute('background'):
|
||||
m = self.PHOTO_REGEXP.match(div.getAttribute('background'))
|
||||
if m:
|
||||
self.photos.append(dict(url=re.sub(u'thumb[0-2]_', u'image', div.getAttribute('background')),
|
||||
hidden=False))
|
||||
if div.hasAttribute('width') and str(div.getAttribute('width')) == '226':
|
||||
trs = div.getElementsByTagName('tr')
|
||||
for tr in trs:
|
||||
tds = tr.getElementsByTagName('td')
|
||||
if len(tds) > 2 and hasattr(tds[2].firstChild, 'data'):
|
||||
label = tds[0].firstChild.data
|
||||
value = tds[2].firstChild.data
|
||||
elif len(tds) == 2:
|
||||
label = unicode(tds[0].childNodes[1].data)
|
||||
value = tds[1].childNodes[1].data
|
||||
else:
|
||||
continue
|
||||
|
||||
m = self.STATS_VALUE_REGEXP.match(value)
|
||||
if m and self.STATS2ID.has_key(label):
|
||||
self.stats[self.STATS2ID[label]] = int(m.group(1).replace(' ', ''))
|
||||
|
||||
divs = self.document.getElementsByTagName('div')
|
||||
for div in divs:
|
||||
if div.hasAttribute('id'):
|
||||
if div.getAttribute('id') == 'about_div':
|
||||
self.parse_description(div)
|
||||
|
||||
if div.getAttribute('id').startswith('tab_'):
|
||||
self.parse_table(div)
|
||||
|
||||
for tag in ('img', 'td'):
|
||||
imgs = self.document.getElementsByTagName(tag)
|
||||
for img in imgs:
|
||||
if img.hasAttribute('onclick'):
|
||||
m = self.ID_REGEXP.match(img.getAttribute('onclick'))
|
||||
if m:
|
||||
self.id = int(m.group(2))
|
||||
break
|
||||
if self.id:
|
||||
break
|
||||
|
||||
if len(self.photos) == 0:
|
||||
return
|
||||
|
||||
# find hidden photos.
|
||||
photo_regex = re.compile('(?P<base_url>http://.+\.adopteunmec\.com/.+/)image(?P<id>.+)\.jpg')
|
||||
photo_max_id = max(int(photo_regex.match(photo['url']).groupdict()['id']) for photo in self.photos)
|
||||
base_url = photo_regex.match(self.photos[0]['url']).groupdict()['base_url']
|
||||
for id in xrange(1, photo_max_id + 1):
|
||||
url = '%simage%s.jpg' % (base_url, id)
|
||||
if not url in [photo['url'] for photo in self.photos]:
|
||||
self.photos.append(dict(url=url, hidden=True))
|
||||
|
||||
def parse_description(self, div):
|
||||
# look for description
|
||||
|
||||
description = ''
|
||||
for c in div.childNodes:
|
||||
if hasattr(c, 'data'):
|
||||
description += ''.join(c.data.split('\n')) # to strip \n
|
||||
elif hasattr(c, 'tagName') and c.tagName == 'br':
|
||||
description += '\n'
|
||||
elif hasattr(c, 'tagName') and c.tagName == 'i':
|
||||
description += ''.join(c.childNodes[0].data.split('\n'))
|
||||
|
||||
self.description = description.strip()
|
||||
|
||||
def parse_table(self, div):
|
||||
d = self.table[self.tables[div.getAttribute('id')]]
|
||||
fields = self.fields[self.tables[div.getAttribute('id')]]
|
||||
table = div.getElementsByTagName('table')[1]
|
||||
|
||||
field1 = None
|
||||
field2 = None
|
||||
|
||||
for tr in table.getElementsByTagName('tr'):
|
||||
tds = tr.getElementsByTagName('td')
|
||||
if len(tds) != 2:
|
||||
continue
|
||||
|
||||
label1 = ''
|
||||
label2 = ''
|
||||
value1 = ''
|
||||
value2 = ''
|
||||
# Check for first column
|
||||
if len(tds[0].childNodes) > 0:
|
||||
b = len(tds[0].childNodes) > 2 and tds[0].childNodes[2]
|
||||
if b and hasattr(b, 'tagName') and b.tagName == 'b':
|
||||
for child in b.childNodes:
|
||||
label1 += child.data
|
||||
else:
|
||||
for child in tds[0].childNodes:
|
||||
if child.data != u'\xa0': # strip nbsp
|
||||
value1 += child.data
|
||||
value2 = value2.strip()
|
||||
|
||||
# Check for second column
|
||||
if len(tds[1].childNodes) > 0:
|
||||
b = tds[1].childNodes[0]
|
||||
if b and hasattr(b, 'tagName') and b.tagName == 'b':
|
||||
for child in b.firstChild.childNodes:
|
||||
label2 += child.data
|
||||
else:
|
||||
for child in tds[1].childNodes:
|
||||
if hasattr(child, 'data') and child.data != u'\xa0': # strip nbsp
|
||||
value2 += child.data
|
||||
|
||||
if label1 and value2:
|
||||
# This is a typically tuple of key/value on the line.
|
||||
try:
|
||||
fields[label1].put_value(d, value2)
|
||||
except KeyError:
|
||||
self.logger.warning('Unable to find "%s" (%s)' % (label1, repr(label1)))
|
||||
elif label1 and label2:
|
||||
# two titles, so there will have a list of value in
|
||||
# next lines on each columns
|
||||
field1 = fields[label1]
|
||||
field2 = fields[label2]
|
||||
elif not label1 and not label1:
|
||||
# two values, so it is a line of values
|
||||
if field1 and value1:
|
||||
field1.put_value(d, value1)
|
||||
if field2 and value2:
|
||||
field2.put_value(d, value2)
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def get_description(self):
|
||||
return self.description
|
||||
|
||||
def get_table(self):
|
||||
return self.table
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def get_photos(self):
|
||||
return self.photos
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
def is_online(self):
|
||||
return self.status.find('en ligne') >= 0
|
||||
|
||||
def get_stats(self):
|
||||
return self.stats
|
||||
|
||||
def get_profile_text(self):
|
||||
body = u'Status: %s' % unicode(self.status)
|
||||
if self.photos:
|
||||
body += u'\nPhotos:'
|
||||
for photo in self.photos:
|
||||
body += u'\n\t\t%s%s' % (unicode(photo['url']), (' (hidden)' if photo['hidden'] else ''))
|
||||
body += u'\nStats:'
|
||||
for label, value in self.get_stats().iteritems():
|
||||
body += u'\n\t\t%-15s %s' % (label + ':', value)
|
||||
body += u'\n\nInformations:'
|
||||
for section, d in self.get_table().iteritems():
|
||||
body += u'\n\t%s\n' % section
|
||||
for key, value in d.items():
|
||||
key = '%s:' % key
|
||||
if isinstance(value, list):
|
||||
body += u'\t\t%-15s %s\n' % (key, u', '.join(unicode(s) for s in value))
|
||||
elif isinstance(value, float):
|
||||
body += u'\t\t%-15s %.2f\n' % (key, value)
|
||||
else:
|
||||
body += u'\t\t%-15s %s\n' % (key, unicode(value))
|
||||
body += u'\n\nDescription:\n%s' % unicode(self.get_description())
|
||||
|
||||
return body
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
import re
|
||||
|
||||
class ProfilesListBase(PageBase):
|
||||
|
||||
PROFILE_REGEXP = re.compile(".*window\.location='(.*)'")
|
||||
PHOTO_REGEXP = re.compile(".*background-image:url\(([A-Za-z0-9_\-:/\.]+)\).*")
|
||||
WITHOUT_PHOTO = 'http://s.adopteunmec.com/img/thumb0.gif'
|
||||
SHOW_WITHOUT_PHOTO = True
|
||||
|
||||
def on_loaded(self):
|
||||
|
||||
self.id_list = []
|
||||
|
||||
a_list = self.document.getElementsByTagName('div')
|
||||
for a in a_list:
|
||||
if a.hasAttribute('onclick') and a.hasAttribute('class') and a.getAttribute('class') in ('small', 'mini'):
|
||||
m = self.PROFILE_REGEXP.match(a.getAttribute('onclick'))
|
||||
if m:
|
||||
url = m.group(1).split('/')[-1]
|
||||
m = self.PHOTO_REGEXP.match(a.getElementsByTagName('div')[0].getAttribute('style'))
|
||||
if url != 'home.php' and not url in self.id_list and \
|
||||
m and (self.SHOW_WITHOUT_PHOTO or m.group(1) != self.WITHOUT_PHOTO):
|
||||
self.id_list.append(url)
|
||||
|
||||
def get_profiles_ids(self):
|
||||
return set(self.id_list)
|
||||
|
||||
def get_profiles_ids_list(self):
|
||||
return self.id_list
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.profileslist_base import ProfilesListBase
|
||||
|
||||
class SearchPage(ProfilesListBase):
|
||||
SHOW_WITHOUT_PHOTO = False
|
||||
|
||||
def search(self, **kwargs):
|
||||
self.browser.select_form(name="form")
|
||||
self.browser.set_all_readonly(False)
|
||||
|
||||
self.browser.set_field(kwargs, 'ageMin', is_list=True)
|
||||
self.browser.set_field(kwargs, 'ageMax', is_list=True)
|
||||
self.browser.set_field(kwargs, 'country', is_list=True)
|
||||
self.browser.set_field(kwargs, 'dist', is_list=True)
|
||||
self.browser.set_field(kwargs, 'nickname', field='pseudo')
|
||||
self.browser.set_field(kwargs, 'save', value='true')
|
||||
# XXX ho, it doesn't work anymore :(
|
||||
#self.browser['originsV[]'] = ['1'] # excludes niggers (it doesn't work :( )
|
||||
|
||||
self.browser.submit()
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# -*- 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/>.
|
||||
|
||||
|
||||
from weboob.backends.aum.pages.base import PageBase
|
||||
from weboob.backends.aum.exceptions import AdopteWait
|
||||
from time import sleep
|
||||
|
||||
class WaitPage(PageBase):
|
||||
|
||||
def on_loaded(self):
|
||||
raise AdopteWait()
|
||||
|
||||
def check(self):
|
||||
result = self.browser.openurl('http://www.adopteunmec.com/fajax_checkEnter.php?anticache=0.46168455299380795').read()
|
||||
return result == 'Ok'
|
||||
|
||||
def process_wait(self):
|
||||
while not self.check():
|
||||
sleep(10)
|
||||
|
||||
self.browser.location('/home.php')
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue