class TwitchAPI(object):
    """Twitch API client."""

    def __init__(self, client_id, oauth_token=None):
        """Initialize the API."""
        super(TwitchAPI, self).__init__()
        self._client_id = client_id
        self._oauth_token = oauth_token
        self._initial_backoff, self._max_retries = backoff_config()

    def _get_request_headers(self):
        """Prepare the headers for the requests."""
        headers = {
            "Accept": "application/vnd.twitchtv.v5+json",
            "Client-ID": self._client_id,
        }

        if self._oauth_token:
            headers["Authorization"] = "OAuth {}".format(self._oauth_token)

        return headers

    def _request_get(self, path, params=None, json=True, url=BASE_URL):
        """Perform a HTTP GET request."""
        url = urljoin(url, path)
        headers = self._get_request_headers()

        response = requests.get(url, params=params, headers=headers)
        if response.status_code >= 500:

            backoff = self._initial_backoff
            for _ in range(self._max_retries):
                time.sleep(backoff)
                backoff_response = requests.get(
                    url, params=params, headers=headers, timeout=DEFAULT_TIMEOUT
                )
                if backoff_response.status_code < 500:
                    response = backoff_response
                    break
                backoff *= 2

        response.raise_for_status()
        if json:
            return response.json()
        else:
            return response

    def _request_post(self, path, data=None, params=None, url=BASE_URL):
        """Perform a HTTP POST request.."""
        url = urljoin(url, path)

        headers = self._get_request_headers()

        response = requests.post(
            url, json=data, params=params, headers=headers, timeout=DEFAULT_TIMEOUT
        )
        response.raise_for_status()
        if response.status_code == 200:
            return response.json()

    def _request_put(self, path, data=None, params=None, url=BASE_URL):
        """Perform a HTTP PUT request."""
        url = urljoin(url, path)

        headers = self._get_request_headers()
        response = requests.put(
            url, json=data, params=params, headers=headers, timeout=DEFAULT_TIMEOUT
        )
        response.raise_for_status()
        if response.status_code == 200:
            return response.json()

    def _request_delete(self, path, params=None, url=BASE_URL):
        """Perform a HTTP DELETE request."""
        url = urljoin(url, path)

        headers = self._get_request_headers()

        response = requests.delete(
            url, params=params, headers=headers, timeout=DEFAULT_TIMEOUT
        )
        response.raise_for_status()
        if response.status_code == 200:
            return response.json() DIRECTION_DESC = "desc" DIRECTIONS = [DIRECTION_ASC, DIRECTION_DESC] MAX_FOLLOWS_LIMIT = 100 USERS_SORT_BY = [
    USERS_SORT_BY_CREATED_AT,
    USERS_SORT_BY_LAST_BROADCAST,
    USERS_SORT_BY_LOGIN,
] USERS_SORT_BY_CREATED_AT = "created_at" def oauth_required(func):
    def wrapper(*args, **kwargs):
        if not args[0]._oauth_token:
            raise TwitchAuthException("OAuth token required")
        return func(*args, **kwargs)

    return wrapper class TwitchAttributeException(TwitchException):
    pass class Follow(TwitchObject):
    pass class Subscription(TwitchObject):
    pass class User(TwitchObject):
    pass class UserBlock(TwitchObject):
    pass
from twitch.api.base import TwitchAPI
from twitch.constants import (
    DIRECTION_DESC,
    DIRECTIONS,
    MAX_FOLLOWS_LIMIT,
    USERS_SORT_BY,
    USERS_SORT_BY_CREATED_AT,
)
from twitch.decorators import oauth_required
from twitch.exceptions import TwitchAttributeException
from twitch.resources import Follow, Subscription, User, UserBlock


class Users(TwitchAPI):
    @oauth_required
    def get(self):
        response = self._request_get("user")
        return User.construct_from(response)

    def get_by_id(self, user_id):
        response = self._request_get("users/{}".format(user_id))
        return User.construct_from(response)

    @oauth_required
    def get_emotes(self, user_id):
        response = self._request_get("users/{}/emotes".format(user_id))
        return response["emoticon_sets"]

    @oauth_required
    def check_subscribed_to_channel(self, user_id, channel_id):
        response = self._request_get(
            "users/{}/subscriptions/{}".format(user_id, channel_id)
        )
        return Subscription.construct_from(response)

    def get_all_follows(
        self, user_id, direction=DIRECTION_DESC, sort_by=USERS_SORT_BY_CREATED_AT
    ):
        if direction not in DIRECTIONS:
            raise TwitchAttributeException(
                "Direction is not valid. Valid values are {}".format(DIRECTIONS)
            )
        if sort_by not in USERS_SORT_BY:
            raise TwitchAttributeException(
                "Sort by is not valid. Valid values are {}".format(USERS_SORT_BY)
            )
        offset = 0
        params = {"limit": MAX_FOLLOWS_LIMIT, "direction": direction}
        follows = []
        while offset is not None:
            params.update({"offset": offset})
            response = self._request_get(
                "users/{}/follows/channels".format(user_id), params=params
            )
            offset = response.get("_offset")
            follows.extend(response["follows"])
        return [Follow.construct_from(x) for x in follows]

    def get_follows(
        self,
        user_id,
        limit=25,
        offset=0,
        direction=DIRECTION_DESC,
        sort_by=USERS_SORT_BY_CREATED_AT,
    ):
        if limit > MAX_FOLLOWS_LIMIT:
            raise TwitchAttributeException(
                "Maximum number of objects returned in one request is 100"
            )
        if direction not in DIRECTIONS:
            raise TwitchAttributeException(

                "Direction is not valid. Valid values are {}".format(DIRECTIONS)