new version of aum backend

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

View file

@ -42,7 +42,7 @@ class ProfileFormatter(IFormatter):
result += self.print_node(sub, level+1) result += self.print_node(sub, level+1)
else: else:
if isinstance(node.value, (tuple,list)): 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: else:
value = node.value value = node.value
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value) result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)

861
weboob/backends/aum/API.txt Normal file
View file

@ -0,0 +1,861 @@
Adopte un Mec API
------------------
Constants:
APIKEY = fb0123456789abcd
URL = http://api.adopteunmec.com/api.php
ME Commands
===========
me.login
---------
Parameters:
- login
- pass
Errors:
- 1.1.1 : invalid login
Return value:
{u'errors': [],
u'result': {u'baskets': u'384',
u'events': {u'lastBasket': {u'alert': u'1',
u'birthday': u'1989-02-04',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'id': u'14471939',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 13:56:36',
u'list5': u'0',
u'login': u'kloo',
u'mod_level': u'0',
u'path': u'9/3/9/1/7/4/4/',
u'pseudo': u'Kloolloo',
u'region': u'11',
u'sex': 1,
u'shard': 9,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14471939/Kloolloo',
u'zip': u'75001'},
u'lastChat': {u'alert': u'1',
u'birthday': u'1987-10-10',
u'city': u'Paris 18e Arrondissement',
u'country': u'fr',
u'cover': u'0',
u'id': u'13280274',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2010-11-20 03:37:00',
u'list5': u'0',
u'login': u'#c1a63bda81cc03ccdf080ca6e003919e',
u'mod_level': u'0',
u'path': u'4/7/2/0/8/2/3/',
u'pseudo': u'Katie Lee',
u'region': u'11',
u'sex': 1,
u'shard': 4,
u'style': u'0',
u'table': u'adopteun.girls_coma',
u'url': u'/api.php?member/view/13280274/KatieLee',
u'zip': u'75018'},
u'lastFlash': {u'alert': u'1',
u'birthday': u'1983-12-20',
u'city': u'Longjumeau',
u'country': u'fr',
u'cover': u'1',
u'id': u'13883475',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 18:52:13',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'5/7/4/3/8/8/3/',
u'pseudo': u'Pas Tjs Sage',
u'region': u'11',
u'sex': 1,
u'shard': 5,
u'style': u'2',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13883475/PasTjsSage',
u'zip': u'91160'},
u'lastMail': {u'alert': u'1',
u'birthday': u'1989-04-28',
u'city': u'Paris 8e Arrondissement',
u'country': u'fr',
u'cover': u'5',
u'id': u'13268738',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 19:24:14',
u'list5': u'0',
u'login': u'@13268738',
u'mod_level': u'0',
u'path': u'8/3/7/8/6/2/3/',
u'pseudo': u'Th\xe9na',
u'region': u'11',
u'sex': 1,
u'shard': 8,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13268738/Thna',
u'zip': u'75008'},
u'lastVisit': {u'alert': u'1',
u'birthday': u'1988-02-27',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'id': u'14477637',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 18:31:00',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'7/3/6/7/7/4/4/',
u'pseudo': u'Lolly',
u'region': u'11',
u'sex': 1,
u'shard': 7,
u'style': u'4',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14477637/Lolly',
u'zip': u'75005'}},
u'flashs': 10,
u'mails': u'731',
u'me': {u'about1': u"Je n'ai pas de temps &agrave; perdre, je n'ai ni MSN ni Facebook, je consid&egrave;re qu'on appr&eacute;cie davantage la discussion face &agrave; face autour d'un verre que dans les yeux de son &eacute;cran.\r<br>\r<br>Bon et ne venez que si vous avez quelque chose &agrave; me dire, je n'envoie jamais de charmes.",
u'about2': u'',
u'admin': u'0',
u'alert': u'0',
u'alert_add': u'6',
u'birthday': u'1986-08-13',
u'books': u"Orwell (1984, La ferme des animaux)<br>Barjavel (La nuit des temps, Le voyageur imprudent, Ravage, \x85)<br>Boris Vian (J'irai cracher sur vos tombes, L'&eacute;cume des jours\x85)<br>Bukowski, Desproges, San Antonio<br>Sartre, Le Canard",
u'cat': u'1',
u'checks1': u'0',
u'checks2': u'16686',
u'checks3': u'0',
u'checks4': u'0',
u'checks5': u'0',
u'checks6': u'2',
u'checks7': u'0',
u'cinema': u'Le Grand D&eacute;tournement \x97 La Classe Am&eacute;ricaine<br>V pour Vendetta, Pulp Fiction, The Truman Show<br>Eternal Sunshine of the Spotless Mind, Match Point<br>Idiocracy, The Big Lebowski, La cit&eacute; de la peur<br>Sin City, Orange Mecanique, Buffet Froid, L',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'5',
u'drink': u'2',
u'email': u'tesiruna@parano.me',
u'eyes': u'3',
u'f': u'',
u'first_cnx': u'2010-05-16 09:13:53',
u'first_ip': u'81.57.125.104',
u'food': u'1',
u'godfather': u'0',
u'hair_color': u'5',
u'hair_size': u'3',
u'hobbies': u'',
u'id': u'22450639',
u'img_count': u'5',
u'isBan': False,
u'isOnline': True,
u'job': u'Dieu',
u'last_chat': u'-0001-11-29 23:09:21',
u'last_cnx': u'2011-09-20 19:24:25',
u'last_ip': u'88.161.27.232',
u'lat': u'48.861961',
u'latR': u'0.852802098431',
u'list1': u'2',
u'list2': u'2',
u'list3': u'2',
u'list4': u'3',
u'list5': u'0',
u'list6': u'0',
u'lng': u'2.33594',
u'lngR': u'0.040769844129',
u'login': u'@22450639',
u'mod_level': u'1',
u'music': u"Pink Floyd, Scorpions, Emperor<br>Metallica, Iron Maiden, Accept<br>Slash's Snakepit, Queen, Deep Purple<br>Led Zeppelin, Rolling Stones<br>Brassens, Souchon, Brel, Vian",
u'origins': u'1',
u'pass': u'8f3fa83cec9a243ae53c1337d2b5e1cf',
u'path': u'9/3/6/0/5/4/2/',
u'phone': u'-',
u'pictures': [{u'file': u'5',
u'height': u'427',
u'id': u'7315391',
u'md5': u'859fcd2e425617c33c16d6a1bc510ad3',
u'member': u'22450639',
u'rank': u'1',
u'valid': u'1578737',
u'width': u'500'}],
u'pseudo': u'Nazification',
u'region': u'11',
u'sex': 0,
u'shape': u'1',
u'shard': 9,
u'size': u'175',
u'smoke': u'2',
u'style': u'0',
u'subregion': u'76',
u'table': u'adopteun.boys',
u'texts1': u'',
u'texts2': u'',
u'texts3': u'',
u'texts4': u'',
u'texts5': u'',
u'texts6': u'',
u'title': u'',
u'tvs': u'Je ne poss&egrave;de pas la TV<br><br><br><br>',
u'url': u'/api.php?member/view/22450639/Nazification',
u'validated': True,
u'visites': u'0',
u'w': u'',
u'warn': u'0',
u'weight': u'55',
u'zip': u'75000'},
u'news': {u'newBaskets': 3, u'newMails': 1, u'newVisits': 113},
u'popu': u'71540',
u'subMobile': False,
u'subWebsite': False,
u'token': u'ea7bac8837f6e5bb1e2a3267d6a08b32e388d593',
u'visites': u'958'}}
me.[default]
------------
Return value:
{u'errors': [],
u'result': {u'events': {u'lastBasket': {u'alert': u'1',
u'birthday': u'1989-02-04',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'id': u'14471939',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 13:56:36',
u'list5': u'0',
u'login': u'kloo',
u'mod_level': u'0',
u'path': u'9/3/9/1/7/4/4/',
u'pseudo': u'Kloolloo',
u'region': u'11',
u'sex': 1,
u'shard': 9,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14471939/Kloolloo',
u'zip': u'75001'},
u'lastChat': {u'alert': u'1',
u'birthday': u'1987-10-10',
u'city': u'Paris 18e Arrondissement',
u'country': u'fr',
u'cover': u'0',
u'id': u'13280274',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2010-11-20 03:37:00',
u'list5': u'0',
u'login': u'#c1a63bda81cc03ccdf080ca6e003919e',
u'mod_level': u'0',
u'path': u'4/7/2/0/8/2/3/',
u'pseudo': u'Katie Lee',
u'region': u'11',
u'sex': 1,
u'shard': 4,
u'style': u'0',
u'table': u'adopteun.girls_coma',
u'url': u'/api.php?member/view/13280274/KatieLee',
u'zip': u'75018'},
u'lastFlash': {u'alert': u'1',
u'birthday': u'1983-12-20',
u'city': u'Longjumeau',
u'country': u'fr',
u'cover': u'1',
u'id': u'13883475',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 18:52:13',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'5/7/4/3/8/8/3/',
u'pseudo': u'Pas Tjs Sage',
u'region': u'11',
u'sex': 1,
u'shard': 5,
u'style': u'2',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13883475/PasTjsSage',
u'zip': u'91160'},
u'lastMail': {u'alert': u'1',
u'birthday': u'1989-04-28',
u'city': u'Paris 8e Arrondissement',
u'country': u'fr',
u'cover': u'5',
u'id': u'13268738',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 19:47:28',
u'list5': u'0',
u'login': u'@13268738',
u'mod_level': u'0',
u'path': u'8/3/7/8/6/2/3/',
u'pseudo': u'Th\xe9na',
u'region': u'11',
u'sex': 1,
u'shard': 8,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13268738/Thna',
u'zip': u'75008'},
u'lastVisit': {u'alert': u'1',
u'birthday': u'1988-02-27',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'id': u'14477637',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 19:09:00',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'7/3/6/7/7/4/4/',
u'pseudo': u'Lolly',
u'region': u'11',
u'sex': 1,
u'shard': 7,
u'style': u'4',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14477637/Lolly',
u'zip': u'75005'}},
u'news': {u'newBaskets': 0, u'newMails': 1, u'newVisits': 113},
u'token': u'9a97a03774c9f440e676c78f48794a7221a67285'}}
me.baskets
----------
Return value:
{u'errors': [],
u'popu': 71840,
u'result': {u'basket': [{u'alert': u'1',
u'birthday': u'1989-02-04',
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'date': u'2011-09-19 01:52:29',
u'id': u'14471939',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 13:56:36',
u'list5': u'0',
u'login': u'kloo',
u'mod_level': u'0',
u'path': u'9/3/9/1/7/4/4/',
u'pseudo': u'Kloolloo',
u'region': u'11',
u'sex': 1,
u'shard': 9,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14471939/Kloolloo',
u'zip': u'75001'}],
u'inBasket': 57,
u'token': u'ea7bac8837f6e5bb1e2a3267d6a08b32e388d593'}}
MESSAGE Commands
================
message.[default]
-----------------
Arguments:
- P=<NB_ENTRIES>,<START>
Return value:
{u'errors': [],
u'result': {u'count': 1,
u'threads': [{u'cat': u'0',
u'date': u'2011-09-20 19:22:11',
u'id': u'11132125',
u'id_from': u'13268738',
u'id_to': u'22450639',
u'member': {u'alert': u'1',
u'birthday': u'1989-04-28',
u'city': u'Paris 8e Arrondissement',
u'country': u'fr',
u'cover': u'5',
u'id': u'13268738',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-20 19:54:15',
u'list5': u'0',
u'login': u'@13268738',
u'mod_level': u'0',
u'path': u'8/3/7/8/6/2/3/',
u'pseudo': u'Th\xe9na',
u'region': u'11',
u'sex': 1,
u'shard': 8,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13268738/Thna',
u'zip': u'75008'},
u'message': u'0',
u'status': u'0',
u'title': u"J'aime bien les \xe9l\xe9phants. Toi ?"}],
u'token': u'0f70c31bc6d05d45bee64e6f2eab9b537640f2f8'}}
message.thread
--------------
Parameters:
- memberId
- count
Return value:
{u'result': {u'popu': u'15910',
u'thread': {u'isNew': False,
u'member': {u'alert': u'3',
u'birthday': u'1987-04-22',
u'city': u'Maisons-Alfort',
u'country': u'fr',
u'cover': u'6',
u'id': u'14022243',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 18:18:00',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'3/4/2/2/2/0/4/',
u'pseudo': u'Sophkipeut',
u'region': u'11',
u'sex': 1,
u'shard': 3,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14022243/Sophkipeut',
u'zip': u'94700'},
u'messages': [{u'date': u'2011-09-19 17:52:03',
u'id': u'46583458',
u'id_from': u'14022243',
u'id_to': u'23185402',
u'message': u"Lol, moi j'ai fait attention, mais bon &ccedil;a n'emp&ecirc;che pas son bidou, ceci dit c'est planqu&eacute; par ses loooooooooooong poils ^^",
u'src': u'',
u'title': u"Lol, moi j'ai fait attention, mais bon \xe7a n'emp\xea..."},
{u'date': u'2011-09-19 17:49:39',
u'id': u'47467748',
u'id_from': u'23185402',
u'id_to': u'14022243',
u'message': u"Ah oui, justement le v&eacute;t&eacute;rinaire m'avait dit apr&egrave;s la castration de Futex qu'il fallait faire attention &agrave; son poids, du coup &ccedil;a m'a tellement vex&eacute; que j'ai fais attention au point qu'il est sans doute m&ecirc;me trop maigre.",
u'src': u'',
u'title': u"Ah oui, justement le v\xe9t\xe9rinaire m'avait dit apr\xe8..."}],
u'remoteStatus': u'2',
u'status': u'1',
u'warning': 0},
u'token': u'dbeccf96256d4f11991626707881fdba28f54d73'}}
message.new
-----------
Parameters:
- memberId
- message
Return value:
{u'errors': [],
u'result': {u'thread': {u'isNew': False,
u'member': {u'alert': u'1',
u'birthday': u'1986-12-08',
u'city': u'Rosny-sous-Bois',
u'country': u'fr',
u'cover': u'6',
u'id': u'11099536',
u'isBan': False,
u'isOnline': False,
u'last_cnx': u'2011-09-20 16:26:32',
u'list5': u'0',
u'login': u'@11099536',
u'mod_level': u'0',
u'path': u'6/3/5/9/9/0/1/',
u'pseudo': u'Debo',
u'region': u'11',
u'sex': 1,
u'shard': 6,
u'style': u'0',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/11099536/Debo',
u'zip': u'93110'},
u'messages': [{u'date': u'2011-09-20 20:09:07',
u'id': u'46588573',
u'id_from': u'23185402',
u'id_to': u'11099536',
u'message': u'Coucou',
u'src': u'iphone',
u'title': u'Coucou'},
{u'date': u'2011-09-20 16:27:34',
u'id': u'46638469',
u'id_from': u'11099536',
u'id_to': u'23185402',
u'message': u"Coucou pour tout t'avouer je ne m'y etais pas connect&eacute; depuis septembre ! un peu moins de boulot alors j'y traine !!\r\n\r\nEt toi &ccedil;a va? tu devais pas partir au canada? tu es deja revenu.?",
u'src': u'',
u'title': u"Coucou pour tout t'avouer je ne m'y etais pas co..."}],
u'remoteStatus': u'0',
u'status': u'2',
u'warning': 0},
u'token': u'2491f8ccb6741ceee2a461dc523939796904a0fa'}}
message.delete
--------------
Parameters:
- id_user
Return Value:
Unknown
MEMBER Commands
===============
member.view
-----------
Parameters:
- id
Return value:
{u'errors': [],
u'result': {u'member': {u'about1': u"comment te dire.. j'ai autant envie te laisser boire dans ma bouteille que mettre ma langue dans ta bouche !",
u'about2': u"LES BISOUS C'EST BIEN LES BIJOUX C'EST MIEUX!\r<br>\r<br>Et l'humour encore plus, mouhaha\r<br>\r<br>PS: Si vous &ecirc;tes le sosie de Pharell Williams, adoptez moi ;)",
u'admin': u'0',
u'alert': u'1',
u'alert_add': u'0',
u'birthday': u'1991-05-31',
u'books': u"L'Analphab&egrave;te - Rendell<br>Etat limite - Assouline<br>Marie-Antoinette - Zweig<br><br>",
u'cat': u'2',
u'checks1': u'2',
u'checks2': u'1588',
u'checks3': u'0',
u'checks4': u'0',
u'checks5': u'64',
u'checks6': u'192',
u'checks7': u'0',
u'cinema': u"My Blueberry Night<br>Shinning - L'exorciste - Ester - Gothika<br>How High ! - Requiem for a dream<br>Remember Me - trainspotting<br>He gots game - Buffet froid",
u'city': u'Paris',
u'country': u'fr',
u'cover': u'1',
u'drink': u'2',
u'eyes': u'5',
u'f': u'',
u'first_cnx': u'2011-09-17 00:18:59',
u'first_ip': u'82.120.134.233',
u'food': u'3',
u'godfather': u'0',
u'hair_color': u'4',
u'hair_size': u'3',
u'hobbies': u'Le th&eacute;&acirc;tre d&eacute;finitivement ! et le sport !',
u'id': u'14465370',
u'img_count': u'6',
u'isBan': False,
u'isOnline': False,
u'job': u'etudiante',
u'last_chat': u'0000-00-00 00:00:00',
u'last_cnx': u'2011-09-24 00:09:29',
u'last_ip': u'92.151.177.164',
u'lat': u'48.8814',
u'latR': u'0.853141372984',
u'list1': u'0',
u'list2': u'42',
u'list3': u'0',
u'list4': u'0',
u'list5': u'0',
u'list6': u'0',
u'lng': u'2.3365',
u'lngR': u'0.0407796179728',
u'login': u'@',
u'mailable': True,
u'mod_level': u'0',
u'music': u'Daft Punk - Bloody Beetrots - Kid Cudi - Jamiroquai<br>Red Hot - Ray Charles - BEP - Gorillaz - Birdy nam nam<br>Citizen Cope - Angus &amp; Julia Stone - Portishead<br>50cent - Sia - Ben Harper - Busta Rhymes<br>Musiques de gansta ! ET Debussy',
u'origins': u'1',
u'path': u'0/7/3/5/6/4/4/',
u'phone': u'-',
u'popu': {u'bonus': u'6',
u'contacts': u'1',
u'flashs': u'246',
u'id': u'14465370',
u'invits': u'0',
u'mails': u'39',
u'popu': u'11345',
u'visites': u'645'},
u'pseudo': u'Ruslana',
u'region': u'11',
u'sex': 1,
u'shape': u'1',
u'shard': 0,
u'size': u'170',
u'smoke': u'2',
u'style': u'4',
u'subregion': u'76',
u'table': u'adopteun.girls',
u'texts1': u'',
u'texts2': u'',
u'texts3': u'',
u'texts4': u'',
u'texts5': u'',
u'texts6': u'MON SOURIRE HAHAHA',
u'title': u'',
u'tvs': u'Dexter<br>OC<br>True blood<br>Envoy&eacute; sp&eacute;cial ;) - Arte !<br>',
u'url': u'/api.php?member/view/14465370/Ruslana',
u'visites': u'0',
u'w': u'',
u'warn': u'0',
u'weight': u'50',
u'zip': u'75008'},
u'token': u'e0247704012e01bc32756b357b010e5206ac9c76'}}
member.pictures
---------------
Parameters:
- id
Return value:
{u'errors': [],
u'result': {u'pictures': [{u'id': u'12363004',
u'rating': 4.4473684210500002,
u'url': u'http://s0.adopteunmec.com/0/6/0/1/6/image7.jpg'}],
u'token': u'e131f1b194f2a19337882398b10b79457a638252'}}
member.addBasket
----------------
Parameters:
- id
Errors:
- 5.1.1 : member does not exist
- 5.1.5 : already sent charm to this one
- 5.1.6 : no enough charms available
Return value:
{u'errors': u'0',
u'flashs': 4,
u'result': {u'token': u'55039d0557393bb7c5e4381792143d003f0e60c0'}}
SEARH Commands
==============
search.[default]
----------------
Return value:
{u'errors': [],
u'result': {u'qsearch': {u'ageMax': u'25',
u'ageMin': u'18',
u'dist': u'0',
u'new': u'0',
u'query': u'{"sex":1,"ageMin":"18","ageMax":"25","region":"fr","new":"0","dist":"0"}',
u'region': u'fr',
u'sex': 1},
u'search': {u'ageMax': u'27',
u'ageMin': u'20',
u'checks1': u'0',
u'checks2': u'0',
u'country': u'fr',
u'dist': u'50',
u'drink': u'0',
u'eyes': u'0',
u'food': u'0',
u'hair_color': u'0',
u'hair_size': u'0',
u'origins': u'0',
u'pseudo': u'',
u'query': u'{"ageMin":"20","ageMax":"27","country":"fr","region":"11","subregion":"0","dist":"50","pseudo":"","sex":"1","sizeMin":"0","sizeMax":"0","weightMin":"0","weightMax":"75","shape":"0","hair_size":"0","hair_color":"0","eyes":"0","origins":"0","style":"0","checks1":"0","checks2":"0","smoke":"0","drink":"0","food":"0","search":"true"}',
u'region': u'11',
u'search': u'true',
u'sex': u'1',
u'shape': u'0',
u'sizeMax': u'0',
u'sizeMin': u'0',
u'smoke': u'0',
u'style': u'0',
u'subregion': u'0',
u'weightMax': u'75',
u'weightMin': u'0'},
u'token': u'3196a3365d927f2ee8738ec8dfc4a5abd75e3ee3'}}
search.quick
------------
Parameters:
- sex (int[0,1])
- ageMin (int)
- ageMax (int)
- region (str)
- new (int)
- dist (int)
Return Value:
{u'errors': [],
u'result': {u'regions': {u'be': None,
u'be_24': u' - wallonie',
u'be_25': u' - bruxelles capitale',
u'be_26': u' - flandre',
u'ca': None,
u'ca_34': u' - canada',
u'ca_35': u' - quebec',
u'ch': None,
u'ch_27': u' - r\xe9gion l\xe9manique',
u'ch_28': u' - espace Mittelland',
u'ch_29': u' - suisse du nord-ouest',
u'ch_30': u' - zurich',
u'ch_31': u' - suisse orientale',
u'ch_32': u' - suisse centrale',
u'ch_33': u' - tessin',
u'fr': None,
u'fr_1': u' - alsace',
u'fr_10': u' - haute-normandie',
u'fr_11': u' - ile-de-france',
u'fr_12': u' - languedoc-roussillon',
u'fr_13': u' - limousin',
u'fr_14': u' - lorraine',
u'fr_15': u' - midi-pyr\xe9n\xe9es',
u'fr_16': u' - nord-pas-de-calais',
u'fr_17': u' - paca',
u'fr_18': u' - pays de la loire',
u'fr_19': u' - picardie',
u'fr_2': u' - aquitaine',
u'fr_20': u' - poitou-charentes',
u'fr_21': u' - rh\xf4ne-alpes',
u'fr_22': u' - corse',
u'fr_23': u' - dom+tom',
u'fr_3': u' - auvergne',
u'fr_4': u' - basse-normandie',
u'fr_5': u' - bourgogne',
u'fr_6': u' - bretagne',
u'fr_7': u' - centre',
u'fr_8': u' - champagne-ardenne',
u'fr_9': u' - franche-comt\xe9'},
u'search': [{u'alert': u'2',
u'birthday': u'1985-11-21',
u'city': u'Boulogne-Billancourt',
u'country': u'fr',
u'cover': u'12',
u'id': u'14252744',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-24 15:19:48',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'4/4/7/2/5/2/4/',
u'pseudo': u'Birdy',
u'region': u'11',
u'sex': 1,
u'shard': 4,
u'style': u'5',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/14252744/Birdy',
u'zip': u'92100'}],
u'token': u'ef78ac6d812ab15f2f8efd578f4da4ef2e23aa71'}}
search.advanced
---------------
Parameters:
- ageMin (int)
- ageMax (int)
- country (str)
- region (int)
- subregion (int)
- dist (int)
- pseudo (str)
- sex (int[0,1])
- sizeMin (int)
- sizeMax (int)
- weightMin (int)
- weightMax (int)
- shape (int)
- hair_size (int)
- hair_color (int)
- eyes (int)
- origins (int)
- style (int)
- checks1 (int)
- checks2 (int)
- smoke (int)
- drink (int)
- food (int)
- search (bool)
Return Value:
{u'errors': [],
u'regions': {u'be': None,
u'be_24': u' - wallonie',
u'be_25': u' - bruxelles capitale',
u'be_26': u' - flandre',
u'ca': None,
u'ca_34': u' - canada',
u'ca_35': u' - quebec',
u'ch': None,
u'ch_27': u' - r\xe9gion l\xe9manique',
u'ch_28': u' - espace Mittelland',
u'ch_29': u' - suisse du nord-ouest',
u'ch_30': u' - zurich',
u'ch_31': u' - suisse orientale',
u'ch_32': u' - suisse centrale',
u'ch_33': u' - tessin',
u'fr': None,
u'fr_1': u' - alsace',
u'fr_10': u' - haute-normandie',
u'fr_11': u' - ile-de-france',
u'fr_12': u' - languedoc-roussillon',
u'fr_13': u' - limousin',
u'fr_14': u' - lorraine',
u'fr_15': u' - midi-pyr\xe9n\xe9es',
u'fr_16': u' - nord-pas-de-calais',
u'fr_17': u' - paca',
u'fr_18': u' - pays de la loire',
u'fr_19': u' - picardie',
u'fr_2': u' - aquitaine',
u'fr_20': u' - poitou-charentes',
u'fr_21': u' - rh\xf4ne-alpes',
u'fr_22': u' - corse',
u'fr_23': u' - dom+tom',
u'fr_3': u' - auvergne',
u'fr_4': u' - basse-normandie',
u'fr_5': u' - bourgogne',
u'fr_6': u' - bretagne',
u'fr_7': u' - centre',
u'fr_8': u' - champagne-ardenne',
u'fr_9': u' - franche-comt\xe9'},
u'result': {u'search': [{u'alert': u'1',
u'birthday': u'1988-04-07',
u'city': u'Dammartin',
u'country': u'fr',
u'cover': u'25',
u'id': u'13579115',
u'isBan': False,
u'isOnline': True,
u'last_cnx': u'2011-09-24 15:17:22',
u'list5': u'0',
u'login': u'@',
u'mod_level': u'0',
u'path': u'5/1/1/9/7/5/3/',
u'pseudo': u"S\xe9 's\xe9",
u'region': u'11',
u'sex': 1,
u'shard': 5,
u'style': u'1',
u'table': u'adopteun.girls',
u'url': u'/api.php?member/view/13579115/Ss',
u'zip': u'77230'}],
u'token': u'f92aede46118dcdba6d484146b4627777fbe7188'}}

