diff --git a/weboob/applications/havesex/havesex.py b/weboob/applications/havesex/havesex.py index ed5d46ae..8f4f3a05 100644 --- a/weboob/applications/havesex/havesex.py +++ b/weboob/applications/havesex/havesex.py @@ -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) diff --git a/weboob/backends/aum/API.txt b/weboob/backends/aum/API.txt new file mode 100644 index 00000000..e5851e02 --- /dev/null +++ b/weboob/backends/aum/API.txt @@ -0,0 +1,861 @@ + Adopte un Mec API + ------------------ + +Constants: + +APIKEY = fb0123456789abcd +URL = http://api.adopteunmec.com/api.php + + +ME Commands +=========== + +me.login +--------- + +Parameters: + - login + - pass + +Errors: + - 1.1.1 : invalid login + +Return value: + {u'errors': [], + u'result': {u'baskets': u'384', + u'events': {u'lastBasket': {u'alert': u'1', + u'birthday': u'1989-02-04', + u'city': u'Paris', + u'country': u'fr', + u'cover': u'1', + u'id': u'14471939', + u'isBan': False, + u'isOnline': False, + u'last_cnx': u'2011-09-20 13:56:36', + u'list5': u'0', + u'login': u'kloo', + u'mod_level': u'0', + u'path': u'9/3/9/1/7/4/4/', + u'pseudo': u'Kloolloo', + u'region': u'11', + u'sex': 1, + u'shard': 9, + u'style': u'0', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/14471939/Kloolloo', + u'zip': u'75001'}, + u'lastChat': {u'alert': u'1', + u'birthday': u'1987-10-10', + u'city': u'Paris 18e Arrondissement', + u'country': u'fr', + u'cover': u'0', + u'id': u'13280274', + u'isBan': False, + u'isOnline': False, + u'last_cnx': u'2010-11-20 03:37:00', + u'list5': u'0', + u'login': u'#c1a63bda81cc03ccdf080ca6e003919e', + u'mod_level': u'0', + u'path': u'4/7/2/0/8/2/3/', + u'pseudo': u'Katie Lee', + u'region': u'11', + u'sex': 1, + u'shard': 4, + u'style': u'0', + u'table': u'adopteun.girls_coma', + u'url': u'/api.php?member/view/13280274/KatieLee', + u'zip': u'75018'}, + u'lastFlash': {u'alert': u'1', + u'birthday': u'1983-12-20', + u'city': u'Longjumeau', + u'country': u'fr', + u'cover': u'1', + u'id': u'13883475', + u'isBan': False, + u'isOnline': True, + u'last_cnx': u'2011-09-20 18:52:13', + u'list5': u'0', + u'login': u'@', + u'mod_level': u'0', + u'path': u'5/7/4/3/8/8/3/', + u'pseudo': u'Pas Tjs Sage', + u'region': u'11', + u'sex': 1, + u'shard': 5, + u'style': u'2', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/13883475/PasTjsSage', + u'zip': u'91160'}, + u'lastMail': {u'alert': u'1', + u'birthday': u'1989-04-28', + u'city': u'Paris 8e Arrondissement', + u'country': u'fr', + u'cover': u'5', + u'id': u'13268738', + u'isBan': False, + u'isOnline': True, + u'last_cnx': u'2011-09-20 19:24:14', + u'list5': u'0', + u'login': u'@13268738', + u'mod_level': u'0', + u'path': u'8/3/7/8/6/2/3/', + u'pseudo': u'Th\xe9na', + u'region': u'11', + u'sex': 1, + u'shard': 8, + u'style': u'0', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/13268738/Thna', + u'zip': u'75008'}, + u'lastVisit': {u'alert': u'1', + u'birthday': u'1988-02-27', + u'city': u'Paris', + u'country': u'fr', + u'cover': u'1', + u'id': u'14477637', + u'isBan': False, + u'isOnline': True, + u'last_cnx': u'2011-09-20 18:31:00', + u'list5': u'0', + u'login': u'@', + u'mod_level': u'0', + u'path': u'7/3/6/7/7/4/4/', + u'pseudo': u'Lolly', + u'region': u'11', + u'sex': 1, + u'shard': 7, + u'style': u'4', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/14477637/Lolly', + u'zip': u'75005'}}, + u'flashs': 10, + u'mails': u'731', + u'me': {u'about1': u"Je n'ai pas de temps à perdre, je n'ai ni MSN ni Facebook, je considère qu'on apprécie davantage la discussion face à face autour d'un verre que dans les yeux de son écran.\r
\r
Bon et ne venez que si vous avez quelque chose à me dire, je n'envoie jamais de charmes.", + u'about2': u'', + u'admin': u'0', + u'alert': u'0', + u'alert_add': u'6', + u'birthday': u'1986-08-13', + u'books': u"Orwell (1984, La ferme des animaux)
Barjavel (La nuit des temps, Le voyageur imprudent, Ravage, \x85)
Boris Vian (J'irai cracher sur vos tombes, L'écume des jours\x85)
Bukowski, Desproges, San Antonio
Sartre, Le Canard", + u'cat': u'1', + u'checks1': u'0', + u'checks2': u'16686', + u'checks3': u'0', + u'checks4': u'0', + u'checks5': u'0', + u'checks6': u'2', + u'checks7': u'0', + u'cinema': u'Le Grand Détournement \x97 La Classe Américaine
V pour Vendetta, Pulp Fiction, The Truman Show
Eternal Sunshine of the Spotless Mind, Match Point
Idiocracy, The Big Lebowski, La cité de la peur
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
Metallica, Iron Maiden, Accept
Slash's Snakepit, Queen, Deep Purple
Led Zeppelin, Rolling Stones
Brassens, Souchon, Brel, Vian", + u'origins': u'1', + u'pass': u'8f3fa83cec9a243ae53c1337d2b5e1cf', + u'path': u'9/3/6/0/5/4/2/', + u'phone': u'-', + u'pictures': [{u'file': u'5', + u'height': u'427', + u'id': u'7315391', + u'md5': u'859fcd2e425617c33c16d6a1bc510ad3', + u'member': u'22450639', + u'rank': u'1', + u'valid': u'1578737', + u'width': u'500'}], + u'pseudo': u'Nazification', + u'region': u'11', + u'sex': 0, + u'shape': u'1', + u'shard': 9, + u'size': u'175', + u'smoke': u'2', + u'style': u'0', + u'subregion': u'76', + u'table': u'adopteun.boys', + u'texts1': u'', + u'texts2': u'', + u'texts3': u'', + u'texts4': u'', + u'texts5': u'', + u'texts6': u'', + u'title': u'', + u'tvs': u'Je ne possède pas la TV



