tuenpy.py
TRANSCRIPT
-
8/9/2019 tuenpy.py
1/7
# -*- coding: utf-8 -*-import libxml2domimport mechanizeimport urllib
TUENTI_HOST = "http://m.tuenti.com"PROFILE_URL = "/?m=profile&user_id="URLS = { 'login': TUENTI_HOST + "/?m=login&func=process_login", 'friends': TUENTI_HOST + "/?m=friends&page=%s", 'profile': TUENTI_HOST + "/?m=profile&user_id=%s", 'comments': TUENTI_HOST + "/?m=profile&func=view_wall&user_id=%s&fpi=%s", 'my_profile': TUENTI_HOST + "/?m=profile&func=my_profile", 'set_status': TUENTI_HOST + "/?m=profile&func=process_set_status&from=home&csfr=%s", 'create_blog_entry': TUENTI_HOST + "/?t=profile_send_blog&ot=blog_entry&m=blog_entry&uid=%s",}MESSAGES = { 'login_error': "Dirección de correo o contraseña inválidos.", 'more_friends': ">Ver más amigos", 'more_comments': ">Ver más comentarios del tablón", 'wall': u"Tablón de",
'my_wall': u"Mi tablón", 'restricted_area': "Acceso Restringido", 'status': "Estado", 'status_verb': u"está",}HTTP_RESPONSE_CODES = { 'OK': 200, 'CREATED': 201, 'ACCEPTED': 202, 'BAD_REQUEST': 400, 'UNAUTHORIZED': 401, 'NOT_FOUND': 404, 'CONFLICT': 409,
'PRECONDITION_FAILED': 412}
class StatusException(Exception): """Response Error class""" def __init__(self, value, result=None): self.value = value self.responses = { 100: ('Continue', 'Request received, please continue'), 101: ('Switching Protocols', 'Switching to new protocol; obey Upgrade header'), 200: ('OK', 'Request fulfilled, document follows'),
201: ('Created', 'Document created, URL follows'), 202: ('Accepted', 'Request accepted, processing continues off-line'), 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), 204: ('No Content', 'Request fulfilled, nothing follows'), 205: ('Reset Content', 'Clear input form for further input.'), 206: ('Partial Content', 'Partial content follows.'), 300: ('Multiple Choices', 'Object has several resources -- see URI list'), 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
-
8/9/2019 tuenpy.py
2/7
302: ('Found', 'Object moved temporarily -- see URI list'), 303: ('See Other', 'Object moved -- see Method and URL list'), 304: ('Not Modified', 'Document has not changed since given time'), 305: ('Use Proxy', 'You must use proxy specified in Location to access this ' 'resource.'), 307: ('Temporary Redirect', 'Object moved temporarily -- see URI list'), 400: ('Bad Request', 'Bad request syntax or unsupported method'), 401: ('Unauthorized', 'No permission -- see authorization schemes'), 402: ('Payment Required', 'No payment -- see charging schemes'), 403: ('Forbidden', 'Request forbidden -- authorization will not help'), 404: ('Not Found', 'Nothing matches the given URI'), 405: ('Method Not Allowed', 'Specified method is invalid for this server.'), 406: ('Not Acceptable', 'URI not available in preferred format.'), 407: ('Proxy Authentication Required', 'You must authenticate with ' 'this proxy before proceeding.'), 408: ('Request Timeout', 'Request timed out; try again later.'),
409: ('Conflict', 'Request conflict.'), 410: ('Gone', 'URI no longer exists and has been permanently removed.'), 411: ('Length Required', 'Client must specify Content-Length.'), 412: ('Precondition Failed', 'Precondition in headers is false.'), 413: ('Request Entity Too Large', 'Entity is too large.'), 414: ('Request-URI Too Long', 'URI is too long.'), 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), 416: ('Requested Range Not Satisfiable', 'Cannot satisfy request range.'), 417: ('Expectation Failed', 'Expect condition could not be satisfied.'), 500: ('Internal Server Error', 'Server got itself in trouble'),
501: ('Not Implemented', 'Server does not support this operation'), 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), 503: ('Service Unavailable', 'The server cannot process the request due to a high load'), 504: ('Gateway Timeout', 'The gateway server did not receive a timely response'), 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), } if result: self.result = "\n%s" % result
def __str__(self):
return "Error [%s]: %s. %s.%s" % (self.value, self.responses[self.value][0], self.responses[self.value][1], self.result)
class API(object): """Main class""" def __init__(self, email, password, *args, **kwargs): self._br = mechanize.Browser() data = {'email': email, 'password': password}
-
8/9/2019 tuenpy.py
3/7
self.encoded_data = urllib.urlencode(data) response = self._br.open(URLS['login'], self.encoded_data) status = response.code body = response.read() if (status == HTTP_RESPONSE_CODES['OK'] and MESSAGES['login_error'] not in body): self.Profile = Profile(self._br) self.Friends = Friends(self._br) self.Comments = Comments(self._br, self.Profile.id) else: raise StatusException(401, "Email or password are invalid.")
class User(object): """User base class""" def __init__(self, br, user_id, *args, **kwargs): self._br = br self.id = user_id
def _get_first_name(self): pass first_name = property(_get_first_name)
def _get_last_name(self):
pass last_name = property(_get_last_name)
def _get_name(self): pass name = property(_get_name)
def _get_birthdate(self): pass birthdate = property(_get_birthdate)
def _get_marital_status(self): pass
marital_status = property(_get_marital_status)
def _get_province(self): pass province = property(_get_province)
def _get_locality(self): pass locality = property(_get_locality)
def _get_hobbies(self): pass hobbies = property(_get_hobbies)
def _get_films(self): pass films = property(_get_films)
def _get_quotes(self): pass quotes = property(_get_quotes)
def _get_music(self):
-
8/9/2019 tuenpy.py
4/7
pass music = property(_get_music)
def _get_books(self): pass books = property(_get_books)
class Profile(User): """Profile class""" def __init__(self, br, *args, **kwargs): self._br = br user_id = self._get_user_id() if not user_id: raise StatusException(404, "User not found in tuenti.") else: super(Profile, self).__init__(br, user_id, *args, **kwargs) self._csfr = self._get_csfr() if not self._csfr: raise StatusException(404, "csfr key not found in profile.")
def _get_user_id(self): response = self._br.open(URLS['my_profile']) status = response.code
body = response.read() if status == HTTP_RESPONSE_CODES['OK']: doc = libxml2dom.parseString(body, html=1) fieldsets = doc.getElementsByTagName('fieldset') for fieldset in fieldsets: if fieldset.getAttribute('title') == MESSAGES['my_wall']: anchor = fieldset.getElementsByTagName('a').pop() user_id = anchor.getAttribute('href').split('user_id=')[1].split('&')[0] return user_id
def _get_csfr(self): response = self._br.open(TUENTI_HOST)
status = response.code body = response.read() if status == HTTP_RESPONSE_CODES['OK']: doc = libxml2dom.parseString(body, html=1) forms = doc.getElementsByTagName('go') if forms: return forms[0].getAttribute('href').split('csfr=')[1].split('&')[0]
def _set_status(self, value): if value: data = {'status': value} else:
data = {'status': ""} self.encoded_data = urllib.urlencode(data) response = self._br.open(URLS['set_status'] % self._csfr, self.encoded_data) status = response.code body = response.read() if (status != HTTP_RESPONSE_CODES['OK'] or MESSAGES['restricted_area'] in body): raise StatusException(304, "Status not modified.") def _get_status(self):
-
8/9/2019 tuenpy.py
5/7
response = self._br.open(URLS['my_profile']) status = response.code body = response.read() if status == HTTP_RESPONSE_CODES['OK']: doc = libxml2dom.parseString(body, html=1) fieldsets = doc.getElementsByTagName("fieldset") for fieldset in fieldsets: if (len(fieldset.childNodes) > 2 and fieldset.childNodes[0].toString() == MESSAGES['status']): status_node_value = fieldset.childNodes[2].value if status_node_value: status_message = status_node_value.split(MESSAGES['status_verb'])[1].strip() return status_message return None status = property(_get_status, _set_status)
def _get_first_name(self): pass first_name = property(_get_first_name)
def _get_last_name(self): pass last_name = property(_get_last_name)
def _get_name(self): pass name = property(_get_name)
class Friends(object): """Friend collection class""" def __init__(self, br, *args, **kwargs): self._br = br cont = 0 response = self._br.open(URLS['friends'] % str(cont)) status = response.code
body = response.read() self._friends = [] while (status == HTTP_RESPONSE_CODES['OK'] and MESSAGES['more_friends'] in body): doc = libxml2dom.parseString(body, html=1) anchors = doc.getElementsByTagName('a') for anchor in anchors: if anchor and anchor.getAttribute('href').startswith(PROFILE_URL): friend_dic = {} friend_dic['user_id'] = anchor.getAttribute('href').split(PROFILE_URL)[1].split('&')[0] friend_dic['nick'] = anchor.textContent
self._friends.append(friend_dic) cont = cont + 1 response = self._br.open(URLS['friends'] % str(cont)) status = response.code body = response.read() doc = libxml2dom.parseString(body, html=1) anchors = doc.getElementsByTagName('a') for anchor in anchors: if anchor and anchor.getAttribute('href').startswith(PROFILE_URL): friend_dic = {}
-
8/9/2019 tuenpy.py
6/7
friend_dic['user_id'] = anchor.getAttribute('href').split(PROFILE_URL)[1].split('&')[0] friend_dic['nick'] = anchor.textContent self._friends.append(friend_dic)
def get(self, index): f = self._friends[index] return Friend(self._br, f.get('user_id', None), f.get('nick', None))
def all(self): friend_list = [] for f in self._friends: friend_list.append(Friend(self._br, f.get('user_id', None), f.get('nick', None))) return friend_list
class Friend(User): """Friend base class""" def __init__(self, br, user_id, nick, *args, **kwargs): super(Friend, self).__init__(br, user_id, *args, **kwargs) self.nick = nick self.Comments = Comments(self._br, user_id)
class Comments(object): """Comment collection class""" def __init__(self, br, user_id, *args, **kwargs): self._br = br self.user_id = user_id cont = 0 response = self._br.open(URLS['comments'] % (self.user_id, cont)) status = response.code body = response.read() self._comments = [] while(status == HTTP_RESPONSE_CODES['OK'] and MESSAGES['more_comments'] in body):
doc = libxml2dom.parseString(body, html=1) fieldsets = doc.getElementsByTagName('fieldset') for fieldset in fieldsets: if (fieldset.getAttribute('title') and (fieldset.getAttribute('title') == MESSAGES['my_wall'] or fieldset.getAttribute('title').startswith(MESSAGES['wall']))): smalls = fieldset.getElementsByTagName('small') for small in smalls[1:]: comment_dic = {} anchor = small.getElementsByTagName('a')[0] if anchor and '?m=profile&user_id' in anchor.getAttribute('href'):
comment_dic.update({'friend_id': anchor.getAttribute('href').split('user_id=')[1]}) comment_dic.update({'friend_name': anchor.textContent}) text = anchor.nextSibling.textContent splitted_text = text.split('(') time = splitted_text.pop() time_parsed = time.split(')')[0] text_parsed = text[2:len(text)-len(time)-3] comment_dic.update({'text': text_parsed})
-
8/9/2019 tuenpy.py
7/7
comment_dic.update({'time': time_parsed}) self._comments.append(comment_dic) cont = cont + 3 response = self._br.open(URLS['comments'] % (self.user_id, cont)) status = response.code body = response.read() doc = libxml2dom.parseString(body, html=1) fieldsets = doc.getElementsByTagName('fieldset') for fieldset in fieldsets: if (fieldset.getAttribute('title') and (fieldset.getAttribute('title') == MESSAGES['my_wall'] or fieldset.getAttribute('title').startswith(MESSAGES['wall']))): smalls = fieldset.getElementsByTagName('small') for small in smalls[1:]: comment_dic = {} anchor = small.getElementsByTagName('a')[0] if anchor and '?m=profile&user_id' in anchor.getAttribute('href'): comment_dic.update({'friend_id': anchor.getAttribute('href').split('user_id=')[1]}) comment_dic.update({'friend_name': anchor.textContent}) text = anchor.nextSibling.textContent splitted_text = text.split('(') time = splitted_text.pop()
time_parsed = time.split(')')[0] text_parsed = text[2:len(text)-len(time)-3] comment_dic.update({'text': text_parsed}) comment_dic.update({'time': time_parsed}) self._comments.append(comment_dic)
def get(self, index): # TODO: Create an Friend instance return self._comments[index]
def all(self): # TODO: Retrieve a Friend collection
return self._comments
def latest(self, entries=5): # TODO: Retrieve a Friend collection return self._comments[:entries]
class Comment(object): """Comment base class""" def __init__(self, br, user_id, *args, **kwargs): self._br = br self.user_id # TODO: Get Comment
__all__ = ['API']