View file

@ -19,117 +19,30 @@
import re import re
from .pages.contact_list import ContactItem
from .pages.profile import ProfilePage
from .pages.contact_thread import MailParser
__all__ = ['AntiSpam'] __all__ = ['AntiSpam']
class AntiSpam(object): class AntiSpam(object):
def check(self, obj): def check_thread(self, thread):
for key, value in self.OBJECTS.iteritems(): resume = thread['title']
if isinstance(obj, key):
return value(self, obj)
raise TypeError('Unsupported object %r' % obj)
def check_contact(self, contact):
resume = contact.get_resume()
# Check if there is an email address in the offer. # Check if there is an email address in the offer.
if re.match('^[\w\d\.\-_]+@[\w\d\.]+ vous offre la pos', resume): if re.match('^[\w\d\.\-_]+@[\w\d\.]+ vous offre la pos', resume):
return False return False
if contact.get_name() == 'Ekaterina': if thread['member']['pseudo'] == 'Ekaterina':
return False return False
return True return True
def check_profile(self, profile): def check_profile(self, profile):
# The name of profile is in form #123456789 # The name of profile is in form #123456789
if re.match('^#\d+$', profile.get_name()): if profile['pseudo'] == '':
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:
return False return False
return True return True
def check_mail(self, mail): def check_mail(self, mail):
# Spambot with a long first-message. # 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 False
return True return True
OBJECTS = {ContactItem: check_contact,
ProfilePage: check_profile,
MailParser: check_mail,
}

