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