Regular expressions fixed
[diaspy.git] / diaspy / client.py
index fcb12dbc70de876d28d3cd4ca5e04924796d972c..a4540f74cc198677696209095d6bda60c8d959e2 100644 (file)
-import requests
-import re
-import json
 import diaspy.models
+import diaspy.streams
+import diaspy.connection
+from diaspy import notifications
 
 
 class Client:
-    """This is the client class to connect to Diaspora.
+    """This is the client class used to interact with Diaspora.
+    It can be used as a reference implementation of client using diaspy.
     """
-    def __init__(self, pod, username, password):
+    def __init__(self, pod, username='', password=''):
         """
-        :param pod: The complete url of the diaspora pod to use.
+        `pod` can also be a diaspy.connection.Connection type and
+        Client() will detect it. When giving a connection there is no need
+        to pass username and password.
+
+        :param pod: The complete url of the diaspora pod to use
+        (or Connection() object).
         :type pod: str
         :param username: The username used to log in.
         :type username: str
         :param password: The password used to log in.
         :type password: str
         """
-        self._token_regex = re.compile(r'content="(.*?)"\s+name="csrf-token')
-        self.pod = pod
-        self.session = requests.Session()
-        self._post_data = {}
-        self._setlogindata(username, password)
-        self._login()
-
-    def _sessionget(self, string):
-        """This method gets data from session.
-        Performs additional checks if needed.
-
-        Example:
-            To obtain 'foo' from pod one should call `_sessionget('foo')`.
-
-        :param string: URL to get without the pod's URL and slash eg. 'stream'.
-        :type string: str
-        """
-        return self.session.get('{0}/{1}'.format(self.pod, string))
-
-    def _sessionpost(self, string, data, headers={}, params={}):
-        """This method posts data to session.
-        Performs additional checks if needed.
-
-        Example:
-            To post to 'foo' one should call `_sessionpost('foo', data={})`.
-
-        :param string: URL to post without the pod's URL and slash eg. 'status_messages'.
-        :type string: str
-        :param data: Data to post.
-        :param headers: Headers.
-        :type headers: dict
-        :param params: Optional parameters.
-        :type params: dict
-        """
-        string = '{0}/{1}'.format(self.pod, string)
-        if headers and params: r = self.session.post(string, data=data, headers=headers, params=params)
-        elif headers and not params: r = self.session.post(string, data=data, headers=headers)
-        elif not headers and params: r = self.session.post(string, data=data, params=params)
-        else: r = self.session.post(string, data=data)
-        return r
-
-    def get_token(self):
-        """This function gets a token needed for authentication in most cases
-
-        :returns: string -- token used to authenticate
-        """
-        r = self._sessionget('stream')
-        token = self._token_regex.search(r.text).group(1)
-        return token
-
-    def _setlogindata(self, username, password):
-        """This function is used to set data for login.
-        .. note::
-            It should be called before _login() function.
-        """
-        self._username, self._password = username, password
-        self._login_data = {'user[username]': self._username,
-                            'user[password]': self._password,
-                            'authenticity_token': self.get_token()}
-
-    def _login(self):
-        """This function is used to connect to the pod and log in.
-        """
-        r = self._sessionpost('users/sign_in',
-                              data=self._login_data,
-                              headers={'accept': 'application/json'})
-        if r.status_code != 201:
-            raise Exception('{0}: Login failed.'.format(r.status_code))
-
-    def _setpostdata(self, text, aspect_ids, photos):
-        """This function prepares data for posting.
+        if type(pod) == diaspy.connection.Connection:
+            self.connection = pod
+        else:
+            self.connection = diaspy.connection.Connection(pod, username, password)
+            self.connection.login()
+        self.stream = diaspy.streams.Stream(self.connection, 'stream.json')
 