View file

@ -20,34 +20,40 @@
from __future__ import with_statement from __future__ import with_statement
import email import email
import time
import re import re
import datetime import datetime
from dateutil import tz from dateutil import tz
from dateutil.parser import parse as _parse_dt
from weboob.capabilities.base import NotLoaded from weboob.capabilities.base import NotLoaded
from weboob.capabilities.chat import ICapChat from weboob.capabilities.chat import ICapChat
from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message, Thread from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message, Thread
from weboob.capabilities.dating import ICapDating, OptimizationNotFound 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.capabilities.account import ICapAccount, StatusField
from weboob.tools.backend import BaseBackend, BackendConfig from weboob.tools.backend import BaseBackend, BackendConfig
from weboob.tools.browser import BrowserUnavailable from weboob.tools.browser import BrowserUnavailable
from weboob.tools.value import Value, ValuesDict, ValueBool, ValueBackendPassword from weboob.tools.value import Value, ValuesDict, ValueBool, ValueBackendPassword
from weboob.tools.log import getLogger from weboob.tools.log import getLogger
from weboob.tools.misc import html2text, local2utc
from .contact import Contact
from .captcha import CaptchaError from .captcha import CaptchaError
from .antispam import AntiSpam from .antispam import AntiSpam
from .browser import AuMBrowser from .browser import AuMBrowser
from .exceptions import AdopteWait
from .optim.profiles_walker import ProfilesWalker from .optim.profiles_walker import ProfilesWalker
from .optim.visibility import Visibility from .optim.visibility import Visibility
from .optim.priority_connection import PriorityConnection
from .optim.queries_queue import QueriesQueue from .optim.queries_queue import QueriesQueue
__all__ = ['AuMBackend'] __all__ = ['AuMBackend']
def parse_dt(s):
d = _parse_dt(s)
return local2utc(d)
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount): class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapChat, ICapContact, ICapAccount):
NAME = 'aum' NAME = 'aum'
MAINTAINER = 'Romain Bignon' MAINTAINER = 'Romain Bignon'
@ -60,7 +66,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
ValueBool('antispam', label='Enable anti-spam', default=False), ValueBool('antispam', label='Enable anti-spam', default=False),
ValueBool('baskets', label='Get baskets with new messages', default=True)) ValueBool('baskets', label='Get baskets with new messages', default=True))
STORAGE = {'profiles_walker': {'viewed': []}, STORAGE = {'profiles_walker': {'viewed': []},
'priority_connection': {'config': {}, 'fakes': {}},
'queries_queue': {'queue': []}, 'queries_queue': {'queue': []},
'sluts': {}, 'sluts': {},
} }
@ -89,7 +94,6 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def init_optimizations(self): def init_optimizations(self):
self.add_optimization('PROFILE_WALKER', ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)) 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('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)) self.add_optimization('QUERIES_QUEUE', QueriesQueue(self.weboob.scheduler, self.storage, self.browser))
# ---- ICapMessages methods --------------------- # ---- ICapMessages methods ---------------------
@ -99,25 +103,25 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def iter_threads(self): def iter_threads(self):
with self.browser: with self.browser:
contacts = self.browser.get_threads_list() threads = self.browser.get_threads_list()
for contact in contacts: for thread in threads:
if not contact.get_id(): if thread['member'].get('isBan', True):
continue continue
if self.antispam and not self.antispam.check(contact): if self.antispam and not self.antispam.check_thread(thread):
self.logger.info('Skipped a spam-thread from %s' % contact.get_name()) self.logger.info('Skipped a spam-thread from %s' % thread['pseudo'])
self.report_spam(contact.get_id(), contact.get_suppr_id()) self.report_spam(thread['member']['id'], thread['id'])
continue continue
thread = Thread(contact.get_id()) t = Thread(int(thread['member']['id']))
thread.flags = Thread.IS_DISCUSSION t.flags = Thread.IS_DISCUSSION
thread.title = 'Discussion with %s' % contact.get_name() t.title = 'Discussion with %s' % thread['member']['pseudo']
yield thread yield t
def get_thread(self, id, profiles=None, contact=None): def get_thread(self, id, contacts=None):
""" """
Get a thread and its messages. 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 thread = None
if isinstance(id, Thread): if isinstance(id, Thread):
@ -125,57 +129,60 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
id = thread.id id = thread.id
if not thread: if not thread:
thread = Thread(id) thread = Thread(int(id))
thread.flags = Thread.IS_DISCUSSION thread.flags = Thread.IS_DISCUSSION
full = False full = False
else: else:
full = True full = True
with self.browser: 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() my_name = self.browser.get_my_name()
child = None child = None
msg = None msg = None
slut = self._get_slut(id) slut = self._get_slut(id)
if not profiles: if contacts is None:
profiles = {} contacts = {}
for mail in mails:
if not thread.title:
thread.title = u'Discussion with %s' % mails['member']['pseudo']
for mail in mails['messages']:
flags = 0 flags = 0
if self.antispam and not self.antispam.check(mail): if self.antispam and not self.antispam.check_mail(mail):
self.logger.info('Skipped a spam-mail from %s' % mail.sender) self.logger.info('Skipped a spam-mail from %s' % mails['member']['pseudo'])
self.report_spam(thread.id, contact and contact.get_suppr_id()) self.report_spam(thread.id, int(mail['id']))
break break
if mail.date > slut['lastmsg']: if parse_dt(mail['date']) > slut['lastmsg']:
flags |= Message.IS_UNREAD flags |= Message.IS_UNREAD
if not mail.profile_link in profiles: if not mail['id_from'] in contacts:
with self.browser: with self.browser:
profiles[mail.profile_link] = self.browser.get_profile(mail.profile_link) contacts[mail['id_from']] = self.get_contact(mail['id_from'])
if self.antispam and not self.antispam.check(profiles[mail.profile_link]): 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' % mail.sender) self.logger.info('Skipped a spam-mail-profile from %s' % mails['member']['pseudo'])
self.report_spam(thread.id, contact and contact.get_suppr_id()) self.report_spam(thread.id, mails['id'])
break break
mail.signature += u'\n%s' % profiles[mail.profile_link].get_profile_text()
if mail.sender == my_name: if int(mail['id_from']) == self.browser.my_id:
if mail.new: if int(mails['remoteStatus']) == 0:
flags |= Message.IS_NOT_ACCUSED flags |= Message.IS_NOT_ACCUSED
else: else:
flags |= Message.IS_ACCUSED 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, msg = Message(thread=thread,
id=mail.message_id, id=int(time.strftime('%Y%m%d%H%M%S', parse_dt(mail['date']).timetuple())),
title=mail.title, title=thread.title,
sender=mail.sender, sender=my_name if int(mail['id_from']) == self.browser.my_id else mails['member']['pseudo'],
receivers=[mail.name if mail.sender == my_name else my_name], # TODO: me receivers=[my_name if int(mail['id_from']) != self.browser.my_id else mails['member']['pseudo']],
date=mail.date, date=parse_dt(mail['date']),
content=mail.content, content=html2text(mail['message'].replace("\r", "<br>")).strip(),
signature=mail.signature, signature=contacts[mail['id_from']].get_text() if mail['id_from'] in contacts else None,
children=[], children=[],
flags=flags) flags=flags)
if child: if child:
@ -198,20 +205,20 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def iter_unread_messages(self, thread=None): def iter_unread_messages(self, thread=None):
try: try:
profiles = {} contacts = {}
with self.browser: with self.browser:
contacts = self.browser.get_threads_list() threads = self.browser.get_threads_list()
for contact in contacts: for thread in threads:
if not contact.get_id(): if thread['member'].get('isBan', True):
continue continue
if self.antispam and not self.antispam.check(contact): if self.antispam and not self.antispam.check_thread(thread):
self.logger.info('Skipped a spam-unread-thread from %s' % contact.get_name()) self.logger.info('Skipped a spam-unread-thread from %s' % thread['member']['pseudo'])
self.report_spam(contact.get_id(), contact.get_suppr_id()) self.report_spam(thread['member']['id'], thread['id'])
continue continue
slut = self._get_slut(contact.get_id()) slut = self._get_slut(thread['member']['id'])
if contact.get_lastmsg_date() > slut['lastmsg']: if parse_dt(thread['date']) > slut['lastmsg'] or int(thread['status']) != int(slut['status']):
thread = self.get_thread(contact.get_id(), profiles, contact) t = self.get_thread(thread['member']['id'], contacts)
for m in thread.iter_all_messages(): for m in t.iter_all_messages():
if m.flags & m.IS_UNREAD: if m.flags & m.IS_UNREAD:
yield m 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 # XXX possibly race condition if a slut adds me in her basket
# between the aum.nb_new_baskets() and aum.get_baskets(). # between the aum.nb_new_baskets() and aum.get_baskets().
with self.browser: with self.browser:
slut = self._get_slut(-self.MAGIC_ID_BASKET)
new_baskets = self.browser.nb_new_baskets() new_baskets = self.browser.nb_new_baskets()
if new_baskets: if new_baskets >= 0:
ids = self.browser.get_baskets() baskets = self.browser.get_baskets()
while new_baskets > 0 and len(ids) > new_baskets: my_name = self.browser.get_my_name()
new_baskets -= 1 for basket in baskets:
if ids[new_baskets] == '-1': if basket['isBan'] or parse_dt(basket['date']) <= slut['lastmsg']:
continue continue
profile = self.browser.get_profile(ids[new_baskets]) contact = self.get_contact(basket['id'])
if not profile or profile.get_id() == 0: if self.antispam and not self.antispam.check_profile(contact.aum_profile):
continue self.logger.info('Skipped a spam-basket from %s' % contact.name)
if self.antispam and not self.antispam.check(profile): self.report_spam(basket['id'])
self.logger.info('Skipped a spam-basket from %s' % profile.get_name())
self.report_spam(profile.get_id())
continue continue
thread = Thread(profile.get_id()) thread = Thread(int(basket['id']))
thread.title = 'Basket of %s' % profile.get_name() thread.title = 'Basket of %s' % contact.name
thread.root = Message(thread=thread, thread.root = Message(thread=thread,
id=self.MAGIC_ID_BASKET, id=self.MAGIC_ID_BASKET,
title=thread.title, title=thread.title,
sender=profile.get_name(), sender=contact.name,
receivers=[self.browser.get_my_name()], receivers=[my_name],
date=None, # now date=parse_dt(basket['date']),
content='You are taken in her basket!', content='You are taken in her basket!',
signature=profile.get_profile_text(), signature=contact.get_text(),
children=[], children=[],
flags=Message.IS_UNREAD) flags=Message.IS_UNREAD)
yield thread.root yield thread.root
@ -256,25 +263,31 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def set_message_read(self, message): def set_message_read(self, message):
if message.id == self.MAGIC_ID_BASKET: 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 return
slut = self._get_slut(message.thread.id) slut = self._get_slut(message.thread.id)
if slut['lastmsg'] < message.date: if slut['lastmsg'] < message.date:
slut['lastmsg'] = message.date slut['lastmsg'] = message.date
#slut['msgstatus'] = contact.get_status()
self.storage.set('sluts', message.thread.id, slut) self.storage.set('sluts', message.thread.id, slut)
self.storage.save() self.storage.save()
def _get_slut(self, id): def _get_slut(self, id):
id = int(id)
sluts = self.storage.get('sluts') sluts = self.storage.get('sluts')
if not sluts or not id in sluts: if not sluts or not id in sluts:
slut = {'lastmsg': datetime.datetime(1970,1,1), slut = {'lastmsg': datetime.datetime(1970,1,1),
'msgstatus': ''} 'status': 0}
else: else:
slut = self.storage.get('sluts', id) 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 return slut
# ---- ICapMessagesPost methods --------------------- # ---- ICapMessagesPost methods ---------------------
@ -319,59 +332,29 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
if not profile: if not profile:
return None return None
_id = profile.id _id = profile['id']
if profile.is_online():
s = Contact.STATUS_ONLINE
else:
s = Contact.STATUS_OFFLINE
if isinstance(contact, Contact): if isinstance(contact, Contact):
contact.id = _id contact.id = _id
contact.name = profile.get_name() contact.name = profile['pseudo']
contact.status = s
else: else:
contact = Contact(_id, profile.get_name(), s) contact = Contact(_id, profile['pseudo'], Contact.STATUS_ONLINE)
contact.url = self.browser.id2url(_id) contact.url = self.browser.id2url(_id)
contact.status_msg = profile.get_status() contact.parse_profile(profile, self.browser.get_consts())
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)
return contact return contact
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
with self.browser: with self.browser:
for contact in self.browser.iter_contacts(): for contact in self.browser.iter_contacts():
s = 0 s = 0
if contact['cat'] == 1: if contact['isOnline']:
s = Contact.STATUS_ONLINE s = Contact.STATUS_ONLINE
elif contact['cat'] == 3:
s = Contact.STATUS_OFFLINE
elif contact['cat'] == 2:
s = Contact.STATUS_AWAY
else: 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 continue
# TODO age in contact['birthday']
c = Contact(contact['id'], contact['pseudo'], s) c = Contact(contact['id'], contact['pseudo'], s)
c.url = self.browser.id2url(contact['id']) c.url = self.browser.id2url(contact['id'])
c.status_msg = u'%s old' % contact['birthday'] c.status_msg = u'%s old' % contact['birthday']
@ -490,15 +473,12 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
def get_account_status(self): def get_account_status(self):
with self.browser: with self.browser:
try: return (
return ( StatusField('myname', 'My name', self.browser.get_my_name()),
StatusField('myname', 'My name', self.browser.get_my_name()), StatusField('score', 'Score', self.browser.score()),
StatusField('score', 'Score', self.browser.score()), StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()),
StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()), StatusField('godchilds', 'Number of godchilds', self.browser.nb_godchilds()),
StatusField('godchilds', 'Number of godchilds', self.browser.nb_godchilds()), )
)
except AdopteWait:
return (StatusField('notice', '', u'<h3>You are currently waiting 1am to be able to connect with this account</h3>', StatusField.FIELD_HTML|StatusField.FIELD_TEXT))
OBJECTS = {Thread: fill_thread, OBJECTS = {Thread: fill_thread,
Contact: fill_contact, Contact: fill_contact,

View file

@ -18,308 +18,252 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>. # along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
import datetime import datetime
import time
import random import random
import urllib import urllib
try: try:
import simplejson import json
except ImportError: except ImportError:
# Python 2.6+ has a module similar to simplejson import simplejson as json
import json as simplejson
from weboob.tools.browser import BaseBrowser, BrowserUnavailable from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword, 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.capabilities.chat import ChatException, ChatMessage from weboob.capabilities.chat import ChatException, ChatMessage
from weboob.capabilities.messages import CantSendMessage
__all__ = ['AuMBrowser'] __all__ = ['AuMBrowser']
class AuMBrowser(BaseBrowser): class AuMBrowser(BaseBrowser):
DOMAIN = 'www.adopteunmec.com' DOMAIN = 'api.adopteunmec.com'
ENCODING = 'iso-8859-1' APIKEY = 'fb0123456789abcd'
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,
}
def __init__(self, *args, **kwargs): consts = None
kwargs['parser'] = Html5libParser(api='dom') my_sex = 0
BaseBrowser.__init__(self, *args, **kwargs) my_id = 0
self.my_id = 0 my_name = u''
def id2url(self, _id): def id2url(self, id):
return u'%s://%s/%s' % (self.PROTOCOL, self.DOMAIN, _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): def login(self):
if not self.is_on_page(LoginPage): r = self.api_request('me', 'login', data={'login': self.username,
self.home() 'pass': self.password,
self.page.login(self.username, 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): #def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country, godfather=None):
return self.page and self.page.is_logged() # 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): #@pageaccess
return self.location('http://www.adopteunmec.com/index.php') #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): def inner(self, *args, **kwargs):
if self.is_on_page(WaitPage): if self.my_id == 0:
if not self.page.check(): self.login()
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()
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return inner return inner
def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country, godfather=None): def get_consts(self):
if not self.is_on_page(RegisterPage): if self.consts is not None:
self.location('http://www.adopteunmec.com/register2.php') return self.consts
self.page.register(password, sex, birthday_d, birthday_m, birthday_y, zipcode, country)
if godfather:
if not self.is_on_page(AccountPage):
self.location('http://www.adopteunmec.com/account.php')
self.page.set_godfather(godfather)
@pageaccess self.consts = []
def add_photo(self, name, f): for i in xrange(2):
if not self.is_on_page(EditPhotoPage): r = self.api_request('me', 'all_values', data={'sex': i})
self.location('/edit.php?type=1') self.consts.append(r['result']['values'])
return self.page.add_photo(name, f)
@pageaccess return self.consts
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 @check_login
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
def score(self): def score(self):
if time.time() - self.last_update > 60: r = self.api_request('member', 'view', data={'id': self.my_id})
self.home() return int(r['result']['member']['popu']['popu'])
return self.page.score()
@pageaccess @check_login
def get_my_name(self): def get_my_name(self):
if time.time() - self.last_update > 60: return self.my_name
self.home()
return self.page.get_my_name()
@check_login
def get_my_id(self): 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 return self.my_id
@pageaccess @check_login
def nb_new_mails(self): def nb_new_mails(self):
if time.time() - self.last_update > 60: r = self.api_request('me', '[default]')
self.home() return r['result']['news']['newMails']
return self.page.nb_new_mails()
@pageaccess @check_login
def nb_new_baskets(self): def nb_new_baskets(self):
if time.time() - self.last_update > 60: r = self.api_request('me', '[default]')
self.home() return r['result']['news']['newBaskets']
return self.page.nb_new_baskets()
@pageaccess @check_login
def nb_new_visites(self): def nb_new_visites(self):
if time.time() - self.last_update > 60: r = self.api_request('me', '[default]')
self.home() return r['result']['news']['newVisits']
return self.page.nb_new_visites()
@pageaccess @check_login
def nb_available_charms(self, reload=False): def nb_available_charms(self):
if reload or not self.is_on_page(HomePage): r = self.login()
self.home() return r['result']['flashs']
return self.page.nb_available_charms()
@pageaccess @check_login
def nb_godchilds(self, reload=False): def nb_godchilds(self):
if reload or not self.is_on_page(HomePage): r = self.api_request('member', 'view', data={'id': self.my_id})
self.home() return int(r['result']['member']['popu']['invits'])
return self.page.nb_godchilds()
@pageaccess @check_login
def get_baskets(self): def get_baskets(self):
self.location('/mail.php?type=1') r = self.api_request('me', 'basket')
return self.page.get_profiles_ids_list() return r['result']['basket']
@pageaccess @check_login
def flush_visits(self): def get_threads_list(self, count=30):
""" Does nothing, only flush new visits to increase my score """ r = self.api_request('message', '[default]', '%d,0' % count)
self.openurl('/mail.php?type=3') return r['result']['threads']
@pageaccess @check_login
def get_threads_list(self): def get_thread_mails(self, id, count=30):
if not self.is_on_page(ContactListPage): r = self.api_request('message', 'thread', data={'memberId': id, 'count': count})
self.location('/mail.php') return r['result']['thread']
return self.page.get_contact_list() @check_login
@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
def post_mail(self, id, content): def post_mail(self, id, content):
if not self.is_on_page(ContactThreadPage) or self.page.id != int(id): r = self.api_request('message', 'new', data={'memberId': id, 'message': content.encode('utf-8')})
self.page.open_thread_page(id) if len(r['errors']) > 0:
self.page.post(content) raise CantSendMessage(r['errors'][0])
@pageaccess @check_login
def delete_thread(self, id): def delete_thread(self, id):
data = 'delete=true&suppr%%5B%%5D=%s' % id r = self.api_request('message', 'delete', data={'id_user': id})
url = 'http://www.adopteunmec.com/mail.php' self.logger.debug('Thread deleted: %r' % r)
self.openurl(url, data).read()
return True @check_login
@pageaccess
def send_charm(self, id): def send_charm(self, id):
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read() r = self.api_request('member', 'addBasket', data={'id': id})
self.logger.debug('Charm: %s' % result) return r['errors'] == '0'
return result.find('noMoreFlashes') < 0
@pageaccess @check_login
def add_basket(self, id): def add_basket(self, id):
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read() r = self.api_request('member', 'addBasket', data={'id': id})
self.logger.debug('Basket: %s' % result) return r['errors'] == '0'
# TODO check if it works (but it should)
return True
@pageaccess
def deblock(self, id): def deblock(self, id):
self.readurl('http://www.adopteunmec.com/fajax_postMessage.php?action=deblock&to=%s' % id) self.readurl('http://www.adopteunmec.com/fajax_postMessage.php?action=deblock&to=%s' % id)
return True return True
@pageaccess
def report_fake(self, id): def report_fake(self, id):
return self.readurl('http://www.adopteunmec.com/fake.php', 'id=%s' % id) return self.readurl('http://www.adopteunmec.com/fake.php', 'id=%s' % id)
@pageaccess
def rate(self, id, what, rating): 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() result = self.openurl('http://www.adopteunmec.com/fajax_vote.php', 'member=%s&what=%s&rating=%s' % (id, what, rating)).read()
return float(result) return float(result)
@pageaccess
def search_profiles(self, **kwargs): def search_profiles(self, **kwargs):
self.location('/search.php') r = self.api_request('searchs', '[default]')
self.page.search(**kwargs) params = r['result']['search']
return self.page.get_profiles_ids() params.pop('query', None)
params.update(kwargs)
@pageaccess r = self.api_request('searchs', 'advanced', '30,0', params)
def get_profile(self, link): ids = [s['id'] for s in r['result']['search']]
if isinstance(link, basestring): return set(ids)
link = link.replace('http://www.adopteunmec.com/', '')
if link.startswith('/'):
link = link[1:]
self.location('/%s' % link)
return self.page
@pageaccess def get_profile(self, id, with_pics=True):
def get_slut_state(self, id): r = self.api_request('member', 'view', data={'id': id})
result = self.openurl('http://www.adopteunmec.com/%s' % id).read() profile = r['result']['member']
if result.find('<td align="right" style="font-size:12px;font-weight:bold">en ligne</td>') >= 0: if with_pics:
r = 'online' r = self.api_request('member', 'pictures', data={'id': id})
elif result.find('Cet utilisateur a quitt\xe9 le site<br />') >= 0: profile['pictures'] = []
r = 'removed' for pic in r['result']['pictures']:
elif result.find('ce profil a \xe9t\xe9 bloqu\xe9 par l\'\xe9quipe de mod\xe9ration<br />') >= 0: d = {'hidden': False}
r = 'removed' d.update(pic)
elif result.find('<div align=center style="color:#ff0000;font-size:16px"><br /><br />Cette personne<br>vous a bloqu\xe9</div>') >= 0: profile['pictures'].append(d)
r = 'blocked'
else:
r = 'offline'
print 'getSlutState(%s) = %s' % (id, r) if len(profile['pictures']) > 0:
return r pic_regex = re.compile('(?P<base_url>http://.+\.adopteunmec\.com/.+/)image(?P<id>.+)\.jpg')
pic_max_id = max(int(pic_regex.match(pic['url']).groupdict()['id']) for pic in profile['pictures'])
base_url = pic_regex.match(profile['pictures'][0]['url']).groupdict()['base_url']
for id in xrange(1, pic_max_id + 1):
url = u'%simage%s.jpg' % (base_url, id)
if not url in [pic['url'] for pic in profile['pictures']]:
profile['pictures'].append({'url': url, u'hidden': True, 'id': u'0', 'rating': 0.0})
@pageaccess return profile
def is_slut_online(self, id):
result = self.openurl('http://www.adopteunmec.com/%s' % id).read()
r = result.find('<td align="right" style="font-size:12px;font-weight:bold">en ligne</td>') >= 0
print 'isSlutOnline(%s) = %s' % (id, r)
return r
def _get_chat_infos(self): def _get_chat_infos(self):
try: 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: except ValueError:
raise BrowserUnavailable() raise BrowserUnavailable()
if json['error']: if data['error']:
raise ChatException(u'Error while getting chat infos. json:\n%s' % json) raise ChatException(u'Error while getting chat infos. json:\n%s' % data)
return json return data
def iter_contacts(self): def iter_contacts(self):
def iter_dedupe(contacts): def iter_dedupe(contacts):
@ -329,13 +273,13 @@ class AuMBrowser(BaseBrowser):
yield contact yield contact
yielded_ids.add(contact['id']) yielded_ids.add(contact['id'])
json = self._get_chat_infos() data = self._get_chat_infos()
return iter_dedupe(json['contacts']) return iter_dedupe(data['contacts'])
def iter_chat_messages(self, _id=None): def iter_chat_messages(self, _id=None):
json = self._get_chat_infos() data = self._get_chat_infos()
if json['messages'] is not None: if data['messages'] is not None:
for message in json['messages']: for message in data['messages']:
yield ChatMessage(id_from=message['id_from'], id_to=message['id_to'], message=message['message'], date=message['date']) 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): def send_chat_message(self, _id, message):

View file

@ -0,0 +1,251 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
from dateutil.parser import parse as parse_dt
from weboob.tools.ordereddict import OrderedDict
from weboob.capabilities.contact import Contact as _Contact, ProfileNode
from weboob.tools.misc import html2text
class FieldBase:
def __init__(self, key, key2=None):
self.key = key
self.key2 = key2
def get_value(self, value, consts):
raise NotImplementedError
class FieldStr(FieldBase):
def get_value(self, profile, consts):
return html2text(unicode(profile[self.key])).strip()
class FieldBool(FieldBase):
def get_value(self, profile, consts):
return bool(int(profile[self.key]))
class FieldIP(FieldBase):
def get_value(self, profile, consts):
s = profile[self.key]
if profile[self.key] != profile[self.key2]:
s += ' (first %s)' % profile[self.key2]
return s
class FieldProfileURL(FieldBase):
def get_value(self, profile, consts):
id = int(profile[self.key])
if id > 0:
return 'http://www.adopteunmec.com/%d' % id
else:
return ''
class FieldPopu(FieldBase):
def get_value(self, profile, consts):
return unicode(profile['popu'][self.key])
class FieldOld(FieldBase):
def get_value(self, profile, consts):
birthday = parse_dt(profile[self.key])
return int((datetime.now() - birthday).days / 365.25)
class FieldSplit(FieldBase):
def get_value(self, profile, consts):
return [html2text(s).strip() for s in profile[self.key].split(self.key2) if len(s.strip()) > 0]
class FieldBMI(FieldBase):
def __init__(self, key, key2, fat=False):
FieldBase.__init__(self, key, key2)
self.fat = fat
def get_value(self, profile, consts):
height = int(profile[self.key])
weight = int(profile[self.key2])
if height == 0 or weight == 0:
return ''
bmi = (weight/float(pow(height/100.0, 2)))
if not self.fat:
return bmi
elif bmi < 15.5:
return 'severely underweight'
elif bmi < 18.4:
return 'underweight'
elif bmi < 24.9:
return 'normal'
elif bmi < 30:
return 'overweight'
else:
return 'obese'
class FieldFlags(FieldBase):
def get_value(self, profile, consts):
i = int(profile[self.key])
labels = []
for d in consts[self.key]:
if i & (1 << int(d['value'])):
labels.append(html2text(d['label']).strip())
return labels
class FieldList(FieldBase):
def get_value(self, profile, consts):
i = int(profile[self.key])
for d in consts[self.key]:
if i == int(d['value']):
return html2text(d['label']).strip()
return ''
class Contact(_Contact):
TABLE = OrderedDict((
('_info', OrderedDict((
('IPaddr', FieldIP('last_ip', 'first_ip')),
('admin', FieldBool('admin')),
('ban', FieldBool('isBan')),
('first', FieldStr('first_cnx')),
('godfather', FieldProfileURL('godfather')),
))),
('_stats', OrderedDict((
('mails', FieldPopu('mails')),
('baskets', FieldPopu('contacts')),
('charms', FieldPopu('flashs')),
('visites', FieldPopu('visites')),
('invits', FieldPopu('invits')),
('bonus', FieldPopu('bonus')),
('score', FieldPopu('popu')),
))),
('details', OrderedDict((
('old', FieldOld('birthday')),
('birthday', FieldStr('birthday')),
('zipcode', FieldStr('zip')),
('location', FieldStr('city')),
('country', FieldStr('country')),
('eyes', FieldList('eyes')),
('hair_color', FieldList('hair_color')),
('hair_size', FieldList('hair_size')),
('height', FieldList('size')),
('weight', FieldList('weight')),
('BMI', FieldBMI('size', 'weight')),
('fat', FieldBMI('size', 'weight', fat=True)),
('origins', FieldList('origins')),
('signs', FieldFlags('checks1')),
('job', FieldStr('job')),
('style', FieldList('style')),
('food', FieldList('food')),
('drink', FieldList('drink')),
('smoke', FieldList('smoke')),
))),
('tastes', OrderedDict((
('hobbies', FieldStr('hobbies')),
('music', FieldSplit('music', '<br>')),
('cinema', FieldSplit('cinema', '<br>')),
('books', FieldSplit('books', '<br>')),
('tv', FieldSplit('tvs', '<br>')),
))),
('sex', OrderedDict((
('underwear', FieldFlags('checks7')),
('practices', FieldFlags('checks5')),
('favorite', FieldFlags('checks3')),
('toys', FieldFlags('checks6')),
))),
('personality', OrderedDict((
('snap', FieldStr('texts1')),
('exciting', FieldStr('texts2')),
('hate', FieldStr('texts3')),
('vices', FieldStr('texts4')),
('assets', FieldStr('texts6')),
('fantasies', FieldStr('texts5')),
('is', FieldFlags('checks2')),
)))
))
def parse_profile(self, profile, consts):
if int(profile['cat']) == 1:
self.status = Contact.STATUS_ONLINE
self.status_msg = u'online'
elif int(profile['cat']) == 2:
self.status = Contact.STATUS_AWAY
self.status_msg = u'away'
elif int(profile['cat']) == 3:
self.status = Contact.STATUS_OFFLINE
self.status_msg = u'last connexion %s' % profile['last_cnx']
self.summary = html2text(profile['about1']).strip().replace('\n\n', '\n')
if len(profile['about2']) > 0:
self.summary += u'\n\nLooking for:\n%s' % html2text(profile['about2']).strip().replace('\n\n', '\n')
for photo in profile['pictures']:
self.set_photo(photo['url'].split('/')[-1],
url=photo['url'],
thumbnail_url=photo['url'].replace('image', 'thumb1_'),
hidden=photo['hidden'])
self.profile = []
for section, d in self.TABLE.iteritems():
flags = ProfileNode.SECTION
if section.startswith('_'):
flags |= ProfileNode.HEAD
section = section.lstrip('_')
s = ProfileNode(section, section.capitalize(), [], flags=flags)
for key, builder in d.iteritems():
value = builder.get_value(profile, consts[int(profile['sex'])])
s.value.append(ProfileNode(key, key.capitalize().replace('_', ' '), value))
self.profile.append(s)
self.aum_profile = profile
def get_text(self):
def print_node(node, level=1):
result = u''
if node.flags & node.SECTION:
result += u'\t' * level + node.label + '\n'
for sub in node.value:
result += print_node(sub, level+1)
else:
if isinstance(node.value, (tuple,list)):
value = ', '.join(unicode(v) for v in node.value)
elif isinstance(node.value, float):
value = '%.2f' % node.value
else:
value = node.value
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
return result
result = u'Nickname: %s\n' % self.name
if self.status & Contact.STATUS_ONLINE:
s = 'online'
elif self.status & Contact.STATUS_OFFLINE:
s = 'offline'
elif self.status & Contact.STATUS_AWAY:
s = 'away'
else:
s = 'unknown'
result += u'Status: %s (%s)\n' % (s, self.status_msg)
result += u'URL: %s\n' % self.url
result += u'Photos:\n'
for name, photo in self.photos.iteritems():
result += u'\t%s\n' % photo
result += u'\nProfile:\n'
for head in self.profile:
result += print_node(head)
result += u'Description:\n'
for s in self.summary.split('\n'):
result += u'\t%s\n' % s
return result

View file

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.browser import BrowserUnavailable
class AdopteWait(BrowserUnavailable):
pass
class AdopteBanned(BrowserUnavailable):
pass
class AdopteCantPostMail(Exception):
pass

View file

@ -82,7 +82,7 @@ class ProfilesWalker(Optimization):
try: try:
with self.browser: with self.browser:
profile = self.browser.get_profile(id) 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 # Get score from the aum_score module
#d = self.nucentral_core.callService(context.Context.fromComponent(self), 'aum_score', 'score', profile) #d = self.nucentral_core.callService(context.Context.fromComponent(self), 'aum_score', 'score', profile)

View file

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Christophe Benz
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.base import PageBase
class AccountPage(PageBase):
def set_godfather(self, _id):
self.browser.select_form(name='setGodfather')
self.browser['godfather'] = _id
self.browser.submit()

View file

@ -1,116 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
from weboob.tools.browser import BasePage, BrowserUnavailable
from weboob.backends.aum.exceptions import AdopteBanned
class PageBase(BasePage):
def __init__(self, *args, **kwargs):
BasePage.__init__(self, *args, **kwargs)
# Check the 'oops' error message when adopteunmec guys are gay.
b = self.document.getElementsByTagName('body')[0]
for div in b.getElementsByTagName('div'):
if div.getAttribute('id') == 'oops':
raise BrowserUnavailable('Oops')
# Check when the account is temporarily blocked.
for img in self.document.getElementsByTagName('img'):
if img.getAttribute('src') == 'http://s.adopteunmec.com/img/exemple.jpg':
raise AdopteBanned('Your account is blocked. You have to unblock by yourself but we can\'t help you.')
def is_logged(self):
for form in self.browser.forms():
if form.name == 'form_login':
return False
return True
def open_contact_list_page(self):
self.browser.follow_link(url_regex=r"/mail.php$")
def open_thread_page(self, id, all_messages=False):
if all_messages:
self.browser.location('/thread.php?id=%d&see=all' % int(id))
else:
self.browser.location('/thread.php?id=%d' % int(id))
def score(self):
"""
<table width="220">
<tr>
<td align=left class=header>popularité</td>
<td align=right class=header><big style="color:#ff0198;" id=popScore>7.230</big> pts</td>
</tr>
</table>
"""
l = self.document.getElementsByTagName('table')
for tag in l:
if tag.getAttribute('width') == '220':
# <table><tbody(implicit)><tr><td>
child = tag.childNodes[0].childNodes[0].childNodes[3]
return int(child.childNodes[0].childNodes[1].data.replace(' ', '').strip())
self.logger.error("Error: I can't find the score :(")
return '0'
def __get_indicator(self, elementName):
""" <span id=mailsCounter><blink>1</blink></span> """
l = self.document.getElementsByTagName('span')
for tag in l:
if tag.getAttribute('id') == elementName:
child = tag.childNodes[0]
if not hasattr(child, 'data'):
if child.tagName != u'blink':
self.logger.warning("Warning: %s counter isn't a blink and hasn't data" % elementName)
child = child.childNodes[0]
if not hasattr(child, 'data'):
break
return int(child.data)
self.logger.error("Error: I can't find the %s counter :(" % elementName)
return 0
MYNAME_REGEXP = re.compile("Bonjour (.*)")
def get_my_name(self):
""" <span class=header2>Bonjour Romain</span> """
tags = self.document.getElementsByTagName('span')
for tag in tags:
if hasattr(tag.firstChild, 'data'):
m = self.MYNAME_REGEXP.match(tag.firstChild.data)
if m:
return m.group(1)
self.logger.warning('Warning: Unable to fetch name')
return '?'
def nb_new_mails(self):
return self.__get_indicator(u'mailsCounter')
def nb_new_baskets(self):
return self.__get_indicator(u'flashsCounter')
def nb_new_visites(self):
return self.__get_indicator(u'visitesCounter')

View file

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.profileslist_base import ProfilesListBase
class BasketsPage(ProfilesListBase):
pass

View file

@ -1,155 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
from datetime import datetime, timedelta
from dateutil import tz
from logging import warning
from weboob.backends.aum.pages.base import PageBase
class ContactItem:
u"""
<tr bgcolor="#ff7ccb" height=1><td colspan=10></td></tr>
<tr style="background:#ffd5ee" height=74 onmouseover="this.style.background='#f3d5e7'" onmouseout="this.style.background='#ffd5ee'">
<td class=a onclick="window.location='/thread.php?id=110921'" width=4></td>
<td class=a onclick="window.location='/rencontres-femmes/france/ile-de-france/Hen/110921'" width=70 align=left>
<table><tr valign="bottom"><td style="background:url(http://p1.adopteunmec.com/1/2/9/0/1/thumb0_7.jpg);width:66px;height:66px" align="right">&nbsp;</td></tr></table>
</td>
<td class=a onclick="window.location='/thread.php?id=110921'" width=150 align=left><big><b>Hen</b></big><br>
19ans, Montreuil</td>
<td class=a onclick="window.location='/thread.php?id=110921'" width=320 align=left><b>Comme ça, on est deux.</b><br>
il y a 1 heure</td>
<td class=a onclick="window.location='/thread.php?id=110921'" width=100 align=right>nouveau&nbsp;<img src="http://img.adopteunmec.com/img/ico_mail0.gif" />&nbsp;&nbsp;&nbsp;
</td>
<td class=a onclick="window.location='/thread.php?id=110921'" width=30 align=left></td>
<td width=20 align=right><input id='fcc_suppr_545615' name='suppr[]' type='hidden' /><img id='cc_suppr_545615' src='http://img.adopteunmec.com/img/i/check0.png' onclick='customCheckClick(this)' align="absmiddle"/>&nbsp;</td>
<script>supprs[supprs.length] = 'cc_suppr_545615';</script>
</td>
<td width=7></td>
</tr>
"""
fields = ['thread_link', 'photo', 'useless3', 'name', 'resume', 'status', 'useless', 'remove', 'useless2']
def __init__(self, tr):
self.tr = tr
self.id = 0
def __get_element(self, id):
return self.tr.getElementsByTagName('td')[self.fields.index(id)]
def get_name(self):
tag = self.__get_element('name')
node = tag.getElementsByTagName('b')[0].firstChild
if node:
name = node.data
else:
# it is possible if the user has left site and hasn't any nickname
name = ''
return name
def get_status(self):
tag = self.__get_element('status')
return tag.firstChild.data
def is_new(self):
return self.get_status() == u'nouveau'
def is_answered(self):
return self.get_status() == u'répondu'
def get_resume(self):
tag = self.__get_element('resume')
return tag.getElementsByTagName('b')[0].firstChild.data.strip()
def get_suppr_id(self):
tag = self.__get_element('remove')
return tag.getElementsByTagName('input')[0].getAttribute('id').split('_')[-1]
LASTMSG_RE = re.compile('il y a (\d+) (\w+)')
def get_lastmsg_date(self):
tag = self.__get_element('resume')
s = tag.childNodes[3].data
m = self.LASTMSG_RE.match(s)
if m:
d = {'secondes': 1,
'seconde': 1,
'minutes': 60,
'minute': 60,
'heures': 3600,
'heure': 3600,
'jours': 24*3600,
'jour': 24*3600,
'mois': 24*3600*30,
}
try:
i = int(m.group(1)) * d[m.group(2)]
except KeyError:
warning('Unable to parse lastmsg ("%s" is not a valid unit)' % m.group(2))
return None
else:
return datetime.now(tz=tz.tzutc()) - timedelta(seconds=i)
else:
warning('Unable to parse lastmsg [%s]' % s)
return None
def get_id(self):
if self.id:
return self.id
tag = self.__get_element('thread_link')
text = tag.getAttribute('onclick')
regexp = re.compile("window.location='/thread.php\?id=(\d+)'")
m = regexp.match(text)
if m:
self.id = int(m.group(1))
return self.id
warning('Unable to parse ID (%s)' % text)
return 0
class ContactListPage(PageBase):
def on_loaded(self):
self.items = []
form = self.document.getElementsByTagName('form')
if not form:
return
tags = form[0].childNodes[3].childNodes[1].childNodes
for tag in tags:
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
continue
if tag.hasAttribute('bgcolor'):
continue
self.items += [ContactItem(tag)]
def get_contact_list(self):
return self.items

View file

@ -1,331 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
import time
from datetime import datetime
from dateutil import tz
from logging import error, warning
from mechanize import FormNotFoundError
from weboob.backends.aum.pages.base import PageBase
from weboob.backends.aum.exceptions import AdopteCantPostMail
class MailParser(object):
"""
<td>
<table width="100%">
<tr valign="top">
<td width="1" bgcolor="black">
<td width="88" align="center">
<table class=a onclick="window.location='romainn'" style="background:url(http://img.adopteunmec.com/img/bg_mini.png);width:74px;height:85px">
<tr height=4>
<td></td>
</tr>
<tr valign=top height=66>
<td width=74 align=center style="-moz-opacity:0.5;opacity:0.5">
<table class=a onclick="window.location='romainn'" style="width:66px;background-image:url(http://p7.adopteunmec.com/7/7/0/8/7/4/thumb0_3.jpg);background-repeat:no-repeat">
<tr height=2><td></td></tr>
<tr height=31 valign=top>
<td width=2></td>
<td align=left><img width=7 heght=7 src='http://img.adopteunmec.com/img/i/null.png'id=status_ /></td>
</tr>
<tr height=31 valign=bottom>
<td width=2></td>
<td width=63 align=right id=online_>
<blnk><blink><img src="http://img.adopteunmec.com/img/online0.png" width=7 height=7 /></blink></blnk>
<img src="http://img.adopteunmec.com/img/online1.png" width=25 height=10 />
</td>
<td width=3></td>
</tr>
<tr height=2><td></td></tr>
<tr><td colspan=10 align=center style="font-size:10px;font-weight:bold;letter-spacing:-1px">Romain</td></tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<p style="color:#444444;font-size:10px">27 octobre 2008, 01:11:32, nouveau
<br />
</p>
<br>Moi en g<EFBFBD>n<EFBFBD>ral j'aime sortir tout simplement dans des bars, pour discuter avec mes amis et/ou coll<6C>gues, et rencontrer des gens. Sinon je fais de la guitare, et je d<>veloppe des projets perso.</p>
</td>
<td width="1" bgcolor="black">
</tr>
<tr height="6"><td width="1" bgcolor="black"><td colspan="2"></td><td width="1" bgcolor="black"></tr>
</table>
</td>
"""
DATETIME_REGEXP = re.compile(u'([0-9]{1,2}) ([a-zéû]*) ([0-9]{4}), ([0-9]{2}):([0-9]{2}):([0-9]{2})(.*)')
months = [u'', u'janvier', u'février', u'mars', u'avril', u'mai', u'juin', u'juillet', u'août', u'septembre', u'octobre', u'novembre', u'décembre']
SMILEY_REGEXP = re.compile('http://s.adopteunmec.com/www/img/smile/([0-9]+).gif')
smileys = {0: ':)',
1: ':D',
2: ':o',
3: ':p',
4: ';)',
5: ':(',
6: ':$',
7: ':\'(',
11: ':#',
14: ':\\',
15: ':|',
10: '(L)',
}
def __init__(self, thread_id, name, tr):
# <td> <table> implicit<tbody> <tr>
self.thread_id = thread_id
self.date = None
self.message_id = 0
self.title = 'Discussion with %s' % name
self.sender = name
self.name = name
self.tr = tr.childNodes[0].childNodes[1].childNodes[1].childNodes[0]
self.new = False
tds = self.tr.childNodes
counter = 0
for td in tds:
if not hasattr(td, 'tagName') or td.tagName != u'td':
continue
counter += 1
if counter == 3:
date = u''
for child in td.childNodes[1].childNodes:
if hasattr(child, 'data'):
date += child.data
self.parse_date(date)
content = ''
for c in td.childNodes[3].childNodes:
if hasattr(c, 'data'):
content += ''.join(c.data.split('\n')) # to strip \n
elif hasattr(c, 'tagName'):
if c.tagName == 'br':
content += '\n'
elif c.tagName == 'img' and c.hasAttribute('src'):
m = self.SMILEY_REGEXP.match(c.getAttribute('src'))
if m and self.smileys.has_key(int(m.group(1))):
try:
content += self.smileys[int(m.group(1))]
except KeyError:
error('Mail: unable to find this smiley: %s' % c.getAttribute('src'))
self.content = content.strip()
break
self.parse_profile_link()
self.parse_from()
@property
def date_int(self):
return int(time.strftime('%Y%m%d%H%M%S', self.date.timetuple()))
def set_parent_message_id(self, date):
self.parent_message_id = date
def parse_date(self, date_str):
# To match regexp, we have to remove any return chars in string
# before the status ('nouveau', 'lu', etc)
date_str = date_str.strip()
m = self.DATETIME_REGEXP.match(date_str)
if m:
day = int(m.group(1))
month = self.months.index(m.group(2))
year = int(m.group(3))
hour = int(m.group(4))
minute = int(m.group(5))
sec = int(m.group(6))
# build datetime object with local timezone
d = datetime(year, month, day, hour, minute, sec, tzinfo=tz.tzlocal())
# then, convert it to UTC timezone
d = d.astimezone(tz.tzutc())
# and get timestamp
self.date = d
self.message_id = self.date_int
self.id = '%s.%s' % (self.thread_id, self.message_id)
if m.group(7).find('nouveau') >= 0:
self.new = True
else:
error('Error: unable to parse the datetime string "%s"' % date_str)
def parse_profile_link(self):
tables = self.tr.getElementsByTagName('div')
for table in tables:
if table.hasAttribute('class') and table.getAttribute('class') == 'mini' and table.hasAttribute('onclick'):
text = table.getAttribute('onclick')
regexp = re.compile("window.location='(.*)'")
m = regexp.match(text)
if m:
self.profile_link = m.group(1)
self.signature = u'Profile: http://www.adopteunmec.com%s' % self.profile_link
return
warning('Unable to find the profile URL in the message %s@%s' % (self.sender, self.id))
def parse_from(self):
tds = self.tr.getElementsByTagName('div')
for td in tds:
if not td.hasAttribute('class') or td.getAttribute('class') != 'mini_pseudo':
continue
if td.childNodes:
self.sender = td.childNodes[0].data
return
warning('Warning: unable to find from in the mail %s' % self.id)
class ContactThreadPage(PageBase):
"""
<form name=sendMsg method="post" action="/thread.php?id=1567992">
<table align=center width=700>
<tr valign="top"><td width=370 align="left">
<table width=370>
<tr height=25 style="background:url(http://img.adopteunmec.com/img/bg_list1.gif)"><td colspan=4>&nbsp;&nbsp;<font color=#ff0198>Ecrire <20> <big><b>zoe</b></big></font></td></tr>
<tr height=193 valign=top><td width=1 bgcolor=black></td><td colspan=2 bgcolor=white><textarea id=message name=message style="width:366px;height:190px;border:none"></textarea></td><td width=1 bgcolor=black></td></tr>
<tr height=1><td width=1 bgcolor=black></td><td bgcolor="#d15c91" colspan=2></td><td width=1 bgcolor=black></td></tr>
<tr height=1><td width=1 bgcolor=black></td><td bgcolor="#ffffff" colspan=2></td><td width=1 bgcolor=black></td></tr>
<tr><td width=1 bgcolor=black></td><td bgcolor="#ffe8f3">
<table><tr>
<td width=8></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/0.gif" style="cursor:pointer" onclick="emote(':)')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/1.gif" style="cursor:pointer" onclick="emote(':D')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/2.gif" style="cursor:pointer" onclick="emote(':o')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/3.gif" style="cursor:pointer" onclick="emote(':p')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/4.gif" style="cursor:pointer" onclick="emote(';)')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/5.gif" style="cursor:pointer" onclick="emote(':(')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/6.gif" style="cursor:pointer" onclick="emote(':$')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/7.gif" style="cursor:pointer" onclick="emote(':\'(')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/11.gif" style="cursor:pointer" onclick="emote(':#')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/14.gif" style="cursor:pointer" onclick="emote(':\\')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/15.gif" style="cursor:pointer" onclick="emote(':|')"></td>
<td width=23><img src="http://img.adopteunmec.com/img/smile/10.gif" style="cursor:pointer" onclick="emote('(L)')"></td>
</tr></table>
</td><td bgcolor="#ffe8f3" align=right><input type="image" src="http://img.adopteunmec.com/img/buttonSendMail.jpg" style="border:none"></td><td width=1 bgcolor=black></td></tr>
<tr height=1><td width=1 bgcolor=black colspan=4></tr>
</table>
</td><td width=304 align="right">
<iframe frameborder=0 scrolling=NO style='width:300px;height:250px;boder:0' src='http://graphs.adopteunmec.com/ads/index.php?i=5'></iframe>
</td></tr>
</table>
<input type=hidden id=link name=link>
</form>
"""
def post(self, content):
if isinstance(content, unicode):
# LOL the data is Windows-1252 encoded, even if the charset label
# of the page is ISO-8859-1.
# That's crappy, but necessary if we want to use special chars.
content = content.encode('Windows-1252', 'replace')
if len(content) < 3:
raise AdopteCantPostMail("Your message is too short (minimum 3 chars)")
try:
self.browser.select_form(name="sendMsg")
self.browser['message'] = content
self.browser.submit() # submit current form
except FormNotFoundError, e:
error = u'Unknown error (%s)' % e
p_list = self.document.getElementsByTagName('p')
for p in p_list:
if p.hasAttribute('align') and p.getAttribute('align') == 'center':
error = p.childNodes[-1].data.encode('utf-8').strip()
break
raise AdopteCantPostMail(error)
"""
<table align=center style="width:700px;background:black">
<tr style="height:1px">
<td>
</td>
</tr>
<tr bgcolor="#ffe9f6" valign="top">
content
</tr>
...
<tr height="25" style="background:url(http://img.adopteunmec.com/img/bg_list1.gif)"><td>&nbsp;&nbsp;<a href="/thread.php?id=1567992&see=all">voir tous les messages</a></td></tr>
</table>
"""
id_regexp = re.compile("/thread.php\?id=([0-9]+)")
def on_loaded(self):
self.mails = []
a_list = self.document.getElementsByTagName('a')
self.id = 0
for a in a_list:
if a.hasAttribute('href'):
m = self.id_regexp.match(a.getAttribute('href'))
if m:
self.id = int(m.group(1))
break
self.name = ''
big_list = self.document.getElementsByTagName('big')
for big in big_list:
for child in big.childNodes:
if hasattr(child, 'tagName') and child.tagName == u'b':
self.name = child.childNodes[0].data.strip()
break
tables = self.document.getElementsByTagName('table')
table = None
for t in tables:
if t.hasAttribute('style') and t.getAttribute('style') == 'width:700px;background:black':
table = t
break
if not table:
# It is probably the 'sent' page
return
for tag in table.childNodes[1].childNodes[1:]:
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
continue
if not tag.hasAttribute('valign'):
continue
mail = MailParser(self.id, self.name, tag)
if self.mails:
self.mails[-1].set_parent_message_id(mail.date_int)
self.mails += [mail]

View file

@ -1,80 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.base import PageBase
class EditPhotoPage(PageBase):
def add_photo(self, name, f):
self.browser.select_form(name="form")
self.browser.find_control('uploaded').add_file(f, 'image/jpeg', name)
self.browser.submit()
self.browser.openurl('http://www.adopteunmec.com/home.php')
class EditPhotoCbPage(PageBase):
# Do nothing
pass
class EditAnnouncePage(PageBase):
def set_nickname(self, nickname):
self.browser.select_form(name="form")
self.browser['pseudo'] = nickname
self.browser.submit()
def set_announce(self, **kwargs):
self.browser.select_form(name="form")
self.browser.set_field(kwargs, 'title')
self.browser.set_field(kwargs, 'description', field='about1')
self.browser.set_field(kwargs, 'lookingfor', field='about2')
self.browser.submit()
class EditDescriptionPage(PageBase):
SHAPES = ['--', 'svelte', 'sportive', u'équilibrée', 'pulpeuse', u'généreuse', 'normale']
HAIR_COLORS = ['--', 'blancs', 'gris', 'noirs', 'bruns', 'chatains', 'roux', 'blonds', 'platines', u'colorés']
HAIR_SIZES = ['--', u'rasés', 'courts', 'mi-longs', 'longs']
EYES = ['--', 'noirs', 'marrons', 'noisettes', 'bleus', 'verts', 'gris']
ORIGINS = ['--', u'européennes', 'afro', 'maghrebines', 'asiatiques', u'métisses', 'eurasiennes', 'latines']
STYLES = ['--', 'fashion', 'chic', 'sport', u'décontracté', 'rock', u'bohème', 'masculin', 'dark', 'excentrique', 'electro', 'skate']
FOODS = ['--', 'mange de tout', 'piscovore', u'végétarien', u'végétalien', 'bio']
DRINKS = ['--', 'jamais', 'de temps en temps', 'souvent', 'pilier de bar']
SMOKES = ['--', u'ne tolère pas la fumée', u'tolère la fumée', 'fume de temps en temps', 'fume souvent']
def set_description(self, **kwargs):
self.browser.select_form(name='form')
self.browser.set_field(kwargs, 'height', field='size', is_list=True)
self.browser.set_field(kwargs, 'weight', is_list=True)
self.browser.set_field(kwargs, 'shape', is_list=self.SHAPES)
self.browser.set_field(kwargs, 'hair_color', is_list=self.HAIR_COLORS)
self.browser.set_field(kwargs, 'hair_size', is_list=self.HAIR_SIZES)
self.browser.set_field(kwargs, 'eyes', is_list=self.EYES)
self.browser.set_field(kwargs, 'origins', is_list=self.ORIGINS)
self.browser.set_field(kwargs, 'style', is_list=self.STYLES)
self.browser.set_field(kwargs, 'food', is_list=self.FOODS)
self.browser.set_field(kwargs, 'drink', is_list=self.DRINKS)
self.browser.set_field(kwargs, 'smoke', is_list=self.SMOKES)
self.browser.submit()
class EditSexPage(PageBase):
pass
class EditPersonalityPage(PageBase):
pass

View file

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
from weboob.backends.aum.pages.base import PageBase
class HomePage(PageBase):
MYID_REGEXP = re.compile("http://www.adopteunmec.com/\?mid=(\d+)")
def get_my_id(self):
fonts = self.document.getElementsByTagName('font')
for font in fonts:
m = self.MYID_REGEXP.match(font.firstChild.data)
if m:
return m.group(1)
self.browser.logger.error("Error: Unable to find my ID")
return 0
def __get_home_indicator(self, pos, what):
tables = self.document.getElementsByTagName('table')
for table in tables:
if table.hasAttribute('style') and table.getAttribute('style') == 'background-color:black;background-image:url(http://s.adopteunmec.com/img/barmec.gif);background-repeat:no-repeat':
fonts = table.getElementsByTagName('font')
i = 0
for font in fonts:
if font.hasAttribute('color') and font.getAttribute('color') == '#ff0198':
i += 1
if i == pos:
return int(font.firstChild.data)
self.browser.logger.error(u'Could not parse number of %s' % what)
return 0
def nb_available_charms(self):
return self.__get_home_indicator(3, 'available charms')
def nb_godchilds(self):
return self.__get_home_indicator(2, 'godchilds')

View file

@ -1,146 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
import mechanize
from weboob.tools.mech import ClientForm
from weboob.tools.browser import BrowserIncorrectPassword
from weboob.backends.aum.exceptions import AdopteBanned
from weboob.capabilities.account import AccountRegisterError
from .base import PageBase
from .home import HomePage
from ..captcha import Captcha
class LoginPage(HomePage):
def login(self, login, password):
try:
self.browser.select_form(name="form_login")
except mechanize._mechanize.FormNotFoundError:
return
self.browser['login'] = login
self.browser['password'] = password
self.browser.submit() # submit current form
class RegisterPage(PageBase):
def on_loaded(self):
display_errors = False
for div in self.document.getElementsByTagName('div'):
if div.getAttribute('class') == 'balloon':
display_errors = True
break
if not display_errors:
return
errors = []
for script in self.document.getElementsByTagName('script'):
for child in script.childNodes:
if child and child.data.find('dispErrors') >= 0:
for m in re.finditer('"(\w+)": "(.*)",', child.data):
errors.append(m.group(2))
raise AccountRegisterError(u'Unable to register account: %s' % ', '.join(errors))
def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country):
"""
Form name=register (#1)
## ## __Name__________________ __Type___ __ID________ __Value__________________
1 sent1time hidden (None)
2 sex radio sex-0 [] of ['0', '1']
3 birthday0 select birthday0 ['0'] of ['0', '1', '2', '3', '4', ' ...
4 birthday1 select birthday1 ['0'] of ['0', '1', '2', '3', '4', ' ...
5 birthday2 select birthday2 ['0'] of ['0', '1992', '1991', '1990 ...
6 country select country ['0'] of ['0', 'fr', 'be', 'ch', 'ca']
7 zip text zip
8 email text email
9 pass password pass
10 pass_retype password pass_retype
11 captcha text captcha
12 swear_adult checkbox swear_adult [] of ['on']
13 want_goods checkbox want_goods [] of ['on']
"""
c = Captcha(self.browser.openurl('/captcha.php'))
self.browser.select_form(name='register')
self.browser.set_all_readonly(False)
try:
self.browser['sex'] = [str(sex)]
except ClientForm.ItemNotFoundError:
raise AccountRegisterError('Please give a right sex! (1 or 0)')
try:
self.browser['birthday0'] = [str(birthday_d)]
self.browser['birthday1'] = [str(birthday_m)]
self.browser['birthday2'] = [str(birthday_y)]
except ClientForm.ItemNotFoundError:
raise AccountRegisterError('Please give a right birthday date!')
try:
self.browser['country'] = [str(country)]
except ClientForm.ItemNotFoundError:
raise AccountRegisterError('Please select a right country!')
self.browser['zip'] = str(zipcode)
self.browser['email'] = self.browser.username
self.browser['pass'] = password
self.browser['pass_retype'] = password
self.browser['captcha'] = c.text
self.browser['swear_adult'] = ['on']
self.browser['want_goods'] = []
self.browser.submit()
class RegisterWaitPage(PageBase):
pass
class RegisterConfirmPage(PageBase):
pass
class RedirectPage(PageBase):
def on_loaded(self):
for link in self.browser.links():
print link
self.browser.location('/wait.php')
class BanPage(PageBase):
def on_loaded(self):
raise AdopteBanned('Your IP address is banned.')
class ShopPage(PageBase):
def on_loaded(self):
self.browser.location('http://www.adopteunmec.com/account.php')
class ErrPage(PageBase):
def on_loaded(self):
raise BrowserIncorrectPassword('Incorrect login/password')
class InvitePage(PageBase):
MYID_REGEXP = re.compile("http://www.adopteunmec.com/\?mid=(\d+)")
def get_my_id(self):
fonts = self.document.getElementsByTagName('font')
for font in fonts:
m = self.MYID_REGEXP.match(font.firstChild.data)
if m:
return m.group(1)
self.browser.logger.error("Error: Unable to find my ID")
return 0