', + 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=, + +Return value: + {u'errors': [], + u'result': {u'count': 1, + u'threads': [{u'cat': u'0', + u'date': u'2011-09-20 19:22:11', + u'id': u'11132125', + u'id_from': u'13268738', + u'id_to': u'22450639', + u'member': {u'alert': u'1', + u'birthday': u'1989-04-28', + u'city': u'Paris 8e Arrondissement', + u'country': u'fr', + u'cover': u'5', + u'id': u'13268738', + u'isBan': False, + u'isOnline': True, + u'last_cnx': u'2011-09-20 19:54:15', + u'list5': u'0', + u'login': u'@13268738', + u'mod_level': u'0', + u'path': u'8/3/7/8/6/2/3/', + u'pseudo': u'Th\xe9na', + u'region': u'11', + u'sex': 1, + u'shard': 8, + u'style': u'0', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/13268738/Thna', + u'zip': u'75008'}, + u'message': u'0', + u'status': u'0', + u'title': u"J'aime bien les \xe9l\xe9phants. Toi ?"}], + u'token': u'0f70c31bc6d05d45bee64e6f2eab9b537640f2f8'}} + +message.thread +-------------- + +Parameters: + - memberId + - count + +Return value: + {u'result': {u'popu': u'15910', + u'thread': {u'isNew': False, + u'member': {u'alert': u'3', + u'birthday': u'1987-04-22', + u'city': u'Maisons-Alfort', + u'country': u'fr', + u'cover': u'6', + u'id': u'14022243', + u'isBan': False, + u'isOnline': False, + u'last_cnx': u'2011-09-20 18:18:00', + u'list5': u'0', + u'login': u'@', + u'mod_level': u'0', + u'path': u'3/4/2/2/2/0/4/', + u'pseudo': u'Sophkipeut', + u'region': u'11', + u'sex': 1, + u'shard': 3, + u'style': u'0', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/14022243/Sophkipeut', + u'zip': u'94700'}, + u'messages': [{u'date': u'2011-09-19 17:52:03', + u'id': u'46583458', + u'id_from': u'14022243', + u'id_to': u'23185402', + u'message': u"Lol, moi j'ai fait attention, mais bon ça n'empêche pas son bidou, ceci dit c'est planqué par ses loooooooooooong poils ^^", + u'src': u'', + u'title': u"Lol, moi j'ai fait attention, mais bon \xe7a n'emp\xea..."}, + {u'date': u'2011-09-19 17:49:39', + u'id': u'47467748', + u'id_from': u'23185402', + u'id_to': u'14022243', + u'message': u"Ah oui, justement le vétérinaire m'avait dit après la castration de Futex qu'il fallait faire attention à son poids, du coup ça m'a tellement vexé que j'ai fais attention au point qu'il est sans doute même trop maigre.", + u'src': u'', + u'title': u"Ah oui, justement le v\xe9t\xe9rinaire m'avait dit apr\xe8..."}], + u'remoteStatus': u'2', + u'status': u'1', + u'warning': 0}, + u'token': u'dbeccf96256d4f11991626707881fdba28f54d73'}} + +message.new +----------- + +Parameters: + - memberId + - message + +Return value: + {u'errors': [], + u'result': {u'thread': {u'isNew': False, + u'member': {u'alert': u'1', + u'birthday': u'1986-12-08', + u'city': u'Rosny-sous-Bois', + u'country': u'fr', + u'cover': u'6', + u'id': u'11099536', + u'isBan': False, + u'isOnline': False, + u'last_cnx': u'2011-09-20 16:26:32', + u'list5': u'0', + u'login': u'@11099536', + u'mod_level': u'0', + u'path': u'6/3/5/9/9/0/1/', + u'pseudo': u'Debo', + u'region': u'11', + u'sex': 1, + u'shard': 6, + u'style': u'0', + u'table': u'adopteun.girls', + u'url': u'/api.php?member/view/11099536/Debo', + u'zip': u'93110'}, + u'messages': [{u'date': u'2011-09-20 20:09:07', + u'id': u'46588573', + u'id_from': u'23185402', + u'id_to': u'11099536', + u'message': u'Coucou', + u'src': u'iphone', + u'title': u'Coucou'}, + {u'date': u'2011-09-20 16:27:34', + u'id': u'46638469', + u'id_from': u'11099536', + u'id_to': u'23185402', + u'message': u"Coucou pour tout t'avouer je ne m'y etais pas connecté depuis septembre ! un peu moins de boulot alors j'y traine !!\r\n\r\nEt toi ça va? tu devais pas partir au canada? tu es deja revenu.?", + u'src': u'', + u'title': u"Coucou pour tout t'avouer je ne m'y etais pas co..."}], + u'remoteStatus': u'0', + u'status': u'2', + u'warning': 0}, + u'token': u'2491f8ccb6741ceee2a461dc523939796904a0fa'}} + +message.delete +-------------- + +Parameters: + - id_user + +Return Value: + Unknown + +MEMBER Commands +=============== + +member.view +----------- + +Parameters: + - id + +Return value: + {u'errors': [], + u'result': {u'member': {u'about1': u"comment te dire.. j'ai autant envie te laisser boire dans ma bouteille que mettre ma langue dans ta bouche !", + u'about2': u"LES BISOUS C'EST BIEN LES BIJOUX C'EST MIEUX!\r
\r
Et l'humour encore plus, mouhaha\r
\r
PS: Si vous êtes le sosie de Pharell Williams, adoptez moi ;)", + u'admin': u'0', + u'alert': u'1', + u'alert_add': u'0', + u'birthday': u'1991-05-31', + u'books': u"L'Analphabète - Rendell
Etat limite - Assouline
Marie-Antoinette - Zweig

", + 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
Shinning - L'exorciste - Ester - Gothika
How High ! - Requiem for a dream
Remember Me - trainspotting
He gots game - Buffet froid", + u'city': u'Paris', + u'country': u'fr', + u'cover': u'1', + u'drink': u'2', + u'eyes': u'5', + u'f': u'', + u'first_cnx': u'2011-09-17 00:18:59', + u'first_ip': u'82.120.134.233', + u'food': u'3', + u'godfather': u'0', + u'hair_color': u'4', + u'hair_size': u'3', + u'hobbies': u'Le théâtre définitivement ! et le sport !', + u'id': u'14465370', + u'img_count': u'6', + u'isBan': False, + u'isOnline': False, + u'job': u'etudiante', + u'last_chat': u'0000-00-00 00:00:00', + u'last_cnx': u'2011-09-24 00:09:29', + u'last_ip': u'92.151.177.164', + u'lat': u'48.8814', + u'latR': u'0.853141372984', + u'list1': u'0', + u'list2': u'42', + u'list3': u'0', + u'list4': u'0', + u'list5': u'0', + u'list6': u'0', + u'lng': u'2.3365', + u'lngR': u'0.0407796179728', + u'login': u'@', + u'mailable': True, + u'mod_level': u'0', + u'music': u'Daft Punk - Bloody Beetrots - Kid Cudi - Jamiroquai
Red Hot - Ray Charles - BEP - Gorillaz - Birdy nam nam
Citizen Cope - Angus & Julia Stone - Portishead
50cent - Sia - Ben Harper - Busta Rhymes
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
OC
True blood
Envoyé spécial ;) - Arte !
', + 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'}} + diff --git a/weboob/backends/aum/antispam.py b/weboob/backends/aum/antispam.py index 20fd5ad0..94935028 100644 --- a/weboob/backends/aum/antispam.py +++ b/weboob/backends/aum/antispam.py @@ -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, - } diff --git a/weboob/backends/aum/backend.py b/weboob/backends/aum/backend.py index 6c726f22..49273729 100644 --- a/weboob/backends/aum/backend.py +++ b/weboob/backends/aum/backend.py @@ -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", "
")).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'

