def getRecentActivity(self):
        """
        Get recent activity.
        :rtype: object
        :return: Recent activity data
        """
        activity = self.request('news/inbox/?')[1]

        if activity['status'] != 'ok':
            raise InstagramException(activity['message'] + "\n")

        return activity
    def getv2Inbox(self):
        """
        He didn't know this yet.
        :rtype: object
        :return: v2 inbox data
        """
        inbox = self.request('direct_v2/inbox/?')[1]

        if inbox['status'] != 'ok':
            raise InstagramException(inbox['message'] + "\n")

        return inbox
    def getTimeline(self):
        """
        Get timeline data.
        :rtype: object
        :return: timeline data
        """
        timeline = self.request("feed/timeline/?rank_token=" + self.rank_token + "&ranked_content=true&")[1]

        if timeline['status'] != 'ok':
            raise InstagramException(timeline['message'] + "\n")

        return timeline
    def getFollowingRecentActivity(self):
        """
        Get recent activity from accounts followed.

        :rtype: object
        :return: Recent activity data of follows
        """
        activity = self.request('news/?')[1]
        if activity['status'] != 'ok':
            raise InstagramException(activity['message'] + "\n")

        return activity
    def getPopularFeed(self):
        """
        Get popular feed.
        :rtype: object
        :return: popular feed data
        """
        popularFeed = self.request("feed/popular/?people_teaser_supported=1&rank_token=" \
                                   + self.rank_token + "&ranked_content=true&")[1]

        if popularFeed['status'] != 'ok':
            raise InstagramException(popularFeed['message'] + "\n")

        return popularFeed
    def getMediaLikers(self, mediaId):
        """
        Get media likers.
        :type mediaId: str
        :param mediaId:
        :rtype: object
        :return:
        """
        likers = self.request("media/" + mediaId + "/likers/?")[1]
        if likers['status'] != 'ok':
            raise InstagramException(likers['message'] + "\n")

        return likers
    def searchTags(self, query):
        """
        Search tags.
        :type query: str
        :param query:
        :rtype: object
        :return: query data
        """
        query = self.request("tags/search/?is_typeahead=true&q=" + query + "&rank_token=" + self.rank_token)[1]

        if query['status'] != 'ok':
            raise InstagramException(query['message'] + "\n")

        return query
    def getUserTags(self, usernameId):
        """
        Get user tags.
        :type usernameId: str
        :param usernameId:
        :rtype: object
        :return: user tags data
        """
        tags = self.request("usertags/" + usernameId + "/feed/?rank_token=" + self.rank_token
                            + "&ranked_content=true&")[1]
        if tags['status'] != 'ok':
            raise InstagramException(tags['message'] + "\n")

        return tags
    def tagFeed(self, tag):
        """
        Get tagged media.
        :type tag: str
        :param tag:
        :rtype: object
        :return:
        """
        userFeed = self.request("feed/tag/" + tag + "/?rank_token=" + self.rank_token + "&ranked_content=true&")[1]

        if userFeed['status'] != 'ok':
            raise InstagramException(userFeed['message'] + "\n")

        return userFeed
    def getGeoMedia(self, usernameId):
        """
        Get user locations media.
        :type usernameId: str
        :param usernameId: Username id
        :rtype: object
        :return: Geo Media data
        """
        locations = self.request("maps/user/" + usernameId + "/")[1]

        if locations['status'] != 'ok':
            raise InstagramException(locations['message'] + "\n")

        return locations
    def fbUserSearch(self, query):
        """
        facebook user search.
        :type query: str
        :param query:
        :rtype: object
        :return: query data
        """
        query = self.request("fbsearch/topsearch/?context=blended&query=" + query + "&rank_token=" + self.rank_token)[1]

        if query['status'] != 'ok':
            raise InstagramException(query['message'] + "\n")

        return query
    def searchLocation(self, query):
        """
        Get locations.
        :type query: str
        :param query: search query
        :rtype: object
        :return: Location location data
        """
        endpoint = "fbsearch/places/?rank_token=" + self.rank_token + "&query=" + query

        locationFeed = self.request(endpoint)[1]

        if locationFeed['status'] != 'ok':
            raise InstagramException(locationFeed['message'] + "\n")

        return locationFeed
    def searchUsers(self, query):
        """
        Search users.
        :type query: str
        :param query:
        :rtype: object
        :return: query data
        """
        query = self.request(
            'users/search/?ig_sig_key_version=' + Constants.SIG_KEY_VERSION \
            + "&is_typeahead=true&query=" + query + "&rank_token=" + self.rank_token)[1]

        if query['status'] != 'ok':
            raise InstagramException(query['message'] + "\n")

        return query
    def searchUsername(self, usernameName):
        """
        Search exact username

        :type usernameName: str
        :param usernameName: username as STRING not an id

        :rtype: object
        :return: query data
        """
        query = self.request("users/" + usernameName + "/usernameinfo/")[1]

        if query['status'] != 'ok':
            raise InstagramException(query['message'] + "\n")

        return query
    def request(self, endpoint, post=None, login=False):
        buffer = BytesIO()
        if (not self.isLoggedIn) and not login:
            raise InstagramException("Not logged in\n")

        headers = [
            'Connection: close',
            'Accept: */*',
            'Content-type: application/x-www-form-urlencoded; charset=UTF-8',
            'Cookie2: $Version=1',
            'Accept-Language: en-US'
        ]

        ch = pycurl.Curl()

        ch.setopt(pycurl.URL, Constants.API_URL + endpoint)
        ch.setopt(pycurl.USERAGENT, Constants.USER_AGENT)
        ch.setopt(pycurl.WRITEFUNCTION, buffer.write)
        ch.setopt(pycurl.FOLLOWLOCATION, True)
        ch.setopt(pycurl.HEADER, True)
        ch.setopt(pycurl.HTTPHEADER, headers)
        ch.setopt(pycurl.VERBOSE, False)
        ch.setopt(pycurl.SSL_VERIFYPEER, False)
        ch.setopt(pycurl.SSL_VERIFYHOST, False)
        ch.setopt(pycurl.COOKIEFILE, self.IGDataPath + self.username + '-cookies.dat')
        ch.setopt(pycurl.COOKIEJAR, self.IGDataPath + self.username + '-cookies.dat')

        if post:
            ch.setopt(pycurl.POST, True)
            ch.setopt(pycurl.POSTFIELDS, post)

        ch.perform()
        resp = buffer.getvalue()
        header_len = ch.getinfo(pycurl.HEADER_SIZE)
        header = resp[0: header_len]
        body = resp[header_len:]
        ch.close()

        if self.debug:
            import urllib
            print "REQUEST: " + endpoint
            if post is not None:
                if not isinstance(post, list):
                    print 'DATA: ' + urllib.unquote_plus(post)
            print "RESPONSE: " + body + "\n"

        return [header, json.loads(body)]
    def login(self, force=False):
        """
        Login to Instagram.

        :type force: bool
        :param force: Force login to Instagram, this will create a new session
        :return: Login data
        :rtype List:
        """
        if (not self.isLoggedIn) or force:
            fetch = self.request('si/fetch_headers/?challenge_type=signup&guid=' + self.generateUUID(False), None, True)
            match = re.search(r'^Set-Cookie: csrftoken=([^;]+)', fetch[0], re.MULTILINE)
            if match:
                self.token = match.group(1)

            data = OrderedDict([
                ('username', self.username),
                ('guid', self.uuid),
                ('device_id', self.device_id),
                ('password', self.password),
                ('login_attempt_count', 0)
            ])

            login = self.request('accounts/login/', self.generateSignature(json.dumps(data)), True)
            if login[1]['status'] == 'fail': raise InstagramException(login[1]['message'])
            self.isLoggedIn = True
            self.username_id = str(login[1]['logged_in_user']['pk'])
            file_put_contents(self.IGDataPath + self.username + '-userId.dat', self.username_id)
            self.rank_token = self.username_id + '_' + self.uuid
            match = re.search(r'^Set-Cookie: csrftoken=([^;]+)', login[0], re.MULTILINE)
            if match: self.token = match.group(1)
            file_put_contents(self.IGDataPath + self.username + '-token.dat', self.token)

            self.syncFeatures()
            self.autoCompleteUserList()
            self.timelineFeed()
            self.getv2Inbox()
            self.getRecentActivity()

            return login[1]

        check = self.timelineFeed()

        if hasattr(check, 'message') and check['message'] == 'login_required':
            self.login(True)
        self.getv2Inbox()
        self.getRecentActivity()
    def getHashtagFeed(self, hashtagString, maxid=''):
        """
        Get hashtag feed.
        :type hashtagString: str
        :param hashtagString: Hashtag string, not including the #
        :rtype: object
        :return: Hashtag feed data
        """
        if maxid == '':
            endpoint = "feed/tag/" + hashtagString + "/?rank_token=" + self.rank_token + "&ranked_content=true&"
        else:
            endpoint = "feed/tag/" + hashtagString + "/?max_id=" \
                       + maxid + "&rank_token=" + self.rank_token + "&ranked_content=true&"
        hashtagFeed = self.request(endpoint)[1]
        if hashtagFeed['status'] != 'ok':
            raise InstagramException(hashtagFeed['message'] + "\n")

        return hashtagFeed
    def getLocationFeed(self, locationId, maxid=''):
        """
        Get location feed.
        :type locationId: str
        :param locationId: location id
        :rtype: object
        :return: Location feed data
        """
        if maxid is '':
            endpoint = "feed/location/" + locationId + "/?rank_token=" + self.rank_token + "&ranked_content=true&"
        else:
            endpoint = "feed/location/" + locationId + "/?max_id=" \
                       + maxid + "&rank_token=" + self.rank_token + "&ranked_content=true&"

        locationFeed = self.request(endpoint)[1]

        if locationFeed['status'] != 'ok':
            raise InstagramException(locationFeed['message'] + "\n")

        return locationFeed
    def getUserFeed(self, usernameId, maxid=None,count=None):
        """
        Get user feed.
        :type usernameId: str
        :param usernameId: Username id
        :type maxid: str
        :param maxid: Max Id
        :type count: str
        :param count: Count
        :rtype: object
        :return: User feed data
        :raises: InstagramException
        """
        userFeed = self.request("feed/user/" + usernameId + "/?rank_token=" + self.rank_token + "&"\
                                + (("&max_id="+str(maxid)) if maxid is not None else '')\
                                + (("&count="+str(maxid)) if count is not None else '')\
                                + "ranked_content=true&"
                                )[1]

        if userFeed['status'] != 'ok':
            raise InstagramException(userFeed['message'] + "\n")

        return userFeed
    def uploadVideo(self, video, caption=None):

        videoData = file_get_contents(video)

        endpoint = Constants.API_URL + 'upload/video/'
        boundary = self.uuid
        upload_id = round(float('%.2f' % time.time()) * 1000)
        bodies = [
            OrderedDict([
                ('type', 'form-data'),
                ('name', 'upload_id'),
                ('data', upload_id)
            ]),
            OrderedDict([
                ('type', 'form-data'),
                ('name', '_csrftoken'),
                ('data', self.token)
            ]),
            OrderedDict([
                ('type', 'form-data'),
                ('name', 'media_type'),
                ('data', 2)
            ]),
            OrderedDict([
                ('type', 'form-data'),
                ('name', '_uuid'),
                ('data', self.uuid)
            ]),
        ]

        data = self.buildBody(bodies, boundary)
        headers = [
            'Connection: close',
            'Accept: */*',
            'Host: i.instagram.com',
            'Content-type: multipart/form-data; boundary=' + boundary,
            'Accept-Language: en-en',
        ]

        buffer = BytesIO()
        ch = pycurl.Curl()
        ch.setopt(pycurl.URL, endpoint)
        ch.setopt(pycurl.USERAGENT, Constants.USER_AGENT)
        ch.setopt(pycurl.WRITEFUNCTION, buffer.write)
        ch.setopt(pycurl.FOLLOWLOCATION, True)
        ch.setopt(pycurl.HEADER, True)
        ch.setopt(pycurl.VERBOSE, self.debug)
        ch.setopt(pycurl.SSL_VERIFYPEER, False)
        ch.setopt(pycurl.SSL_VERIFYHOST, False)
        ch.setopt(pycurl.HTTPHEADER, headers)
        ch.setopt(pycurl.COOKIEFILE, self.IGDataPath + self.username + "-cookies.dat")
        ch.setopt(pycurl.COOKIEJAR, self.IGDataPath + self.username + "-cookies.dat")
        ch.setopt(pycurl.POST, True)
        ch.setopt(pycurl.POSTFIELDS, data)

        ch.perform()
        resp = buffer.getvalue()
        header_len = ch.getinfo(pycurl.HEADER_SIZE)

        header = resp[0: header_len]
        body = json.loads(resp[header_len:])

        uploadUrl = body['video_upload_urls'][3]['url']
        job = body['video_upload_urls'][3]['job']

        request_size = int(math.floor(len(videoData) / 4.0))

        lastRequestExtra = (len(videoData) - (request_size * 4))

        for a in range(4):
            start = (a * request_size)
            end = (a + 1) * request_size + (lastRequestExtra if a == 3 else 0)

            headers = [
                'Connection: keep-alive',
                'Accept: */*',
                'Host: upload.instagram.com',
                'Cookie2: $Version=1',
                'Accept-Encoding: gzip, deflate',
                'Content-Type: application/octet-stream',
                'Session-ID: ' + str(upload_id),
                'Accept-Language: en-en',
                'Content-Disposition: attachment; filename="video.mov"',
                'Content-Length: ' + str(end - start),
                'Content-Range: ' + 'bytes ' + str(start) + '-' + str(end - 1) + '/' + str(len(videoData)),
                'job: ' + job,
            ]

            buffer = BytesIO()
            ch = pycurl.Curl()
            ch.setopt(pycurl.URL, uploadUrl)
            ch.setopt(pycurl.USERAGENT, Constants.USER_AGENT)
            ch.setopt(pycurl.CUSTOMREQUEST, 'POST')
            ch.setopt(pycurl.WRITEFUNCTION, buffer.write)
            ch.setopt(pycurl.FOLLOWLOCATION, True)
            ch.setopt(pycurl.HEADER, True)
            ch.setopt(pycurl.VERBOSE, False)
            ch.setopt(pycurl.HTTPHEADER, headers)
            ch.setopt(pycurl.COOKIEFILE, self.IGDataPath + self.username + "-cookies.dat")
            ch.setopt(pycurl.COOKIEJAR, self.IGDataPath + self.username + "-cookies.dat")
            ch.setopt(pycurl.POST, True)
            ch.setopt(pycurl.POSTFIELDS, videoData[start:end])

            ch.perform()
            result = buffer.getvalue()
            header_len = ch.getinfo(pycurl.HEADER_SIZE)
            body = result[header_len:]
            # array.append([body]) todo fix

        ch.perform()
        resp = buffer.getvalue()
        header_len = ch.getinfo(pycurl.HEADER_SIZE)
        ch.close()

        header = resp[0: header_len]
        upload = json.loads(resp[header_len:])

        if upload['status'] == 'fail':
            raise InstagramException(upload['message'])

        if self.debug:
            print 'RESPONSE: ' + resp[header_len:] + "\n"

        configure = self.configureVideo(upload['upload_id'], video, caption)
        self.expose()

        return configure[1]
    def uploadPhoto(self, photo, caption=None, upload_id=None):
        """
        Upload photo to Instagram.

        :type photo: str
        :param photo: Path to your photo
        :type caption: str
        :param caption: Caption to be included in your photo.
        :rtype: object
        :return: Upload data
        """
        endpoint = Constants.API_URL + 'upload/photo/'
        boundary = self.uuid

        if upload_id is not None:
            fileToUpload = createVideoIcon(photo)
        else:
            upload_id = locale.format("%.*f", (0, round(float('%.2f' % time.time()) * 1000)), grouping=False)
            fileToUpload = file_get_contents(photo)

        bodies = [
            OrderedDict([
                ('type', 'form-data'),
                ('name', 'upload_id'),
                ('data', upload_id)
            ]),
            OrderedDict([
                ('type', 'form-data'),
                ('name', '_uuid'),
                ('data', self.uuid)
            ]),
            OrderedDict([
                ('type', 'form-data'),
                ('name', '_csrftoken'),
                ('data', self.token)
            ]),

            OrderedDict([
                ('type', 'form-data'),
                ('name', 'image_compression'),
                ('data', '{"lib_name":"jt","lib_version":"1.3.0","quality":"70"}')
            ]),

            OrderedDict([
                ('type', 'form-data'),
                ('name', 'photo'),
                ('data', fileToUpload),
                ('filename', 'pending_media_' + locale.format("%.*f", (0, round(float('%.2f' % time.time()) * 1000)),
                                                              grouping=False) + '.jpg'),
                ('headers', [
                    'Content-Transfer-Encoding: binary',
                    'Content-type: application/octet-stream',
                ])
            ]),
        ]

        data = self.buildBody(bodies, boundary)
        headers = [
            'Connection: close',
            'Accept: */*',
            'Content-type: multipart/form-data; boundary=' + boundary,
            'Content-Length: ' + str(len(data)),
            'Cookie2: $Version=1',
            'Accept-Language: en-US',
            'Accept-Encoding: gzip',
        ]

        buffer = BytesIO()
        ch = pycurl.Curl()

        ch.setopt(pycurl.URL, endpoint)
        ch.setopt(pycurl.USERAGENT, Constants.USER_AGENT)
        ch.setopt(pycurl.WRITEFUNCTION, buffer.write)
        ch.setopt(pycurl.FOLLOWLOCATION, True)
        ch.setopt(pycurl.HEADER, True)
        ch.setopt(pycurl.VERBOSE, self.debug)
        ch.setopt(pycurl.SSL_VERIFYPEER, False)
        ch.setopt(pycurl.SSL_VERIFYHOST, False)
        ch.setopt(pycurl.HTTPHEADER, headers)
        ch.setopt(pycurl.COOKIEFILE, self.IGDataPath + self.username + "-cookies.dat")
        ch.setopt(pycurl.COOKIEJAR, self.IGDataPath + self.username + "-cookies.dat")
        ch.setopt(pycurl.POST, True)
        ch.setopt(pycurl.POSTFIELDS, data)

        ch.perform()
        resp = buffer.getvalue()
        header_len = ch.getinfo(pycurl.HEADER_SIZE)
        ch.close()

        header = resp[0: header_len]
        upload = json.loads(resp[header_len:])

        if upload['status'] == 'fail':
            raise InstagramException(upload['message'])

        if self.debug:
            print 'RESPONSE: ' + resp[header_len:] + "\n"

        configure = self.configure(upload['upload_id'], photo, caption)
        self.expose()

        return configure