View file

@ -1,446 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.base import PageBase
from weboob.tools.ordereddict import OrderedDict
from copy import deepcopy
from logging import warning
import re
class FieldBase:
def __init__(self, key):
self.key = key
def put_value(self, d, value):
raise NotImplementedError
class FieldString(FieldBase):
def put_value(self, d, value):
d[self.key] = unicode(value)
class FieldList(FieldBase):
def put_value(self, d, value):
d[self.key] = value.split(', ')
class FieldWideList(FieldBase):
def put_value(self, d, value):
d[self.key] += [value]
class FieldOld(FieldBase):
regexp = re.compile(u'([0-9]+) ans( \(Née le ([0-9]+) ([^ ]+) ([0-9]+)\))?')
month2i = ['', 'janvier', u'février', 'mars', 'avril', 'mai', 'juin', 'juillet', u'août', 'septembre', 'octobre', 'novembre', u'décembre']
def put_value(self, d, value):
m = self.regexp.match(value)
if not m:
return
d[self.key] = int(m.group(1))
if not m.group(2):
return
try:
d['birthday'] = (int(m.group(3)),
self.month2i.index(m.group(4)),
int(m.group(5)))
except ValueError, e:
print str(e)
class FieldLocation(FieldBase):
location = re.compile('(?P<location>.+?)( \((?P<zipcode>[0-9]{5})\))?, (?P<country>.*)')
def __init__(self):
FieldBase.__init__(self, '')
def put_value(self, d, value):
# TODO: determine distance, or something like
m = self.location.match(value)
if m:
for field in ('country', 'location', 'zipcode'):
d[field] = m.groupdict().get(field)
else:
warning('Unable to parse the location "%s"' % value)
d['location'] = unicode(value)
class FieldMeasurements(FieldBase):
height = re.compile('([0-9]{1,3}) cm')
weight = re.compile('([0-9]{1,3}) kg')
# TODO: parse third parameter
def __init__(self):
FieldBase.__init__(self, '')
def put_value(self, d, value):
for s in value.split(', '):
m = self.height.match(s)
if m:
d['height'] = int(m.group(1))
continue
m = self.weight.match(s)
if m:
d['weight'] = int(m.group(1))
continue
if d['height'] and d['weight']:
bmi = (d['weight']/float(pow(d['height']/100.0, 2)))
if bmi < 15.5:
d['fat'] = 'severely underweight'
elif bmi < 18.4:
d['fat'] = 'underweight'
elif bmi < 24.9:
d['fat'] = 'normal'
elif bmi < 30:
d['fat'] = 'overweight'
else:
d['fat'] = 'obese'
d['BMI'] = bmi
class FieldParticularSignes(FieldBase):
def __init__(self): FieldBase.__init__(self, '')
def put_value(self, d, value):
for s in value.split(', '):
if s.find('tatouages') >= 0:
d['tatoos'] = True
elif s.find('piercing') >= 0:
d['piercing'] = True
elif s.find('lunettes') >= 0:
d['glasses'] = True
elif s.find('rousseur') >= 0:
d['freckle'] = True
class ProfilePage(PageBase):
empty_table = OrderedDict((
('details', OrderedDict((
('old', 0),
('birthday', (0,0,0)),
('zipcode', 0),
('location', ''),
('country', ''),
('eyes', ''),
('hairs', []),
('height', 0),
('weight', 0),
('BMI', 0),
('fat', ''),
('from', ''),
('tatoos', False),
('piercing', False),
('freckle', False),
('glasses', False),
('job', ''),
('style', ''),
('alimentation', ''),
('alcool', ''),
('tabac', ''),
))),
('liking', OrderedDict((
('activities', ''),
('music', []),
('cinema', []),
('books', []),
('tv', []),
))),
('sex', OrderedDict((
('underwear', []),
('top', []),
('bottom', []),
('interval', ''),
('favorite', []),
('practices', []),
('toys', []),
))),
('personality', OrderedDict((
('snap', ''),
('exciting', ''),
('hate', ''),
('vices', ''),
('assets', ''),
('fantasies', ''),
('is', []),
))),
))
tables = {'tab_0': 'details',
'tab_1': 'liking',
'tab_2': 'sex',
'tab_3': 'personality'
}
fields = {'details': {'Age': FieldOld('old'),
u'Réside à': FieldLocation(),
'Yeux': FieldString('eyes'),
'Cheveux': FieldList('hairs'),
'Mensurations ': FieldMeasurements(),
'Origines ': FieldString('from'),
'Signes particuliers ': FieldParticularSignes(),
'Style ': FieldString('style'),
'Profession ': FieldString('job'),
'Alimentation': FieldString('alimentation'),
'Alcool': FieldString('alcool'),
'Tabac': FieldString('tabac'),
},
'liking': {'Hobbies ': FieldString('activities'),
'Musique ': FieldWideList('music'),
u'Cinéma': FieldWideList('cinema'),
'Livres ': FieldWideList('books'),
u'Télé': FieldWideList('tv'),
},
'sex': {u'Sous-v\xeatements ': FieldList('underwear'),
'... en haut ': FieldList('top'),
'... en bas ': FieldList('bottom'),
u'Fréquence idéale des rapports sexuels ':
FieldString('interval'),
u'Position favorite ': FieldList('favorite'),
'Pratiques sexuelles ': FieldList('practices'),
u'Accessoires préférés ':FieldList('toys'),
u'Ce qui se cache en dessous ':
FieldList('underwear'),
u"Ce qui m'\xe9moustille ":
FieldList('favorite'),
u"Au lit j'aime ": FieldList('practices'),
u'Mes accessoires ': FieldList('toys'),
},
'personality': {u'Ça la fait craquer ': FieldString('snap'),
u'Ça l\'excite ': FieldString('exciting'),
u'Elle déteste ': FieldString('hate'),
'Ses vices ': FieldString('vices'),
'Ses atouts ': FieldString('assets'),
'Ses fantasmes ': FieldString('fantasies'),
'Elle est ': FieldList('is'),
},
}
ID_REGEXP = re.compile('(charm|addBasket|openAlbum)\(([0-9]+)(,[\s\'\d]+)?\)')
PHOTO_REGEXP = re.compile('http://(s|p)([0-9]+)\.adopteunmec\.com/(.*)')
STATS2ID = {'visites': 'visits',
'charmes': 'charms',
'paniers': 'baskets',
'mails': 'mails',
'POPULARIT': 'score',
}
STATS_VALUE_REGEXP = re.compile('([0-9\s]+).*')
def __repr__(self):
if isinstance(self.name, unicode):
name = self.name.encode('utf-8', 'backslashreplace')
else:
name = self.name
return '<Profile name="%s">' % name
def on_loaded(self):
self.name = u''
self.description = u''
self.table = deepcopy(self.empty_table)
self.id = 0
self.photos = []
self.status = ''
self.stats = OrderedDict((
('score', 0),
('visits', 0),
('charms', 0),
('baskets', 0),
('mails', 0),
))
divs = self.document.getElementsByTagName('td')
for div in divs:
if (div.hasAttribute('style') and
div.getAttribute('style') == "color:#ffffff;font-size:32px;font-weight:bold;letter-spacing:-2px" and
hasattr(div.firstChild, 'data')):
if len(div.childNodes) > 1:
self.name = div.childNodes[1].data
if (div.hasAttribute('style') and
div.getAttribute('style') == "font-size:12px;font-weight:bold" and
hasattr(div.firstChild, 'data')):
self.status = div.childNodes[-1].data.strip()
if div.hasAttribute('background'):
m = self.PHOTO_REGEXP.match(div.getAttribute('background'))
if m:
self.photos.append(dict(url=re.sub(u'thumb[0-2]_', u'image', div.getAttribute('background')),
hidden=False))
if div.hasAttribute('width') and str(div.getAttribute('width')) == '226':
trs = div.getElementsByTagName('tr')
for tr in trs:
tds = tr.getElementsByTagName('td')
if len(tds) > 2 and hasattr(tds[2].firstChild, 'data'):
label = tds[0].firstChild.data
value = tds[2].firstChild.data
elif len(tds) == 2:
label = unicode(tds[0].childNodes[1].data)
value = tds[1].childNodes[1].data
else:
continue
m = self.STATS_VALUE_REGEXP.match(value)
if m and self.STATS2ID.has_key(label):
self.stats[self.STATS2ID[label]] = int(m.group(1).replace(' ', ''))
divs = self.document.getElementsByTagName('div')
for div in divs:
if div.hasAttribute('id'):
if div.getAttribute('id') == 'about_div':
self.parse_description(div)
if div.getAttribute('id').startswith('tab_'):
self.parse_table(div)
for tag in ('img', 'td'):
imgs = self.document.getElementsByTagName(tag)
for img in imgs:
if img.hasAttribute('onclick'):
m = self.ID_REGEXP.match(img.getAttribute('onclick'))
if m:
self.id = int(m.group(2))
break
if self.id:
break
if len(self.photos) == 0:
return
# find hidden photos.
photo_regex = re.compile('(?P<base_url>http://.+\.adopteunmec\.com/.+/)image(?P<id>.+)\.jpg')
photo_max_id = max(int(photo_regex.match(photo['url']).groupdict()['id']) for photo in self.photos)
base_url = photo_regex.match(self.photos[0]['url']).groupdict()['base_url']
for id in xrange(1, photo_max_id + 1):
url = '%simage%s.jpg' % (base_url, id)
if not url in [photo['url'] for photo in self.photos]:
self.photos.append(dict(url=url, hidden=True))
def parse_description(self, div):
# look for description
description = ''
for c in div.childNodes:
if hasattr(c, 'data'):
description += ''.join(c.data.split('\n')) # to strip \n
elif hasattr(c, 'tagName') and c.tagName == 'br':
description += '\n'
elif hasattr(c, 'tagName') and c.tagName == 'i':
description += ''.join(c.childNodes[0].data.split('\n'))
self.description = description.strip()
def parse_table(self, div):
d = self.table[self.tables[div.getAttribute('id')]]
fields = self.fields[self.tables[div.getAttribute('id')]]
table = div.getElementsByTagName('table')[1]
field1 = None
field2 = None
for tr in table.getElementsByTagName('tr'):
tds = tr.getElementsByTagName('td')
if len(tds) != 2:
continue
label1 = ''
label2 = ''
value1 = ''
value2 = ''
# Check for first column
if len(tds[0].childNodes) > 0:
b = len(tds[0].childNodes) > 2 and tds[0].childNodes[2]
if b and hasattr(b, 'tagName') and b.tagName == 'b':
for child in b.childNodes:
label1 += child.data
else:
for child in tds[0].childNodes:
if child.data != u'\xa0': # strip nbsp
value1 += child.data
value2 = value2.strip()
# Check for second column
if len(tds[1].childNodes) > 0:
b = tds[1].childNodes[0]
if b and hasattr(b, 'tagName') and b.tagName == 'b':
for child in b.firstChild.childNodes:
label2 += child.data
else:
for child in tds[1].childNodes:
if hasattr(child, 'data') and child.data != u'\xa0': # strip nbsp
value2 += child.data
if label1 and value2:
# This is a typically tuple of key/value on the line.
try:
fields[label1].put_value(d, value2)
except KeyError:
self.logger.warning('Unable to find "%s" (%s)' % (label1, repr(label1)))
elif label1 and label2:
# two titles, so there will have a list of value in
# next lines on each columns
field1 = fields[label1]
field2 = fields[label2]
elif not label1 and not label1:
# two values, so it is a line of values
if field1 and value1:
field1.put_value(d, value1)
if field2 and value2:
field2.put_value(d, value2)
def get_name(self):
return self.name
def get_description(self):
return self.description
def get_table(self):
return self.table
def get_id(self):
return self.id
def get_photos(self):
return self.photos
def get_status(self):
return self.status
def is_online(self):
return self.status.find('en ligne') >= 0
def get_stats(self):
return self.stats
def get_profile_text(self):
body = u'Status: %s' % unicode(self.status)
if self.photos:
body += u'\nPhotos:'
for photo in self.photos:
body += u'\n\t\t%s%s' % (unicode(photo['url']), (' (hidden)' if photo['hidden'] else ''))
body += u'\nStats:'
for label, value in self.get_stats().iteritems():
body += u'\n\t\t%-15s %s' % (label + ':', value)
body += u'\n\nInformations:'
for section, d in self.get_table().iteritems():
body += u'\n\t%s\n' % section
for key, value in d.items():
key = '%s:' % key
if isinstance(value, list):
body += u'\t\t%-15s %s\n' % (key, u', '.join(unicode(s) for s in value))
elif isinstance(value, float):
body += u'\t\t%-15s %.2f\n' % (key, value)
else:
body += u'\t\t%-15s %s\n' % (key, unicode(value))
body += u'\n\nDescription:\n%s' % unicode(self.get_description())
return body

