TWITTER_PAGE_DOES_NOT_EXISTS_ERROR = 34 TWITTER_TWEET_NOT_FOUND_ERROR = 144 TWITTER_USER_NOT_FOUND_ERROR = 50 TWITTER_DELETE_OTHER_USER_TWEET = 183 TWITTER_ACCOUNT_SUSPENDED_ERROR = 64 TWITTER_USER_IS_NOT_LIST_MEMBER_SUBSCRIBER = 109 TWITTER_AUTOMATED_REQUEST_ERROR = 226 TWITTER_OVER_CAPACITY_ERROR = 130 TWITTER_DAILY_STATUS_UPDATE_LIMIT_ERROR = 185 TWITTER_CHARACTER_LIMIT_ERROR_1 = 186 TWITTER_CHARACTER_LIMIT_ERROR_2 = 354 TWITTER_STATUS_DUPLICATE_ERROR = 187 class APIQuotaError(APIError):
    """
    Exception indicate some API quota breached.
    """
    pass class AutomatedRequestError(ResponseBotError):
    """
    Error to indicate a request is deemed automated by Twitter.
    """
    pass class OverCapacityError(ResponseBotError):
    """
    Error to indicate Twitter is currently over capacity.
    """
    pass class DailyStatusUpdateError(ResponseBotError):
    """
    Error to indicate your account reached the daily status update limit.
    """
    pass class CharacterLimitError(ResponseBotError):
    """
    Error to indicate your tweet reached the character limit.
    """
    pass class StatusDuplicateError(ResponseBotError):
    """
    Error to indicate your status is a duplicate.
    """
    pass class Tweet(object):
    """
    Represents a tweet. E.g. you can get a tweet's text via it's :code:`text` property (:code:`tweet.text`).
    All properties except :code:`retweeted_status`, :code:`quoted_status`, :code:`quoted_status_id_str`, :code:`in_reply_to_status_id`
    and :code:`in_reply_sto_status_id_str` have the same name as Twitter defined them `here <https://dev.twitter.com/overview/api/tweets>`_.
    :code:`retweeted_status` is renamed to :code:`retweeted_tweet`, similar for other properties above.
    """
    def __init__(self, data):
        """
        :param data: Parsed JSON data
        :type data: dictionary
        """
        self.raw_data = data

        # TODO: handle place. source, quoted_status_id_str
        for key, value in data.items():
            if key == 'user':
                setattr(self, key, User(value))
            elif key == 'created_at':
                setattr(self, key, parse(value))
            elif key == 'retweeted_status':
                setattr(self, 'retweeted_tweet', Tweet(value))
            elif key == 'quoted_status':
                setattr(self, 'quoted_tweet', Tweet(value))
            elif key == 'quoted_status_id_str':
                setattr(self, 'quoted_tweet_id_str', value)
            elif key == 'in_reply_to_status_id':
                setattr(self, 'in_reply_to_tweet_id', value)
            elif key == 'in_reply_to_status_id_str':
                setattr(self, 'in_reply_to_tweet_id_str', value)
            else:
                setattr(self, key, value) class User(object):
    """
    Represents a user. E.g. you can get a user's screen name via it's :code:`screen_name` property
    (:code:`user.screen_name`). All properties except :code:`status` have the same name as Twitter defined them `here
    <https://dev.twitter.com/overview/api/users>`_. :code:`status` is renamed to :code:`tweet`.
    """
    def __init__(self, data):
        """
        :param data: Parsed JSON data
        :type data: dictionary
        """
        self.raw_data = data

        for key, value in data.items():
            if key == 'created_at':
                setattr(self, key, parse(value))
            elif key == 'status':
                setattr(self, 'tweet', Tweet(value))
            elif key == 'following':
                setattr(self, key, value is True)
            else:
                setattr(self, key, value) class List(object):
    """
    Represent a user list.
    """
    def __init__(self, data):
        """
        :param data: Parsed JSON data
        :type data: dictionary
        """
        self.raw_data = data

        for key, value in data.items():
            if key == 'user':
                setattr(self, key, User(value))
            elif key == 'created_at':
                setattr(self, key, parse(value))
            else:
                setattr(self, key, value) def tweepy_list_to_json(tweepy_list):
    json = dict(tweepy_list.__dict__)

    json.pop('_api')
    # Set default timezone to +0000 here since datetime does not contain tz info
    json['created_at'] = datetime.strftime(json['created_at'], '%a %b %d %H:%M:%S +0000 %Y')
    json['user'] = json['user']._json

    return json
