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