-        :param text: Text to post.
-        :type text: str
-        :param aspect_ids: Aspect ids to send post to.
-        :type aspect_ids: str
-        """
-        data = {}
-        data['aspect_ids'] = aspect_ids
-        data['status_message'] = {'text': text}
-        if photos:
-            data['photos'] = photos
-        self._post_data = data
-
-    def _post(self):
-        """Sends post to an aspect.
-
-        :returns: diaspy.models.Post -- the Post which has been created
-        """
-        r = self._sessionpost('status_messages',
-                              data=json.dumps(self._post_data),
-                              headers={'content-type': 'application/json',
-                                       'accept': 'application/json',
-                                       'x-csrf-token': self.get_token()})
-        if r.status_code != 201:
-            raise Exception('{0}: Post could not be posted.'.format(
-                            r.status_code))
-
-        return diaspy.models.Post(str(r.json()['id']), self)
-
-    def post(self, text, aspect_ids='public', photos=None):
+    def post(self, text, aspect_ids='public', photos=None, photo=''):
         """This function sends a post to an aspect
 
-        :param text: Text to post.
+        :param text: text to post
         :type text: str
         :param aspect_ids: Aspect ids to send post to.
         :type aspect_ids: str
-
+        :param photo: path to picture file
+        :type photo: str
         :returns: diaspy.models.Post -- the Post which has been created
         """
-        self._setpostdata(text, aspect_ids, photos)
-        post = self._post()
-        self._post_data = {}
+        post = self.stream.post(text, aspect_ids, photos, photo)
         return post
 
-    def get_user_info(self):
-        """This function returns the current user's attributes.
+    def get_activity(self):
+        """This function returns activity stream.
 
-        :returns: dict -- json formatted user info.
+        :returns: diaspy.streams.Activity
         """
-        r = self._sessionget('bookmarklet')
-        regex = re.compile(r'window.current_user_attributes = ({.*})')
-        userdata = json.loads(regex.search(r.text).group(1))
-        return userdata
-
-    def post_picture(self, filename):
-        """This method posts a picture to D*.
-
-        :param filename: Path to picture file.
-        :type filename: str
-        """
-        aspects = self.get_user_info()['aspects']
-        params = {}
-        params['photo[pending]'] = 'true'
-        params['set_profile_image'] = ''
-        params['qqfile'] = filename
-        for i, aspect in enumerate(aspects):
-            params['photo[aspect_ids][%d]' % (i)] = aspect['id']
-
-        data = open(filename, 'rb')
-
-        headers = {'content-type': 'application/octet-stream',
-                   'x-csrf-token': self.get_token(),
-                   'x-file-name': filename}
-
-        r = self._sessionpost('photos', params=params, data=data, headers=headers)
-        return r
+        return diaspy.streams.Activity(self.connection, 'activity.json')
 
     def get_stream(self):
-        """This functions returns a list of posts found in the stream.
+        """This functions returns stream.
 
-        :returns: list -- list of Post objects.
+        :returns: diaspy.streams.Stream
         """
-        r = self._sessionget('stream.json')
+        self.stream.update()
+        return self.stream
 
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
+    def get_aspects(self):
+        """Returns aspects stream.
 
-        stream = r.json()
-        return [diaspy.models.Post(str(post['id']), self) for post in stream]
-
-    def get_notifications(self):
-        """This functions returns a list of notifications.
-
-        :returns: list -- list of json formatted notifications
+        :returns: diaspy.streams.Aspects
         """
-        r = self._sessionget('notifications.json')
-
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
-
-        notifications = r.json()
-        return notifications
+        return diaspy.streams.Aspects(self.connection)
 
     def get_mentions(self):
-        """This functions returns a list of
-        posts the current user is being mentioned in.
+        """Returns /mentions stream.
 
-        :returns: list -- list of Post objects
+        :returns: diaspy.streams.Mentions
         """
-        r = self._sessionget('mentions.json')
+        return diaspy.streams.Mentions(self.connection)
 
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
+    def get_followed_tags(self):
+        """Returns followed tags stream.
 
-        mentions = r.json()
-        return [diaspy.models.Post(str(post['id']), self) for post in mentions]
+        :returns: diaspy.streams.FollowedTags
+        """
+        return diaspy.streams.FollowedTags(self.connection)
 
     def get_tag(self, tag):
         """This functions returns a list of posts containing the tag.
         :param tag: Name of the tag
         :type tag: str
 