from decorator import decorate
from tweepy.error import TweepError, RateLimitError
from responsebot.common.constants import TWITTER_PAGE_DOES_NOT_EXISTS_ERROR, TWITTER_TWEET_NOT_FOUND_ERROR, \
    TWITTER_USER_NOT_FOUND_ERROR, TWITTER_DELETE_OTHER_USER_TWEET, TWITTER_ACCOUNT_SUSPENDED_ERROR,\
    TWITTER_USER_IS_NOT_LIST_MEMBER_SUBSCRIBER, TWITTER_AUTOMATED_REQUEST_ERROR, TWITTER_OVER_CAPACITY_ERROR,\
    TWITTER_DAILY_STATUS_UPDATE_LIMIT_ERROR, TWITTER_CHARACTER_LIMIT_ERROR_1, TWITTER_CHARACTER_LIMIT_ERROR_2, \
    TWITTER_STATUS_DUPLICATE_ERROR
from responsebot.common.exceptions import APIQuotaError, AutomatedRequestError, OverCapacityError,\
    DailyStatusUpdateError, CharacterLimitError, StatusDuplicateError
from responsebot.models import Tweet, User, List
from responsebot.utils.tweepy import tweepy_list_to_json
        """
        try:
            return User(self._client.create_friendship(user_id=user_id, follow=notify)._json)
        except TweepError as e:
            if e.api_code in [TWITTER_ACCOUNT_SUSPENDED_ERROR]:
                return self.get_user(user_id)
            raise

    def unfollow(self, user_id):
        """
        Follow a user.

        :param user_id: ID of the user in question
        :return: The user that were unfollowed
        """
        return User(self._client.destroy_friendship(user_id=user_id)._json)

    ###################################################################################
    # Lists
    ###################################################################################
    @api_error_handle
    def create_list(self, name, mode='public', description=None):
        """
        Create a list

        :param name: Name of the new list
        :param mode: :code:`'public'` (default) or :code:`'private'`
        :param description: Description of the new list
        :return: The new list object
        :rtype: :class:`~responsebot.models.List`
        """
        return List(tweepy_list_to_json(self._client.create_list(name=name, mode=mode, description=description)))

    @api_error_handle
    def destroy_list(self, list_id):
        """
        Destroy a list

        :param list_id: list ID number
        :return: The destroyed list object
        :rtype: :class:`~responsebot.models.List`
        """
        return List(tweepy_list_to_json(self._client.destroy_list(list_id=list_id)))

    @api_error_handle
    def update_list(self, list_id, name=None, mode=None, description=None):
        """
        Update a list

        :param list_id: list ID number
        :param name: New name for the list
        :param mode: :code:`'public'` (default) or :code:`'private'`
        :param description: New description of the list
        :return: The updated list object
        :rtype: :class:`~responsebot.models.List`
        """
        return List(tweepy_list_to_json(
            self._client.update_list(list_id=list_id, name=name, mode=mode, description=description))
        )

    @api_error_handle
    def lists(self):
        """
        List user's lists

        :return: list of :class:`~responsebot.models.List` objects
        """
        return [List(tweepy_list_to_json(list)) for list in self._client.lists_all()]

    @api_error_handle
    def lists_memberships(self):
        """
        List lists which user was added

        :return: list of :class:`~responsebot.models.List` objects
        """
        return [List(tweepy_list_to_json(list)) for list in self._client.lists_memberships()]

    @api_error_handle
    def lists_subscriptions(self):
        """
        List lists which user followed

        :return: list of :class:`~responsebot.models.List` objects
        """
        return [List(tweepy_list_to_json(list)) for list in self._client.lists_subscriptions()]

    @api_error_handle
    def list_timeline(self, list_id, since_id=None, max_id=None, count=20):
        """
        List the tweets of specified list.

        :param list_id: list ID number
        :param since_id: results will have ID greater than specified ID (more recent than)
        :param max_id: results will have ID less than specified ID (older than)
        :param count: number of results per page
        :return: list of :class:`~responsebot.models.Tweet` objects
        """
        statuses = self._client.list_timeline(list_id=list_id, since_id=since_id, max_id=max_id, count=count)
        return [Tweet(tweet._json) for tweet in statuses]

    @api_error_handle
    def get_list(self, list_id):
        """
        Get info of specified list

        :param list_id: list ID number
        :return: :class:`~responsebot.models.List` object
        """
        return List(tweepy_list_to_json(self._client.get_list(list_id=list_id)))

    @api_error_handle
    def add_list_member(self, list_id, user_id):
        """
        Add a user to list

        :param list_id: list ID number
        :param user_id: user ID number
        :return: :class:`~responsebot.models.List` object
        """

        return List(tweepy_list_to_json(self._client.add_list_member(list_id=list_id, user_id=user_id)))