Skip to content

Commit

Permalink
Docstrings update.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwatkot committed Jun 20, 2024
1 parent c1388d4 commit 9f21531
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 8 deletions.
1 change: 1 addition & 0 deletions py3xui/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# pylint: disable=missing-module-docstring
from py3xui.api.api_client import ClientApi
from py3xui.api.api_database import DatabaseApi
from py3xui.api.api_inbound import InboundApi
72 changes: 71 additions & 1 deletion py3xui/api/api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
"""This module provides classes to interact with the XUI API."""

from __future__ import annotations

from py3xui.api import ClientApi, DatabaseApi, InboundApi
from py3xui.utils import Logger, env

logger = Logger(__name__)


class Api:
"""This class provides a high-level interface to interact with the XUI API.
Access to the client, inbound, and database APIs is provided through this class.
Args:
host (str): The XUI host URL.
username (str): The XUI username.
password (str): The XUI password.
skip_login (bool): Skip the login process. Default is False.
Attributes and Properties:
client (ClientApi): The client API.
inbound (InboundApi): The inbound API.
database (DatabaseApi): The database API.
Public Methods:
login: Logs into the XUI API.
from_env: Creates an instance of the API from environment variables.
Examples:
```python
import py3xui
# It's recommended to use environment variables for the credentials.
os.environ["XUI_HOST"] = "https://xui.example.com"
os.environ["XUI_USERNAME"] = "username"
os.environ["XUI_PASSWORD"] = "password"
api = py3xui.Api.from_env()
# Alternatively, you can provide the credentials directly.
api = py3xui.Api("https://xui.example.com", "username", "password")
# Some examples of using the API.
inbounds: list[py3xui.Inbound] = api.inbound.get_list()
client: py3xui.Client = api.client.get_by_email("email")
```
"""