-        :returns: list -- list of Post objects
+        :returns: diaspy.streams.Generic -- stream containg posts with given tag
         """
-        r = self._sessionget('tags/{0}.json'.format(tag))
+        return diaspy.streams.Generic(self.connection, location='tags/{0}.json'.format(tag))
 
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
+    def get_notifications(self):
+        """This functions returns a list of notifications.
 
-        tagged_posts = r.json()
-        return [diaspy.models.Post(str(post['id']), self) for post in tagged_posts]
+        :returns: list -- list of json formatted notifications
+        """
+        return notifications.Notifications(self.connection)
 
     def get_mailbox(self):
         """This functions returns a list of messages found in the conversation.
 
         :returns: list -- list of Conversation objects.
         """
-        r = self._sessionget('conversations.json')
+        r = self.connection.get('conversations.json')
 
         if r.status_code != 200:
             raise Exception('wrong status code: {0}'.format(r.status_code))
 
         mailbox = r.json()
-        return [diaspy.conversations.Conversation(str(conversation['conversation']['id']), self)
+        return [diaspy.conversations.Conversation(self.connection, conversation['conversation']['id'])
                 for conversation in mailbox]
 
+    def add_aspect(self, aspect_name, visible=0):
+        """This function adds a new aspect.
+        """
+        diaspy.streams.Aspects(self.connection).add(aspect_name, visible)
+
+    def remove_aspect(self, aspect_id):
+        """This function removes an aspect.
+        """
+        diaspy.streams.Aspects(self.connection).remove(aspect_id)
+
     def add_user_to_aspect(self, user_id, aspect_id):
         """ this function adds a user to an aspect.
 
@@ -242,28 +128,7 @@ class Client:
         :type aspect_id: str
 
         """
-        data = {'authenticity_token': self.get_token(),
-                'aspect_id': aspect_id,
-                'person_id': user_id}
-
-        r = self._sessionpost('aspect_memberships.json', data=data)
-
-        if r.status_code != 201:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
-        return r.json()
-
-    def add_aspect(self, aspect_name, visible=0):
-        """ This function adds a new aspect.
-        """
-
-        data = {'authenticity_token': self.get_token(),
-                'aspect[name]': aspect_name,
-                'aspect[contacts_visible]': visible}
-
-        r = self._sessionpost('aspects', data=data)
-
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
+        return diaspy.models.Aspect(self.connection, aspect_id).addUser(user_id)
 
     def remove_user_from_aspect(self, user_id, aspect_id):
         """ this function removes a user from an aspect.
@@ -274,30 +139,7 @@ class Client:
         :type aspect_id: str
 
         """
-
-        data = {'authenticity_token': self.get_token(),
-                'aspect_id': aspect_id,
-                'person_id': user_id}
-
-        r = self.session.delete('{0}/aspect_memberships/42.json'.format(
-                                self.pod),
-                                data=data)
-
-        if r.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
-
-        return r.json()
-
-    def remove_aspect(self, aspect_id):
-        """ This function adds a new aspect.
-        """
-        data = {'authenticity_token': self.get_token()}
-
-        r = self.session.delete('{0}/aspects/{1}'.format(self.pod, aspect_id),
-                                data=data)
-
-        if r.status_code != 404:
-            raise Exception('wrong status code: {0}'.format(r.status_code))
+        return diaspy.models.Aspect(self.connection, aspect_id).removeUser(user_id)
 
     def new_conversation(self, contacts, subject, text):
         """Start a new conversation.
@@ -313,11 +155,11 @@ class Client:
                 'conversation[subject]': subject,
                 'conversation[text]': text,
                 'utf8': '✓',
-                'authenticity_token': self.get_token()}
+                'authenticity_token': self.connection.get_token()}
 
-        r = self._sessionpost('conversations/',
-                              data=data,
-                              headers={'accept': 'application/json'})
+        r = self.connection.post('conversations/',
+                                 data=data,
+                                 headers={'accept': 'application/json'})
         if r.status_code != 200:
             raise Exception('{0}: Conversation could not be started.'
                             .format(r.status_code))