View file

@ -1,50 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.base import PageBase
import re
class ProfilesListBase(PageBase):
PROFILE_REGEXP = re.compile(".*window\.location='(.*)'")
PHOTO_REGEXP = re.compile(".*background-image:url\(([A-Za-z0-9_\-:/\.]+)\).*")
WITHOUT_PHOTO = 'http://s.adopteunmec.com/img/thumb0.gif'
SHOW_WITHOUT_PHOTO = True
def on_loaded(self):
self.id_list = []
a_list = self.document.getElementsByTagName('div')
for a in a_list:
if a.hasAttribute('onclick') and a.hasAttribute('class') and a.getAttribute('class') in ('small', 'mini'):
m = self.PROFILE_REGEXP.match(a.getAttribute('onclick'))
if m:
url = m.group(1).split('/')[-1]
m = self.PHOTO_REGEXP.match(a.getElementsByTagName('div')[0].getAttribute('style'))
if url != 'home.php' and not url in self.id_list and \
m and (self.SHOW_WITHOUT_PHOTO or m.group(1) != self.WITHOUT_PHOTO):
self.id_list.append(url)
def get_profiles_ids(self):
return set(self.id_list)
def get_profiles_ids_list(self):
return self.id_list

View file

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.profileslist_base import ProfilesListBase
class SearchPage(ProfilesListBase):
SHOW_WITHOUT_PHOTO = False
def search(self, **kwargs):
self.browser.select_form(name="form")
self.browser.set_all_readonly(False)
self.browser.set_field(kwargs, 'ageMin', is_list=True)
self.browser.set_field(kwargs, 'ageMax', is_list=True)
self.browser.set_field(kwargs, 'country', is_list=True)
self.browser.set_field(kwargs, 'dist', is_list=True)
self.browser.set_field(kwargs, 'nickname', field='pseudo')
self.browser.set_field(kwargs, 'save', value='true')
# XXX ho, it doesn't work anymore :(
#self.browser['originsV[]'] = ['1'] # excludes niggers (it doesn't work :( )
self.browser.submit()

View file

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2008-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.backends.aum.pages.base import PageBase
from weboob.backends.aum.exceptions import AdopteWait
from time import sleep
class WaitPage(PageBase):
def on_loaded(self):
raise AdopteWait()
def check(self):
result = self.browser.openurl('http://www.adopteunmec.com/fajax_checkEnter.php?anticache=0.46168455299380795').read()
return result == 'Ok'
def process_wait(self):
while not self.check():
sleep(10)
self.browser.location('/home.php')