import re
from diaspy.streams import Outer
from diaspy.models import Aspect
+from diaspy import errors
+from diaspy import search
-class User:
+def sephandle(handle):
+ """Separate Diaspora* handle into pod pod and user.
+
+ :returns: two-tuple (pod, user)
+ """
+ if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', handle) is None:
+ raise errors.InvalidHandleError('{0}'.format(handle))
+ handle = handle.split('@')
+ pod, user = handle[1], handle[0]
+ return (pod, user)
+
+
+class User():
"""This class abstracts a D* user.
This object goes around the limitations of current D* API and will
extract user data using black magic.
However, no chickens are harmed when you use it.
- If user has not posted yet diaspy will not be able to extract the information
- from his/her posts. Since there is no official way to do it we rely
- on user posts. If this will be the case user will be notified with appropriate
- exception message.
+ The parameter fetch should be either 'posts', 'data' or 'none'. By
+ default it is 'posts' which means in addition to user data, stream
+ will be fetched. If user has not posted yet diaspy will not be able
+ to extract the information from his/her posts. Since there is no official
+ way to do it we rely on user posts. If this will be the case user
+ will be notified with appropriate exception message.
- When creating new User() one can pass either guid or handle as
- an optional parameter. GUID takes precedence over handle.
- """
- data = {}
- stream = []
+ If fetch is 'data', only user data will be fetched. If the user is
+ not found, no exception will be returned.
- def __init__(self, connection, guid='', handle=''):
+ When creating new User() one can pass either guid, handle and/or id as
+ optional parameters. GUID takes precedence over handle when fetching
+ user stream. When fetching user data, handle is required.
+ """
+ def __init__(self, connection, guid='', handle='', fetch='posts', id=0):
self._connection = connection
- self.guid, self.handle = guid, handle
- if handle and guid: self.fetchguid(guid)
- elif guid and not handle: self.fetchguid(guid)
- elif handle and not guid: self.fetchhandle(handle)
+ self.stream = []
+ self.data = {
+ 'guid': guid,
+ 'handle': handle,
+ 'id': id,
+ }
+ self._fetch(fetch)
def __getitem__(self, key):
return self.data[key]
- def _sephandle(self, handle):
- """Separate D* handle into pod pod and user.
+ def __str__(self):
+ return self['guid']
+
+ def __repr__(self):
+ return '{0} ({1})'.format(self['diaspora_name'], self['guid'])
- :param handle: diaspora id: user@pod.example.com
- :type handle: str
- :returns: two-tuple (pod, user)
+ def _fetchstream(self):
+ self.stream = Outer(self._connection, location='people/{0}.json'.format(self['guid']))
+
+ def _fetch(self, fetch):
+ """Fetch user posts or data.
+ """
+ if fetch == 'posts':
+ if self['handle'] and not self['guid']: self.fetchhandle()
+ else: self.fetchguid()
+ elif fetch == 'data' and self['handle']:
+ self.fetchprofile()
+
+ def _finalize_data(self, data):
+ """Adjustments are needed to have similar results returned
+ by search feature and fetchguid()/fetchhandle().
"""
- if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', handle) is None:
- raise Exception('invalid handle: {0}'.format(handle))
- handle = handle.split('@')
- pod, user = handle[1], handle[0]
- return (pod, user)
+ names = [('id', 'id'),
+ ('guid', 'guid'),
+ ('name', 'name'),
+ ('avatar', 'avatar'),
+ ('handle', 'diaspora_id'),
+ ]
+ final = {}
+ for f, d in names:
+ final[f] = data[d]
+ return final
def _postproc(self, request):
"""Makes necessary modifications to user data and
:param request: request object
:type request: request
"""
- if request.status_code != 200:
- raise Exception('wrong error code: {0}'.format(request.status_code))
- else:
- request = request.json()
-
- if not len(request): raise ('Cannot extract user data: no posts to analyze')
- data = request[0]['author']
- final = {}
- names = [('id', 'id'),
- ('diaspora_id', 'diaspora_id'),
- ('guid', 'guid'),
- ('name', 'diaspora_name'),
- ('avatar', 'image_urls'),
- ]
- for d, f in names:
- final[f] = data[d]
- self.data = final
- self.stream = Outer(self._connection, location='people/{0}.json'.format(self.data['guid']))
+ if request.status_code != 200: raise Exception('wrong error code: {0}'.format(request.status_code))
+ request = request.json()
+ if not len(request): raise errors.UserError('cannot extract user data: no posts to analyze')
+ self.data = self._finalize_data(request[0]['author'])
- def fetchhandle(self, diaspora_id, protocol='https'):
- """Fetch user data using Diaspora handle.
+ def fetchhandle(self, protocol='https'):
+ """Fetch user data and posts using Diaspora handle.
"""
- pod, user = self._sephandle(diaspora_id)
- request = self._connection.session.get('{0}://{1}/u/{2}.json'.format(protocol, pod, user))
+ pod, user = sephandle(self['handle'])
+ request = self._connection.get('{0}://{1}/u/{2}.json'.format(protocol, pod, user), direct=True)
self._postproc(request)
+ self._fetchstream()
- def fetchguid(self, guid):
- """Fetch user data using guid.
+ def fetchguid(self):
+ """Fetch user data and posts using guid.
"""
- request = self._connection.get('people/{0}.json'.format(guid))
- self._postproc(request)
+ if self['guid']:
+ request = self._connection.get('people/{0}.json'.format(self['guid']))
+ self._postproc(request)
+ self._fetchstream()
+ else:
+ raise errors.UserError('GUID not set')
+
+ def fetchprofile(self):
+ """Fetches user data.
+ """
+ data = search.Search(self._connection).user(self['handle'])[0]
+ self.data = data
+
+
+class Me():
+ """Object represetnting current user.
+ """
+ _userinfo_regex = re.compile(r'window.current_user_attributes = ({.*})')
+ _userinfo_regex_2 = re.compile(r'gon.user=({.*});gon.preloads')
+
+ def __init__(self, connection):
+ self._connection = connection
+
+ def getInfo(self, fetch=False):
+ """This function returns the current user's attributes.
+
+ :returns: dict -- json formatted user info.
+ """
+ request = self._connection.get('bookmarklet')
+ userdata = self._userinfo_regex.search(request.text)
+ if userdata is None: userdata = self._userinfo_regex_2.search(request.text)
+ if userdata is None: raise errors.DiaspyError('cannot find user data')
+ userdata = userdata.group(1)
+ return json.loads(userdata)
+
class Contacts():
request = self._connection.get('contacts.json', params=params)
if request.status_code != 200:
raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
- contacts = [User(user['guid']) for user in request.json()]
- return contacts
+ return [User(self._connection, guid=user['guid'], handle=user['handle'], fetch=None) for user in request.json()]