7 from diaspy
.streams
import Outer
8 from diaspy
.models
import Aspect
9 from diaspy
import errors
10 from diaspy
import search
13 def sephandle(handle
):
14 """Separate Diaspora* handle into pod pod and user.
16 :returns: two-tuple (pod, user)
18 if re
.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', handle
) is None:
19 raise errors
.InvalidHandleError('{0}'.format(handle
))
20 handle
= handle
.split('@')
21 pod
, user
= handle
[1], handle
[0]
26 """This class abstracts a D* user.
27 This object goes around the limitations of current D* API and will
28 extract user data using black magic.
29 However, no chickens are harmed when you use it.
31 The parameter fetch should be either 'posts', 'data' or 'none'. By
32 default it is 'posts' which means in addition to user data, stream
33 will be fetched. If user has not posted yet diaspy will not be able
34 to extract the information from his/her posts. Since there is no official
35 way to do it we rely on user posts. If this will be the case user
36 will be notified with appropriate exception message.
38 If fetch is 'data', only user data will be fetched. If the user is
39 not found, no exception will be returned.
41 When creating new User() one can pass either guid, handle and/or id as
42 optional parameters. GUID takes precedence over handle when fetching
43 user stream. When fetching user data, handle is required.
46 def parse(cls
, connection
, data
):
47 person
= data
.get('person')
49 raise errors
.KeyMissingFromFetchedData('person', data
)
51 guid
= person
.get('guid')
53 raise errors
.KeyMissingFromFetchedData('guid', person
)
55 handle
= person
.get('diaspora_id')
57 raise errors
.KeyMissingFromFetchedData('diaspora_id', person
)
59 person_id
= person
.get('id')
61 raise errors
.KeyMissingFromFetchedData('id', person
)
63 return User(connection
, guid
, handle
, id)
65 def __init__(self
, connection
, guid
='', handle
='', fetch
='posts', id=0):
66 self
._connection
= connection
75 def __getitem__(self
, key
):
79 return self
.data
.get('guid', '<guid missing>')
82 return '{0} ({1})'.format(self
.handle(), self
.guid())
85 return self
.data
.get('diaspora_id', 'Unknown handle')
88 return self
.data
.get('guid', '<guid missing>')
90 def _fetchstream(self
):
91 self
.stream
= Outer(self
._connection
, guid
=self
['guid'])
93 def _fetch(self
, fetch
):
94 """Fetch user posts or data.
97 if self
['handle'] and not self
['guid']: self
.fetchhandle()
98 else: self
.fetchguid()
99 elif fetch
== 'data' and self
['handle']:
102 def _finalize_data(self
, data
):
103 """Adjustments are needed to have similar results returned
104 by search feature and fetchguid()/fetchhandle().
108 def _postproc(self
, request
):
109 """Makes necessary modifications to user data and
112 :param request: request object
113 :type request: request
115 if request
.status_code
!= 200: raise Exception('wrong error code: {0}'.format(request
.status_code
))
116 data
= request
.json()
117 self
.data
= self
._finalize
_data
(data
)
119 def fetchhandle(self
, protocol
='https'):
120 """Fetch user data and posts using Diaspora handle.
122 pod
, user
= sephandle(self
['handle'])
123 request
= self
._connection
.get('{0}://{1}/u/{2}.json'.format(protocol
, pod
, user
), direct
=True)
124 self
._postproc
(request
)
128 """Fetch user data and posts using guid.
131 request
= self
._connection
.get('people/{0}.json'.format(self
['guid']))
132 self
._postproc
(request
)
135 raise errors
.UserError('GUID not set')
137 def fetchprofile(self
):
138 """Fetches user data.
140 data
= search
.Search(self
._connection
).user(self
['handle'])
142 raise errors
.UserError('user with handle "{0}" has not been found on pod "{1}"'.format(self
['handle'], self
._connection
.pod
))
146 def aspectMemberships(self
):
147 return self
.data
.get('contact', {}).get('aspect_memberships', [])
150 """Returns XML string containing user HCard.
152 request
= self
._connection
.get('hcard/users/{0}'.format(self
['guid']))
153 if request
.status_code
!= 200: raise errors
.UserError('could not fetch hcard for user: {0}'.format(self
['guid']))
158 """Object represetnting current user.
160 _userinfo_regex
= re
.compile(r
'window.current_user_attributes = ({.*})')
161 _userinfo_regex_2
= re
.compile(r
'gon.user=({.*});gon.preloads')
163 def __init__(self
, connection
):
164 self
._connection
= connection
167 """This function returns the current user's attributes.
171 request
= self
._connection
.get('bookmarklet')
172 userdata
= self
._userinfo
_regex
.search(request
.text
)
173 if userdata
is None: userdata
= self
._userinfo
_regex
_2.search(request
.text
)
174 if userdata
is None: raise errors
.DiaspyError('cannot find user data')
175 userdata
= userdata
.group(1)
176 return json
.loads(userdata
)
180 """This class represents user's list of contacts.
182 def __init__(self
, connection
):
183 self
._connection
= connection
185 def add(self
, user_id
, aspect_ids
):
186 """Add user to aspects of given ids.
188 :param user_id: user guid
190 :param aspect_ids: list of aspect ids
191 :type aspect_ids: list
193 for aid
in aspect_ids
: Aspect(self
._connection
, aid
).addUser(user_id
)
195 def remove(self
, user_id
, aspect_ids
):
196 """Remove user from aspects of given ids.
198 :param user_id: user guid
200 :param aspect_ids: list of aspect ids
201 :type aspect_ids: list
203 for aid
in aspect_ids
: Aspect(self
._connection
, aid
).removeUser(user_id
)
205 def get(self
, set=''):
206 """Returns list of user contacts.
207 Contact is a User() who is in one or more of user's
210 By default, it will return list of users who are in
213 If `set` is `all` it will also include users who only share
214 with logged user and are not in his/hers aspects.
216 If `set` is `only_sharing` it will return users who are only
217 sharing with logged user and ARE NOT in his/hers aspects.
219 :param set: if passed could be 'all' or 'only_sharing'
223 if set: params
['set'] = set
225 request
= self
._connection
.get('contacts.json', params
=params
)
226 if request
.status_code
!= 200:
227 raise Exception('status code {0}: cannot get contacts'.format(request
.status_code
))
228 return [User
.parse(self
._connection
, each
) for each
in request
.json()]