You are currently waiting 1am to be able to connect with this account

', 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, diff --git a/weboob/backends/aum/browser.py b/weboob/backends/aum/browser.py index 38e841bc..7ff8b3fe 100644 --- a/weboob/backends/aum/browser.py +++ b/weboob/backends/aum/browser.py @@ -18,308 +18,252 @@ # along with weboob. If not, see . +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('en ligne') >= 0: - r = 'online' - elif result.find('Cet utilisateur a quitt\xe9 le site
') >= 0: - r = 'removed' - elif result.find('ce profil a \xe9t\xe9 bloqu\xe9 par l\'\xe9quipe de mod\xe9ration
') >= 0: - r = 'removed' - elif result.find('


Cette personne
vous a bloqu\xe9
') >= 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('(?Phttp://.+\.adopteunmec\.com/.+/)image(?P.+)\.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('en ligne') >= 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): diff --git a/weboob/backends/aum/contact.py b/weboob/backends/aum/contact.py new file mode 100644 index 00000000..2e25e26e --- /dev/null +++ b/weboob/backends/aum/contact.py @@ -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 . + + +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', '
')), + ('cinema', FieldSplit('cinema', '
')), + ('books', FieldSplit('books', '
')), + ('tv', FieldSplit('tvs', '
')), + ))), + ('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 diff --git a/weboob/backends/aum/exceptions.py b/weboob/backends/aum/exceptions.py deleted file mode 100644 index cef692f0..00000000 --- a/weboob/backends/aum/exceptions.py +++ /dev/null @@ -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 . - - -from weboob.tools.browser import BrowserUnavailable - -class AdopteWait(BrowserUnavailable): - pass - -class AdopteBanned(BrowserUnavailable): - pass - -class AdopteCantPostMail(Exception): - pass diff --git a/weboob/backends/aum/optim/profiles_walker.py b/weboob/backends/aum/optim/profiles_walker.py index 92d86dd4..2744510c 100644 --- a/weboob/backends/aum/optim/profiles_walker.py +++ b/weboob/backends/aum/optim/profiles_walker.py @@ -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) diff --git a/weboob/backends/aum/pages/__init__.py b/weboob/backends/aum/pages/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/weboob/backends/aum/pages/account.py b/weboob/backends/aum/pages/account.py deleted file mode 100644 index 78153be9..00000000 --- a/weboob/backends/aum/pages/account.py +++ /dev/null @@ -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 . - - -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() diff --git a/weboob/backends/aum/pages/base.py b/weboob/backends/aum/pages/base.py deleted file mode 100644 index 498c2c88..00000000 --- a/weboob/backends/aum/pages/base.py +++ /dev/null @@ -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 . - - -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): - """ - - - - - -
popularité7.230 pts
- """ - - l = self.document.getElementsByTagName('table') - for tag in l: - if tag.getAttribute('width') == '220': - # - - - - - - - - - - - - - - """ - - 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 diff --git a/weboob/backends/aum/pages/contact_thread.py b/weboob/backends/aum/pages/contact_thread.py deleted file mode 100644 index 9aeafee8..00000000 --- a/weboob/backends/aum/pages/contact_thread.py +++ /dev/null @@ -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 . - -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): - - """ - - """ - - 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): - #
- 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): - """ 1 """ - - 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): - """ Bonjour Romain """ - - 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') diff --git a/weboob/backends/aum/pages/baskets.py b/weboob/backends/aum/pages/baskets.py deleted file mode 100644 index 16dfb9ce..00000000 --- a/weboob/backends/aum/pages/baskets.py +++ /dev/null @@ -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 . - - -from weboob.backends.aum.pages.profileslist_base import ProfilesListBase - -class BasketsPage(ProfilesListBase): - - pass - diff --git a/weboob/backends/aum/pages/contact_list.py b/weboob/backends/aum/pages/contact_list.py deleted file mode 100644 index 3ea13702..00000000 --- a/weboob/backends/aum/pages/contact_list.py +++ /dev/null @@ -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 . - -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""" -
-
 
