Metadata-Version: 2.1 Name: py-dactyl Version: 2.0.4 Summary: An easy to use Python wrapper for the Pterodactyl Panel API. Home-page: https://github.com/iamkubi/pydactyl Author: Ryan Kubiak Author-email: iamkubi@gmail.com License: UNKNOWN Project-URL: Documentation, https://pydactyl.readthedocs.io/ Project-URL: Source, https://github.com/iamkubi/pydactyl Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Requires-Python: >=3.4 Description-Content-Type: text/markdown License-File: LICENSE Requires-Dist: requests (>=2.21.0) # pydactyl ![lint-and-test] [![Latest docs][docs-img]][docs] [![Coverage][codecov-img]][codecov] [![Latest version][pypi-img]][pypi] [![Discord][discord-img]][discord-join] An easy to use Python wrapper for the Pterodactyl Panel API. ## State of the project Support for the Pterodactyl 1.x API is complete. The 2.0 Pydactyl release was created to standardize how includes and params are passed. As a result some endpoints (namely nests) **will break** when upgrading from Pydactyl 1.x to 2.0. Pull Requests are being accepted and new endpoints will continue to be added. If you encounter problems, find APIs that haven't been implemented, or have a feature request please file a [Github issue][issues]. ## Documentation Generated documentation can be found at [https://pydactyl.readthedocs.io/][docs] . ## Installing To install with pip: ```shell pip install py-dactyl ``` **If you get an error saying `ImportError: cannot import name 'PterodactylClient' from 'pydactyl'` this probably means you installed the wrong package from pip.** ## Getting Started **Pterodactyl has two different types of API keys: Client (also known as Account) and Application.** Any user can generate an Account API key to control their own servers. The Account API key for an Administrator user will be able to access any server's Client API. Application API keys can only be generated by administrators. These keys can be used to create, modify, and delete servers, among other things. They have access to any server on the panel and can be destructive, so use with care. ### Exploring the API In addition to the documentation you can explore the interface in an interactive Python interpreter using built-ins like `dir()` and the `__doc__` attribute as shown below. ```python from pydactyl import PterodactylClient api = PterodactylClient('debug', 'anything') [i for i in dir(api.client.servers) if not i.startswith('_')] # ['account', 'backups', 'databases', 'files', 'get_server', 'get_server_utilization', 'list_permissions', 'list_servers', 'network', 'schedules', 'send_console_command', 'send_power_action', 'servers', 'settings', 'startup', 'users'] [i for i in dir(api.client.servers.settings) if not i.startswith('_')] # ['reinstall_server', 'rename_server'] print(api.client.servers.settings.rename_server.__doc__) #Renames the server. # Args: # server_id(str): Server identifier (abbreviated UUID) # name(str): New name for the server ``` ### Client API The Client API or Account API is accessed by users of the Pterodactyl panel. Below is the layout of the Client API namespace. ```python api.client.account api.client.servers api.client.servers.backups api.client.servers.databases api.client.servers.files api.client.servers.network api.client.servers.schedules api.client.servers.settings api.client.servers.startup api.client.servers.users ``` A full list of methods available can be found in the [documentation][docs]. Below are examples of how you might get information about your servers. ```python from pydactyl import PterodactylClient # Create a client to connect to the panel and authenticate with your API key. api = PterodactylClient('https://panel.mydomain.com', 'MySuperSecretApiKey') # Get a list of all servers the user has access to my_servers = api.client.servers.list_servers() # Get the unique identifier for the first server. srv_id = my_servers[0]['attributes']['identifier'] # Check the utilization of the server srv_utilization = api.client.servers.get_server_utilization(srv_id) print(srv_utilization) # Turn the server on. api.client.servers.send_power_action(srv_id, 'start') ``` ### Application API **As of Pterodactyl 1.8.0 Application API keys are deprecated and client API keys should now be used exclusively.** The Application API is the administrative API of the Pterodactyl panel. Below is the layout of the Application API namespace. ```python api.locations api.nests api.nodes api.servers api.user ``` Below are examples of how you might use this API. ```python from pydactyl import PterodactylClient # Create a client to connect to the panel and authenticate with your API key. api = PterodactylClient('https://panel.mydomain.com', 'MySuperSecretApiKey') # Create a server. Customize the Nest and Egg IDs to match the IDs in your panel. # This server is created with a limit of 8000 MB of memory, no access to swap, unlimited disk space, in location_id 1. api.servers.create_server(name='My Paper Server', user_id=1, nest_id=1, egg_id=3, memory_limit=8000, swap_limit=0, backup_limit=0, disk_limit=0, location_ids=[1]) < Response[201] > ``` A 201 response indicates success, however if there is a problem with the request Pydactyl will raise an exception with additional details. When updating the location_ids field to an invalid location it displays an error: ```python api.servers.create_server(name='My Paper Server', user_id=1, nest_id=1, egg_id=3, memory_limit=8000, swap_limit=0, disk_limit=0, location_ids=[199]) Traceback (most recent call last): File "", line 6, in File "D:\code\pydactyl\pydactyl\api\servers.py", line 268, in create_server mode='POST', data=data, json=False) File "D:\code\pydactyl\pydactyl\api\base.py", line 98, in _api_request 'code'], errors['detail']) pydactyl.exceptions.PterodactylApiError: Bad API Request(400) - NoViableNodeException - No nodes satisfying the requirements specified for automatic deployment could be found. ``` You can use the User class to add, modify, and delete panel users. ```python # Create a new user result = api.user.create_user('test_user', 'test@gmail.com', 'Test', 'Name') # Get the ID of the created user user_id = result['attributes']['id'] # Get the user info, also returned by create_user() api.user.get_user_info(user_id) {'object': 'user', 'attributes': {'id': 14, 'external_id': None, .... }} # Delete the user api.user.delete_user(user_id=14) ``` ### Includes Pterodactyl API endpoints have different sets of includes you can pass to alter the response. Using includes will cause additional information to show up in the relationships field of the response data. Some endpoints have no includes and some have many. Pydactyl docstrings include examples of valid includes for each endpoint, but they are **not an exhaustive list**. ``` server_includes = [ 'allocations', 'user', 'subusers', 'pack', 'nest', 'egg', 'variables', 'location', 'node', 'databases'] ``` As an example the application server details endpoint has 10 potential includes according to the API docs. Note that the API docs are not always accurate either! Most endpoints that generate lists will have optional includes that can be passed as lists or tuples. ```python api.nodes.list_nodes(includes=('allocations', 'location')) api.servers.get_server_info( server_id=53, includes=['user', 'subusers', 'location']) ``` ### Params Most endpoints with includes will also have a `params` parameter. This can be used to pass additional parameters. Many endpoint specific `params` are already supported by Pydactyl, however some additional params apply universally like `per_page`. ```python api.nodes.list_nodes(params={'per_page': 9001}) api.servers.list_servers(params={'per_page': 9001}) api.users.list_users(params={'per_page': 9001}) ``` ## PterodactylClient Each of the classes in pydactyl could be imported independently, but the PterodactylClient class [pydactyl/api_client.py](pydactyl/api_client.py) provides a simplified interface that imports libraries for you and provides some convenience features like retries and debug logging. ### Retries Instances of [PterodactylClient](pydactyl/api_client.py) will automatically retry calls that fail with a 429 status code indicating that the request was rate-limited by Pterodactyl. By default it uses a backoff_factor of 1 with 3 retries. You can configure the number of retries and backoff_factor when instantiating a client. ```python PterodactylClient('panel', 'key', backoff_factor=2, retries=10) ``` Details on backoff_factor and retires can be found in the [urllib3.util.Retry documentation](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry) . If your server is overloaded or intermittently unavailable you may want to retry on other status codes as well. To do this you can pass in a list of integer HTTP status codes. ```python PterodactylClient('foo', 'bar', extra_retry_codes=[502, 504]) ``` ### Debug logging Most errors from pydactyl will present as exceptions and there is no logging by default, however sometimes additional logging is helpful. You can get request logging by passing `debug=True` to PterodactylClient. ```python app_api = PterodactylClient('https://why', 'broken', debug=True) app_api.servers.list_servers(includes=('egg', 'nest')) DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): why:443 DEBUG:urllib3.connectionpool:https://why:443 "GET /api/application/servers?include=egg%2Cnest HTTP/1.1" 200 None ``` ## Paginated Responses Pydactyl API responses return a [PaginatedResponse object](pydactyl/responses.py#L4) that can be iterated over to automatically fetch additional pages as required. This is currently implemented on many endpoints which frequently return multi-page responses, but not all. ```python # Create a list of all ports allocs = api.nodes.list_node_allocations(node_id) ports = [] for page in allocs: for item in page: ports.append(item['attributes']['port']) len(ports) 151 ``` #### collect() The `collect()` method will fetch the data from all pages of a PaginatedResponse. This allows you to easily fetch all results when you want all the data without having to iterate over the pages. The above example to get a list of ports now looks like: ```python # Create a list of all ports allocs = api.nodes.list_node_allocations(node_id) ports = allocs.collect() len(ports) 151 ``` [docs]: https://pydactyl.readthedocs.io/ [docs-img]: https://readthedocs.org/projects/pydactyl/badge/?version=latest (Latest docs) [pulls]: https://github.com/iamkubi/pydactyl/pulls [issues]: https://github.com/iamkubi/pydactyl/issues [pypi]: https://pypi.python.org/pypi/py-dactyl/ [pypi-img]: https://img.shields.io/pypi/v/py-dactyl.svg [codecov]: https://codecov.io/gh/iamkubi/pydactyl [codecov-img]: https://codecov.io/gh/iamkubi/pydactyl/branch/main/graph/badge.svg [discord-img]: https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat [discord-join]: https://discord.gg/TgZDHPB [lint-and-test]: https://github.com/iamkubi/pydactyl/actions/workflows/lint-and-test.yml/badge.svg?branch=main (https://github.com/iamkubi/pydactyl/actions/workflows/lint-and-test.yml)