Small fix in manual
[diaspy.git] / diaspy / client.py
CommitLineData
a993a4b6
MK
1import requests
2import re
3import json
95d2d310 4import diaspy.models
a993a4b6 5
4685fc31 6
a993a4b6 7class Client:
d930e127 8 """This is the client class to connect to Diaspora.
a993a4b6 9 """
a7661afd
MK
10 def __init__(self, pod, username, password):
11 """
12 :param pod: The complete url of the diaspora pod to use.
13 :type pod: str
14 :param username: The username used to log in.
15 :type username: str
16 :param password: The password used to log in.
17 :type password: str
a7661afd 18 """
a993a4b6
MK
19 self._token_regex = re.compile(r'content="(.*?)"\s+name="csrf-token')
20 self.pod = pod
21 self.session = requests.Session()
313c9233 22 self._post_data = {}
94c2d637 23 self._setlogindata(username, password)
a1a09a06 24 self._login()
a993a4b6 25
d88b0f94
MM
26 def _sessionget(self, string):
27 """This method gets data from session.
28 Performs additional checks if needed.
29
30 Example:
31 To obtain 'foo' from pod one should call `_sessionget('foo')`.
32
33 :param string: URL to get without the pod's URL and slash eg. 'stream'.
34 :type string: str
35 """
36 return self.session.get('{0}/{1}'.format(self.pod, string))
37
e475a9d0
MM
38 def _sessionpost(self, string, data, headers={}, params={}):
39 """This method posts data to session.
40 Performs additional checks if needed.
41
42 Example:
43 To post to 'foo' one should call `_sessionpost('foo', data={})`.
44
45 :param string: URL to post without the pod's URL and slash eg. 'status_messages'.
46 :type string: str
47 :param data: Data to post.
48 :param headers: Headers.
49 :type headers: dict
50 :param params: Optional parameters.
51 :type params: dict
52 """
53 string = '{0}/{1}'.format(self.pod, string)
54 if headers and params: r = self.session.post(string, data=data, headers=headers, params=params)
55 elif headers and not params: r = self.session.post(string, data=data, headers=headers)
56 elif not headers and params: r = self.session.post(string, data=data, params=params)
57 else: r = self.session.post(string, data=data)
58 return r
59
a993a4b6 60 def get_token(self):
ae221396
MK
61 """This function gets a token needed for authentication in most cases
62
63 :returns: string -- token used to authenticate
ae221396 64 """
d88b0f94 65 r = self._sessionget('stream')
a993a4b6
MK
66 token = self._token_regex.search(r.text).group(1)
67 return token
68
03e37d6d
MM
69 def _setlogindata(self, username, password):
70 """This function is used to set data for login.
94c2d637 71
a7661afd 72 .. note::
03e37d6d 73 It should be called before _login() function.
a993a4b6 74 """
94c2d637 75 self._username, self._password = username, password
03e37d6d
MM
76 self._login_data = {
77 'user[username]': self._username,
78 'user[password]': self._password,
79 'authenticity_token': self.get_token(),
80 }
a993a4b6 81
03e37d6d
MM
82 def _login(self):
83 """This function is used to connect to the pod and log in.
03e37d6d 84 """
d930e127
MM
85 r = self.session.post('{0}/users/sign_in'.format(self.pod),
86 data=self._login_data,
a7661afd 87 headers={'accept': 'application/json'})
d930e127
MM
88
89 if r.status_code != 201: raise Exception('{0}: Login failed.'.format(r.status_code))
313c9233 90
a1a09a06
MM
91 def _setpostdata(self, text, aspect_id, photos):
92 """This function prepares data for posting.
93
313c9233
MM
94 :param text: Text to post.
95 :type text: str
96 :param aspect_id: Aspect id to send post to.
97 :type aspect_id: str
98 """
a1a09a06
MM
99 data = {}
100 data['aspect_id'] = aspect_id
101 data['status_message'] = {'text': text}
313c9233
MM
102 if photos: data['photos'] = photos
103 self._post_data = data
104
a1a09a06
MM
105 def _post(self):
106 """Sends post to an aspect.
313c9233 107
a1a09a06 108 :returns: diaspy.models.Post -- the Post which has been created
313c9233 109 """
d930e127 110 r = self.session.post('{0}/status_messages'.format(self.pod),
a1a09a06 111 data=json.dumps(self._post_data),
28ca595a
MK
112 headers={'content-type': 'application/json',
113 'accept': 'application/json',
114 'x-csrf-token': self.get_token()})
d930e127 115 if r.status_code != 201: raise Exception('{0}: Post could not be posted.'.format(r.status_code))
313c9233 116
95d2d310 117 return diaspy.models.Post(str(r.json()['id']), self)
313c9233 118
28ca595a 119 def post(self, text, aspect_id='public', photos=None):
a993a4b6
MK
120 """This function sends a post to an aspect
121
122 :param text: Text to post.
123 :type text: str
124 :param aspect_id: Aspect id to send post to.
125 :type aspect_id: str
126
95d2d310 127 :returns: diaspy.models.Post -- the Post which has been created
a993a4b6 128 """
a1a09a06 129 self._setpostdata(text, aspect_id, photos)
9fae7c88
MM
130 post = self._post()
131 self._post_data = {}
132 return post
a993a4b6
MK
133
134 def get_user_info(self):
135 """This function returns the current user's attributes.
136
137 :returns: dict -- json formatted user info.
a993a4b6 138 """
81367a60 139 r = self._sessionget('bookmarklet')
a993a4b6
MK
140 regex = re.compile(r'window.current_user_attributes = ({.*})')
141 userdata = json.loads(regex.search(r.text).group(1))
142 return userdata
b356c9f9 143
28ca595a 144 def post_picture(self, filename):
a1a09a06
MM
145 """This method posts a picture to D*.
146
147 :param filename: Path to picture file.
148 :type filename: str
149 """
28ca595a
MK
150 aspects = self.get_user_info()['aspects']
151 params = {}
152 params['photo[pending]'] = 'true'
153 params['set_profile_image'] = ''
154 params['qqfile'] = filename
155 for i, aspect in enumerate(aspects):
156 params['photo[aspect_ids][%d]' % (i)] = aspect['id']
157
158 data = open(filename, 'rb')
159
160 headers = {'content-type': 'application/octet-stream',
161 'x-csrf-token': self.get_token(),
162 'x-file-name': filename}
163
d930e127 164 r = self.session.post('{0}/photos'.format(self.pod),
4685fc31 165 params=params, data=data, headers=headers)
28ca595a
MK
166
167 return r
168
b356c9f9
MK
169 def get_stream(self):
170 """This functions returns a list of posts found in the stream.
171
172 :returns: list -- list of Post objects.
b356c9f9 173 """
af4eb01a 174 r = self._sessionget('stream.json')
b356c9f9 175
af4eb01a 176 if r.status_code != 200:
d930e127 177 raise Exception('wrong status code: {0}'.format(r.status_code))
b356c9f9
MK
178
179 stream = r.json()
af4eb01a 180 return [ diaspy.models.Post(str(post['id']), self) for post in stream ]
33f21ecf
MK
181
182 def get_notifications(self):
183 """This functions returns a list of notifications.
184
185 :returns: list -- list of json formatted notifications
33f21ecf 186 """
af4eb01a 187 r = self._sessionget('notifications.json')
33f21ecf 188
af4eb01a 189 if r.status_code != 200:
d930e127 190 raise Exception('wrong status code: {0}'.format(r.status_code))
33f21ecf
MK
191
192 notifications = r.json()
193 return notifications
3d3dff8f 194
3d3dff8f 195 def get_mentions(self):
4685fc31
MK
196 """This functions returns a list of
197 posts the current user is being mentioned in.
3d3dff8f
MK
198
199 :returns: list -- list of Post objects
3d3dff8f 200 """
bebe44b5 201 r = self._sessionget('mentions.json')
3d3dff8f
MK
202
203 if r.status_code != 200:
d930e127 204 raise Exception('wrong status code: {0}'.format(r.status_code))
3d3dff8f
MK
205
206 mentions = r.json()
4f59eb52 207 return [ diaspy.models.Post(str(post['id']), self) for post in mentions ]
5c2b6162 208
4685fc31
MK
209 def get_tag(self, tag):
210 """This functions returns a list of posts containing the tag.
211 :param tag: Name of the tag
212 :type tag: str
213
214 :returns: list -- list of Post objects
4685fc31 215 """
4f59eb52 216 r = self._sessionget('tags/{0}.json'.format(tag))
4685fc31
MK
217
218 if r.status_code != 200:
d930e127 219 raise Exception('wrong status code: {0}'.format(r.status_code))
4685fc31
MK
220
221 tagged_posts = r.json()
4f59eb52 222 return [ diaspy.models.Post(str(post['id']), self) for post in tagged_posts ]
4685fc31 223
264336e2
MM
224 def get_mailbox(self):
225 """This functions returns a list of messages found in the conversation.
226
227 :returns: list -- list of Conversation objects.
228 """
229 r = self._sessionget('conversations.json')
230
231 if r.status_code != 200:
232 raise Exception('wrong status code: {0}'.format(r.status_code))
233
234 mailbox = r.json()
235 return [ diaspy.conversations.Conversation(str(conversation['conversation']['id']), self) for conversation in mailbox ]
236
5c2b6162
MK
237 def add_user_to_aspect(self, user_id, aspect_id):
238 """ this function adds a user to an aspect.
239
240 :param user_id: User ID
241 :type user_id: str
242 :param aspect_id: Aspect ID
243 :type aspect_id: str
244
245 """
246
247 data = {'authenticity_token': self.get_token(),
248 'aspect_id': aspect_id,
249 'person_id': user_id}
250
d930e127 251 r = self.session.post('{0}/aspect_memberships.json'.format(self.pod),
5c2b6162
MK
252 data=data)
253
254 if r.status_code != 201:
d930e127 255 raise Exception('wrong status code: {0}'.format(r.status_code))
5c2b6162
MK
256 return r.json()
257
e475a9d0
MM
258 def add_aspect(self, aspect_name, visible=0):
259 """ This function adds a new aspect.
260 """
261
262 data = {'authenticity_token': self.get_token(),
263 'aspect[name]': aspect_name,
264 'aspect[contacts_visible]': visible}
265
266 r = self.session.post('{0}/aspects'.format(self.pod),
267 data=data)
268
269 if r.status_code != 200:
270 raise Exception('wrong status code: {0}'.format(r.status_code))
271
5c2b6162
MK
272 def remove_user_from_aspect(self, user_id, aspect_id):
273 """ this function removes a user from an aspect.
274
275 :param user_id: User ID
276 :type user_id: str
277 :param aspect_id: Aspect ID
278 :type aspect_id: str
279
280 """
281
282 data = {'authenticity_token': self.get_token(),
283 'aspect_id': aspect_id,
284 'person_id': user_id}
285
d930e127 286 r = self.session.delete('{0}/aspect_memberships/42.json'.format(self.pod),
5c2b6162
MK
287 data=data)
288
289 if r.status_code != 200:
d930e127 290 raise Exception('wrong status code: {0}'.format(r.status_code))
5c2b6162
MK
291
292 return r.json()
22cb1646 293
22cb1646
MK
294 def remove_aspect(self, aspect_id):
295 """ This function adds a new aspect.
296 """
22cb1646
MK
297 data = {'authenticity_token': self.get_token()}
298
d930e127 299 r = self.session.delete('{0}/aspects/{1}'.format(self.pod, aspect_id),
4685fc31 300 data=data)
22cb1646
MK
301
302 if r.status_code != 404:
d930e127 303 raise Exception('wrong status code: {0}'.format(r.status_code))
91d2d5dc 304
91d2d5dc 305 def new_conversation(self, contacts, subject, text):
264336e2 306 """Start a new conversation.
91d2d5dc
B
307
308 :param contacts: recipients ids, no guids, comma sperated.
309 :type contacts: str
310 :param subject: subject of the message.
311 :type subject: str
312 :param text: text of the message.
313 :type text: str
91d2d5dc 314 """
91d2d5dc
B
315 data = {'contact_ids': contacts,
316 'conversation[subject]': subject,
317 'conversation[text]': text,
318 'utf8': '✓',
319 'authenticity_token': self.get_token()}
320
d930e127
MM
321 r = self.session.post('{0}/conversations/'.format(self.pod),
322 data=data,
323 headers={'accept': 'application/json'})
91d2d5dc 324 if r.status_code != 200:
d930e127 325 raise Exception('{0}: Conversation could not be started.'.format(r.status_code))
91d2d5dc
B
326
327 return r.json()