new version of aum backend

This commit is contained in:
Romain Bignon 2011-09-20 21:01:27 +02:00
commit d714250cc8
21 changed files with 1408 additions and 2000 deletions

View file

@ -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
View 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 &agrave; perdre, je n'ai ni MSN ni Facebook, je consid&egrave;re qu'on appr&eacute;cie davantage la discussion face &agrave; face autour d'un verre que dans les yeux de son &eacute;cran.\r<br>\r<br>Bon et ne venez que si vous avez quelque chose &agrave; 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'&eacute;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&eacute;tournement \x97 La Classe Am&eacute;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&eacute; 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&egrave;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 &ccedil;a n'emp&ecirc;che pas son bidou, ceci dit c'est planqu&eacute; 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&eacute;t&eacute;rinaire m'avait dit apr&egrave;s la castration de Futex qu'il fallait faire attention &agrave; son poids, du coup &ccedil;a m'a tellement vex&eacute; que j'ai fais attention au point qu'il est sans doute m&ecirc;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&eacute; depuis septembre ! un peu moins de boulot alors j'y traine !!\r\n\r\nEt toi &ccedil;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 &ecirc;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&egrave;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&eacute;&acirc;tre d&eacute;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 &amp; 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&eacute; sp&eacute;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'}}

View file

@ -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,
}

View file

@ -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,

View file

@ -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):

View 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

View file

@ -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

View file

@ -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)

View file

@ -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()

View file

@ -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')

View file

@ -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

View file

@ -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">&nbsp;</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&nbsp;<img src="http://img.adopteunmec.com/img/ico_mail0.gif" />&nbsp;&nbsp;&nbsp;
</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"/>&nbsp;</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

View file

@ -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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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]

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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')