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