def __init__(self, host: str, username: str, password: str, skip_login: bool = False):
self.client = ClientApi(host, username, password)
self.inbound = InboundApi(host, username, password)
Expand All @@ -15,13 +55,43 @@ def __init__(self, host: str, username: str, password: str, skip_login: bool = F
self.login()

@classmethod
def from_env(cls, skip_login: bool = False):
def from_env(cls, skip_login: bool = False) -> Api:
"""Creates an instance of the API from environment variables.
Following environment variables should be set:
- XUI_HOST: The XUI host URL.
- XUI_USERNAME: The XUI username.
- XUI_PASSWORD: The XUI password.
Args:
skip_login (bool): Skip the login process. Default is False.
Returns:
Api: The API instance.
Examples:
```python
import py3xui
api = py3xui.Api.from_env()
```
"""
host = env.xui_host()
username = env.xui_username()
password = env.xui_password()
return cls(host, username, password, skip_login)

def login(self) -> None:
"""Logs into the XUI API and sets the session cookie for the client, inbound, and
database APIs.
Examples:
```python
import py3xui
api = py3xui.Api.from_env(skip_login=True)
api.login()
```
"""
self.client.login()
self.inbound.session = self.client.session
self.database.session = self.client.session
Expand Down
108 changes: 108 additions & 0 deletions py3xui/api/api_base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""This module contains the base class for the XUI API."""

from time import sleep
from typing import Any, Callable

Expand All @@ -20,6 +22,32 @@ class ApiFields:


class BaseApi:
"""Base class for the XUI API. Contains common methods for making requests.
Arguments:
host (str): The host of the XUI API.
username (str): The username for the XUI API.
password (str): The password for the XUI API.
Attributes and Properties:
host (str): The host of the XUI API.
username (str): The username for the XUI API.
password (str): The password for the XUI API.
max_retries (int): The maximum number of retries for a request.
session (str): The session cookie for the XUI API.
Public Methods:
login: Logs into the XUI API.
Private Methods:
_check_response: Checks the response from the XUI API.
_url: Returns the URL for the XUI API.
_request_with_retry: Makes a request to the XUI API with retries.
_post: Makes a POST request to the XUI API.
_get: Makes a GET request to the XUI API.
"""

def __init__(self, host: str, username: str, password: str):
self._host = host.rstrip("/")
self._username = username
Expand All @@ -29,33 +57,65 @@ def __init__(self, host: str, username: str, password: str):

@property
def host(self) -> str:
"""The host of the XUI API.
Returns:
str: The host of the XUI API."""
return self._host

@property
def username(self) -> str:
"""The username for the XUI API.
Returns:
str: The username for the XUI API."""
return self._username

@property
def password(self) -> str:
"""The password for the XUI API.
Returns:
str: The password for the XUI API."""
return self._password

@property
def max_retries(self) -> int:
"""The maximum number of retries for a request.
Returns:
int: The maximum number of retries for a request."""
return self._max_retries

@max_retries.setter
def max_retries(self, value: int) -> None:
"""Sets the maximum number of retries for a request.
Arguments:
value (int): The maximum number of retries for a request."""
self._max_retries = value

@property
def session(self) -> str | None:
"""The session cookie for the XUI API.
Returns:
str | None: The session cookie for the XUI API."""
return self._session

@session.setter
def session(self, value: str | None) -> None:
"""Sets the session cookie for the XUI API.
Arguments:
value (str | None): The session cookie for the XUI API."""
self._session = value

def login(self) -> None:
"""Logs into the XUI API and sets the session cookie if successful.
Raises:
ValueError: If the login is unsuccessful."""
endpoint = "login"
headers: dict[str, str] = {}

Expand All @@ -71,6 +131,14 @@ def login(self) -> None:
self.session = cookie

def _check_response(self, response: requests.Response) -> None:
"""Checks the response from the XUI API using the success field.
Arguments:
response (requests.Response): The response from the XUI API.
Raises:
ValueError: If the response status is not successful.
"""
response_json = response.json()

status = response_json.get(ApiFields.SUCCESS)
Expand All @@ -79,6 +147,13 @@ def _check_response(self, response: requests.Response) -> None:
raise ValueError(f"Response status is not successful, message: {message}")

def _url(self, endpoint: str) -> str:
"""Returns the URL for the XUI API (adds the endpoint to the host URL).
Arguments:
endpoint (str): The endpoint for the XUI API.
Returns:
str: The URL for the XUI API."""
return f"{self._host}/{endpoint}"

def _request_with_retry(
Expand All @@ -88,6 +163,20 @@ def _request_with_retry(
headers: dict[str, str],
**kwargs: Any,
) -> requests.Response:
"""Makes a request to the XUI API with retries.
Arguments:
method (Callable[..., requests.Response]): The request method to use.
url (str): The URL for the XUI API.
headers (dict[str, str]): The headers for the request.
**kwargs (Any): Additional keyword arguments for the request.
Returns:
requests.Response: The response from the XUI API.
Raises:
requests.exceptions.RequestException: If the request fails.
requests.exceptions.RetryError: If the maximum number of retries is exceeded."""
logger.debug("%s request to %s...", method.__name__.upper(), url)
for retry in range(1, self.max_retries + 1):
try:
Expand All @@ -114,7 +203,26 @@ def _request_with_retry(
def _post(
self, url: str, headers: dict[str, str], data: dict[str, Any], **kwargs
) -> requests.Response:
"""Makes a POST request to the XUI API.
Arguments:
url (str): The URL for the XUI API.
headers (dict[str, str]): The headers for the request.
data (dict[str, Any]): The data for the request.
**kwargs (Any): Additional keyword arguments for the request.
Returns:
requests.Response: The response from the XUI API."""
return self._request_with_retry(requests.post, url, headers, json=data, **kwargs)

def _get(self, url: str, headers: dict[str, str], **kwargs) -> requests.Response:
"""Makes a GET request to the XUI API.
Arguments:
url (str): The URL for the XUI API.
headers (dict[str, str]): The headers for the request.
**kwargs (Any): Additional keyword arguments for the request.
Returns:
requests.Response: The response from the XUI API."""
return self._request_with_retry(requests.get, url, headers, **kwargs)
17 changes: 10 additions & 7 deletions py3xui/api/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ def get_by_email(self, email: str) -> Client | None:
This endpoint provides details such as traffic statistics and other relevant information
related to the client.
`Source documentation <https://documenter.getpostman.com/view/16802678/2s9YkgD5jm#9d0e5cd5-e6ac-4d72-abca-76cf75af5f00>`_
[Source documentation](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm#9d0e5cd5-e6ac-4d72-abca-76cf75af5f00)
Args:
email (str): The email of the client to retrieve.
Returns:
Client | None: The client object if found, otherwise None.
Examples::
Examples:
```python
import py3xui
api = py3xui.Api.from_env()
client: py3xui.Client = api.get_by_email("email")
client: py3xui.Client = api.client.get_by_email("email")
```
""" # pylint: disable=line-too-long

endpoint = f"panel/api/inbounds/getClientTraffics/{email}"
Expand All @@ -47,20 +49,21 @@ def get_ips(self, email: str) -> str | None:
"""This route is used to retrieve the IP records associated with a specific client
identified by their email.
`Source documentation <https://documenter.getpostman.com/view/16802678/2s9YkgD5jm#06f1214c-dbb0-49f2-81b5-8e924abd19a9>`_
[Source documentation](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm#06f1214c-dbb0-49f2-81b5-8e924abd19a9)
Args:
email (str): The email of the client to retrieve.
Returns:
str | None: The client IPs if found, otherwise None.
Examples::
Examples:
```python
import py3xui
api = py3xui.Api.from_env()
ips = api.get_ips("email")
ips = api.client.get_ips("email")
```
""" # pylint: disable=line-too-long
endpoint = f"panel/api/inbounds/clientIps/{email}"
headers = {"Accept": "application/json"}
Expand Down

0 comments on commit 9f21531

Please sign in to comment.