-
Hen
- 19ans, Montreuil
Comme ça, on est deux.
- il y a 1 heure
nouveau     -  
- - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - - -
- - -
Romain
-
-
-

27 octobre 2008, 01:11:32, nouveau -
-

-
Moi en g�n�ral j'aime sortir tout simplement dans des bars, pour discuter avec mes amis et/ou coll�gues, et rencontrer des gens. Sinon je fais de la guitare, et je d�veloppe des projets perso.

-
-
-
implicit - 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): - - """ - -
- -
- - - - - - - -
  Ecrire � zoe
- - - - - - - - - - - - - - -
-
-
- -
- - - """ - - 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) - - """ - - - - - - content - - ... - -
-
  voir tous les messages
- - """ - - 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] diff --git a/weboob/backends/aum/pages/edit.py b/weboob/backends/aum/pages/edit.py deleted file mode 100644 index 8e632814..00000000 --- a/weboob/backends/aum/pages/edit.py +++ /dev/null @@ -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 . - - -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 diff --git a/weboob/backends/aum/pages/home.py b/weboob/backends/aum/pages/home.py deleted file mode 100644 index 00a45ec0..00000000 --- a/weboob/backends/aum/pages/home.py +++ /dev/null @@ -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 . - - -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') diff --git a/weboob/backends/aum/pages/login.py b/weboob/backends/aum/pages/login.py deleted file mode 100644 index 64d5743d..00000000 --- a/weboob/backends/aum/pages/login.py +++ /dev/null @@ -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 . - - -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 diff --git a/weboob/backends/aum/pages/profile.py b/weboob/backends/aum/pages/profile.py deleted file mode 100644 index 75e1524b..00000000 --- a/weboob/backends/aum/pages/profile.py +++ /dev/null @@ -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 . - - -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.+?)( \((?P[0-9]{5})\))?, (?P.*)') - - 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 '' % 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('(?Phttp://.+\.adopteunmec\.com/.+/)image(?P.+)\.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 diff --git a/weboob/backends/aum/pages/profileslist_base.py b/weboob/backends/aum/pages/profileslist_base.py deleted file mode 100644 index 3483a13e..00000000 --- a/weboob/backends/aum/pages/profileslist_base.py +++ /dev/null @@ -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 . - - -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 diff --git a/weboob/backends/aum/pages/search.py b/weboob/backends/aum/pages/search.py deleted file mode 100644 index 1a2adb8f..00000000 --- a/weboob/backends/aum/pages/search.py +++ /dev/null @@ -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 . - - -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() diff --git a/weboob/backends/aum/pages/wait.py b/weboob/backends/aum/pages/wait.py deleted file mode 100644 index 67d3496c..00000000 --- a/weboob/backends/aum/pages/wait.py +++ /dev/null @@ -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 . - - -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') -