72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
40 |
41 | ## Linting and Pre-commit
42 |
43 | We make use of `flake8` and `pre-commit` to ensure that the code style is consistent across the code base.
44 |
45 | Running `flake8` will warn you about any potential style errors in your contribution. You must always check it before pushing. Your commit will be rejected by the build server if it fails to lint.
46 |
47 | `pre-commit` is a powerful tool that helps you automatically lint before you commit. If the linter complains,
48 | the commit is aborted so that you can fix the linting errors before committing again. That way, you never commit
49 | the problematic code in the first place!
50 |
51 | To make linting and checking easy, we have setup pipenv scripts to faciliate installing the commit hooks, and
52 | running the lint checks.
53 |
54 | Here is how you can setup pre-commit with ease:
55 |
56 | 1. Ensure that you have dependencies (and dev-dependencies) installed using pipenv.
57 | 2. Once you're ready, run `pipenv run precommit`, which install the precommit hooks to check your code style
58 | when you're commiting it. It stops code from getting commited, if issues are discovered.
59 | 3. Finally, To run the linting manually, just use `pipenv run lint`, and you should be good to go.
60 |
61 | **Note**: If you really need to commit code, and fix the issues or take assistance, run `git commit --no-verify`
62 | to skips the precommit checks.
63 |
64 | ## Style Guide
65 |
66 | We have enforced several rules related to the code-style which we are following. They have been added
67 | to ensure readable, clean and maintainable code is written, and enable working for everyone smooth and easy.
68 |
69 | We use `flake8` to perform linting, and `pre-commit` to ensure the code is perfect, before getting pushed.
70 |
71 | ### Type Hinting
72 |
73 | [PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added
74 | to the Python Standard Library in version 3.5. Type hints are recognized by most modern code editing tools and
75 | provide useful insight into both the input and output types of a function, preventing the user from having to
76 | go through the codebase to determine these types.
77 |
78 | For an example, a function without annotations would look like:
79 |
80 | ```py
81 | def divide(a, b):
82 | """Divide the two given arguments."""
83 | return a / b
84 | ```
85 |
86 | With annotations, this is how the function would look:
87 |
88 | ```py
89 | def divide(a: int, b: int) -> float:
90 | """Divide the two given arguments."""
91 | return a / b
92 | ```
93 |
94 | Python type-hinting is relatively easy to use, but helps to keep the code more maintainable, clean, and also
95 | enables the use of type annotations in the future. In a lot of situations, the type-hinting enables your editors
96 | to give better intellisense and suggestions based on what you're working on.
97 |
98 | Python being a dynamically typed language, There is a neat tool called MyPy that enables static type hinting
99 | and checking on the code. You can read more about it [here](https://mypy.readthedocs.io/en/stable/).
100 |
101 | ### Docstring formatting directive
102 |
103 | Many documentation packages provide support for automatic documentation generation from the codebase's docstrings.
104 | These tools utilize special formatting directives to enable richer formatting in the generated documentation.
105 |
106 | For example:
107 |
108 | ```py
109 | import typing as t
110 |
111 |
112 | def foo(bar: int, baz: t.Optional[t.Dict[str, str]] = None) -> bool:
113 | """
114 | Does some things with some stuff.
115 |
116 | :param bar: Some input
117 | :param baz: Optional, some dictionary with string keys and values
118 |
119 | :return: Some boolean
120 | """
121 | ...
122 | ```
123 |
124 | Since we don't utilize automatic documentation generation, use of this syntax should not be used in the code contributed here.
125 |
126 | Should the purpose and type of the input variables not be easily discernable from the variable name and type
127 | annotation a prose explanation can be used. Explicit references to variables, function, classes, etc. should be
128 | wrapped with backticks (`` ` ``), such as \`variable\`.
129 |
130 | For example, the above docstring would become:
131 |
132 | ```py
133 | import typing as t
134 |
135 |
136 | def foo(bar: int, baz: t.Optional[t.Dict[str, str]] = None) -> bool:
137 | """
138 | Does some things with some stuff.
139 |
140 | This function takes an index, `bar` and checks for its presence in the database `baz`, passed as a dictionary.
141 | Returns `False` if `baz` is not passed or if `bar` wasn't found in `baz`.
142 | """
143 | ...
144 | ```
145 |
146 | ### Strings and Quotes
147 |
148 | Preference is to use double-quotes (`"`) wherever possible. Single quotes should only be used for cases where it is
149 | logical. Exceptions might include:
150 |
151 | - Using a key string within an f-string: f"Today is {data['day']}".
152 | - Using double quotes within a string: 'She said "oh dear" in response'
153 |
154 | Multi-line strings or Docstrings should be ensured that it's wrapped in triple double quotes (`"""my string"""`).
155 |
156 | Wildcard imports should be avoided.
157 |
158 | ### Work in Progress (WIP) PRs
159 |
160 | When any PR is actively being worked on, and is not ready for merging, it should be marked as a WIP. This provides
161 | both a visual and functional indicator that the PR is in a draft state, and is not ready for review or merge.
162 |
163 | Github provides a feature of marking a PR as Draft to indicate that it is not ready for review or merge. This
164 | feature should be utilized in place of the traditional method of prepending `[WIP]` to the PR title.
165 |
166 | As stated earlier, **ensure that "Allow edits from maintainers" is checked**. This gives permission for maintainers to commit changes directly to your fork, speeding up the review process.
167 |
168 | [Here](https://github.blog/2019-02-14-introducing-draft-pull-requests/) is all the info you need about Draft PRs
169 | in Github.
170 |
171 | ## Changes to this Arrangement
172 |
173 | Every kind of projects evolve over time, and these contributing guidelines are no different.
174 |
175 | This document is open to any kind of contributions, and PRs. Feel free to contribute to this guideline by
176 | adding, or changing anything, that you feel can change it, and improve it aswell.
177 |
178 | ## Credits
179 |
180 | This contributing guidelines file was inspired by
181 | [Python discord's Contributing Guidelines](https://github.com/python-discord/bot/blob/master/CONTRIBUTING.md).
182 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present Sunrit Jana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | requests = "==2.28.2"
8 | aiohttp = "==3.8.3"
9 |
10 | [dev-packages]
11 | flake8 = "~=5.0.4"
12 | flake8-annotations = "~=2.9.1"
13 | flake8-bugbear = "~=22.8.23"
14 | flake8-import-order = "~=0.18.1"
15 | flake8-tidy-imports = "~=4.8.0"
16 | pep8-naming = "~=0.13.2"
17 | pyright = "~=1.1.260"
18 | pre-commit = "~=2.20.0"
19 | ipython = "~=8.5.0"
20 | # Dependencies for documentation
21 | recommonmark = "~=0.7.1"
22 | myst-parser = "~=0.18.0"
23 | sphinx-rtd-theme = "~=1.0.0"
24 | # Development
25 | hypixelio = {editable = true, extras = ["all"], path = "."}
26 |
27 | [scripts]
28 | precommit = "pre-commit install"
29 | lint = "pre-commit run --all-files"
30 | tests = "python -m unittest"
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ```
2 | __ __ _ __ ________
3 | / / / /_ ______ (_) _____ / / / _/ __ \
4 | / /_/ / / / / __ \/ / |/_/ _ \/ / / // / / /
5 | / __ / /_/ / /_/ / /> __/ / _/ // /_/ /
6 | /_/ /_/\__, / .___/_/_/|_|\___/_/ /___/\____/
7 | /____/_/
8 | ```
9 |
10 | HypixelIO
11 |
12 | A Modern, Efficient and Easy way of interacting with the Hypixel API!
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
68 |
69 | ## ✨ Why choose HypixelIO?
70 |
71 | - Modern way of handling requests
72 | - Modern OOP based structure
73 | - Both Async and blocking support
74 | - Simple ratelimit handling and caching
75 | - Elegant design with complete optimization
76 | - Easy to use with a modern and simple design
77 | - Complete API coverage
78 |
79 | ## 🚀 Installing
80 |
81 | **Python 3.7 or above is required!**
82 |
83 | ```sh
84 | # Windows
85 | py -3 -m pip install -U HypixelIO
86 |
87 | # Linux or MacOS
88 | python3 -m pip install -U HypixelIO
89 |
90 | # Install the nightly build
91 | python3 -m pip install -U git+https://github.com/janaSunrise/HypixelIO
92 | ```
93 |
94 | You can also get extra features with this library. Here's how:
95 |
96 | ```sh
97 | # Use [speedups] to speed up only for async API
98 | python3 -m pip install -U "HypixelIO[speedups]"
99 | ```
100 |
101 | ## Usage
102 |
103 | ```python
104 | from hypixelio import Client, Converters
105 |
106 | client = Client(api_key="your-api-key")
107 |
108 | boosters = client.get_boosters() # Get the boosters object
109 |
110 | friends = client.get_friends(uuid="user's-uuid") # Returns the Friends object
111 | # Or, if you don't know the UUID
112 | friends = client.get_friends(name="user's-username")
113 |
114 | print(boosters[0].id)
115 | print(friends.friends[0].receiver_id)
116 | ```
117 |
118 | ### Async API usage
119 |
120 | ```python
121 | import asyncio
122 |
123 | from hypixelio import AsyncClient, AsyncConverters
124 |
125 | client = AsyncClient(api_key="your-api-key")
126 |
127 | # Async function to fetch info
128 | async def fetch_from_hypixel():
129 | boosters = await client.get_boosters() # Get the boosters object
130 |
131 | friends = await client.get_friends(uuid="user's-uuid") # Returns the Friends object
132 | # Or, if you don't know the UUID
133 | friends = await client.get_friends(name="user's-username")
134 |
135 | # Safely close the connection
136 | await client.close()
137 |
138 | return boosters, friends
139 |
140 | # Run the coroutine using `asyncio`
141 | boosters, friends = asyncio.run(fetch_from_hypixel())
142 |
143 | print(boosters[0].id)
144 | print(friends.friends[0].receiver_id)
145 | ```
146 |
147 | **Find more examples [here](https://github.com/janaSunrise/HypixelIO/tree/main/examples)!**
148 |
149 | ## 📢 Changelog
150 |
151 | If you're interested in seeing the **Changelog**, Go [here!](https://github.com/janaSunrise/HypixelIO/blob/main/CHANGELOG.md)
152 |
153 | ## 🤝 Contributing
154 |
155 | Contributions, issues and feature requests are welcome. After cloning & setting up project locally, you can just submit
156 | a PR to this repo and it will be deployed once it's accepted.
157 |
158 | ⚠️ It’s good to have descriptive commit messages, or PR titles so that other contributors can understand about your
159 | commit or the PR Created. Read [conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.3/) before
160 | making the commit message. You can find our contributing guidelines
161 | [here](https://github.com/janaSunrise/HypixelIO/blob/main/CONTRIBUTING.md)
162 |
163 | We have a branch called `dev` containing development code. If you're contributing, Remember to contribute to
164 | `dev` branch, instead of `main`.
165 |
166 | ## 💬 Get in touch
167 |
168 | If you have various suggestions, questions or want to discuss things with our community, Have a look at
169 | [Github discussions](https://github.com/janaSunrise/HypixelIO/discussions) or join our Discord server!
170 |
171 | [](https://discord.gg/MKC4qna4Gz)
172 |
173 | ## 👋 Show your support
174 |
175 | Be sure to drop a 🌟 if you like the project!
176 |
177 | ## ▶ Links
178 |
179 | - [Official Documentation](http://hypixelio.rtfd.io/)
180 | - [Raise an Issue](https://github.com/janaSunrise/HypixelIO/issues)
181 | - [Discussions](https://github.com/janaSunrise/HypixelIO/discussions)
182 | - [Hypixel API Documentation](https://api.hypixel.net)
183 |
184 | Made by Sunrit Jana with ❤
185 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | SPHINXOPTS ?=
3 | SPHINXBUILD ?= sphinx-build
4 | SOURCEDIR = .
5 | BUILDDIR = build
6 |
7 | .PHONY: help Makefile
8 |
9 | # Put it first so that "make" without argument is like "make help".
10 | help:
11 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
12 |
13 | clean:
14 | rm -rf $(BUILDDIR)/*
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
20 |
--------------------------------------------------------------------------------
/docs/_async/api.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: hypixelio
2 |
3 | API Reference
4 | =============
5 |
6 | Client
7 | ~~~~~~
8 |
9 | .. autoclass:: hypixelio._async.AsyncClient
10 | :members:
11 | :undoc-members:
12 |
13 |
14 | Converter
15 | ~~~~~~~~~
16 |
17 | .. autoclass:: hypixelio._async.AsyncConverters
18 | :members:
19 | :undoc-members:
20 |
21 |
22 | Utils
23 | ~~~~~
24 |
25 | .. autoclass:: hypixelio._async.Utils
26 | :members:
27 | :undoc-members:
28 |
--------------------------------------------------------------------------------
/docs/_static/css/custom.css:
--------------------------------------------------------------------------------
1 | /* CSS files import */
2 | @import "../basic.css";
3 |
4 | /* Fonts */
5 | @import url("https://fonts.googleapis.com/css2?family=Fira+Code&family=Poppins&family=Varela+Round&display=swap");
6 |
7 | /* Reset */
8 | * {
9 | box-sizing: border-box;
10 | }
11 |
12 | html {
13 | scroll-behavior: smooth;
14 | }
15 |
16 | body {
17 | font-family: "Poppins", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
18 | Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
19 | font-weight: 400;
20 | }
21 |
22 | /* Other styles */
23 | div.sphinxsidebarwrapper {
24 | margin-right: 30px;
25 | }
26 |
27 | div.sphinxsidebar {
28 | width: 260px;
29 | font-size: 14px;
30 | line-height: 1.5;
31 | }
32 |
33 | .wy-nav-content {
34 | max-width: 1500px;
35 | width: 75%;
36 | }
37 |
38 | .rst-content div[class^="highlight"],
39 | .rst-content pre.literal-block {
40 | border: transparent;
41 | overflow-x: auto;
42 | margin: 1px 0 24px;
43 | border-radius: 10px;
44 | background: #f1f1f1;
45 | }
46 |
47 | .rst-content .linenodiv pre,
48 | .rst-content div[class^="highlight"] pre,
49 | .rst-content pre.literal-block {
50 | font-family: "Fira Code", monospace;
51 | font-size: 12px;
52 | line-height: 1.4;
53 | }
54 |
55 | .rst-content .toctree-wrapper > p.caption,
56 | h1,
57 | h2,
58 | h3,
59 | h4,
60 | h5,
61 | h6,
62 | legend {
63 | margin-top: 0;
64 | font-weight: 700;
65 | font-family: "Varela Round", sans-serif;
66 | }
67 |
68 | .rst-content code.literal,
69 | .rst-content tt.literal {
70 | color: #373737;
71 | white-space: normal;
72 | border-radius: 0.5em;
73 | padding: 0.4% 0.4% 0.3%;
74 | background: #e8e8e8;
75 | border: transparent;
76 | }
77 |
78 | .ethical-rst {
79 | visibility: hidden;
80 | }
81 |
82 | .wy-side-nav-search {
83 | background: #4a94c4;
84 | }
85 |
86 | .version {
87 | color: #d9d9d9;
88 | }
89 |
90 | dt {
91 | padding-left: 8px;
92 | }
93 |
94 | .sig {
95 | border-radius: 0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334em;
96 | }
97 |
98 | html.writer-html4 .rst-content dl:not(.docutils) > dt,
99 | html.writer-html5
100 | .rst-content
101 | dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
102 | > dt {
103 | border: 1px solid #72cdf7;
104 | }
105 |
106 | html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list) > dt,
107 | html.writer-html5
108 | .rst-content
109 | dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
110 | dl:not(.field-list)
111 | > dt {
112 | border: 1px solid #adadad;
113 | }
114 |
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: hypixelio
2 |
3 | API Reference
4 | =============
5 |
6 | Version Related Info
7 | ---------------------
8 |
9 | There is a single way to check the version for this library.
10 |
11 | .. data:: __version__
12 |
13 | A string representation of the version. e.g. ``'0.1.0b1'``. This is based
14 | off of :pep:`440`.
15 |
16 |
17 | Clients
18 | -------
19 |
20 | .. autoclass:: Client
21 | :members:
22 | :undoc-members:
23 |
24 |
25 | Converters
26 | ----------
27 |
28 | .. autoclass:: Converters
29 | :members:
30 | :undoc-members:
31 |
32 |
33 | Utility
34 | -------
35 |
36 | .. autoclass:: Utils
37 | :members:
38 | :undoc-members:
39 |
40 |
41 | Async to sync portal
42 | --------------------
43 |
44 | .. autoclass:: hypixelio._async.Portal
45 | :members:
46 | :undoc-members:
47 |
48 | .. autofunction:: hypixelio._async.create_portal
49 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | from hypixelio import __version__ as hypixelio_version
5 |
6 | # Sphinx path setup
7 | sys.path.insert(0, os.path.abspath(".."))
8 | sys.path.append(os.path.abspath("extensions"))
9 |
10 | # Project info configuration
11 | project = "HypixelIO"
12 | copyright = "Copyright 2021-present, Sunrit Jana"
13 | author = "Sunrit Jana"
14 |
15 | release = hypixelio_version
16 | branch = (
17 | "main"
18 | if hypixelio_version.endswith("a") or hypixelio_version.endswith("b") or hypixelio_version.endswith("rc")
19 | else "v" + hypixelio_version
20 | )
21 |
22 | # General configuration
23 | extensions = [
24 | "myst_parser",
25 | "resourcelinks",
26 | "sphinx.ext.autodoc",
27 | "sphinx.ext.extlinks",
28 | "sphinx.ext.napoleon",
29 | ]
30 |
31 | rst_prolog = """
32 | .. |coro| replace:: This function is a |coroutine_link|_.
33 | .. |maybecoro| replace:: This function *could be a* |coroutine_link|_.
34 | .. |coroutine_link| replace:: *coroutine*
35 | .. _coroutine_link: https://docs.python.org/3/library/asyncio-task.html#coroutine
36 | """
37 |
38 | # Warnings
39 | suppress_warnings = ["myst.header"]
40 |
41 | extlinks = {
42 | "issue": ("https://github.com/janaSunrise/HypixelIO/issues/%s", "GH-"),
43 | }
44 |
45 | source_suffix = {".rst": "restructuredtext", ".md": "markdown", ".txt": "markdown"}
46 |
47 | master_doc = "index"
48 |
49 | # Add any paths that contain templates here, relative to this directory.
50 | templates_path = ["_templates"]
51 |
52 | # List of patterns, relative to source directory, that match files and
53 | # directories to ignore when looking for source files.
54 | # This pattern also affects html_static_path and html_extra_path.
55 | exclude_patterns = ["_build", "Thumbs.db", ".DS_STORE"]
56 |
57 | # Links for the RST
58 | resource_links = {
59 | "repo": "https://github.com/janaSunrise/HypixelIO/",
60 | "issues": "https://github.com/janaSunrise/HypixelIO/issues",
61 | "discussions": "https://github.com/janaSunrise/HypixelIO/discussions",
62 | "examples": f"https://github.com/janaSunrise/HypixelIO/tree/{branch}/examples",
63 | }
64 |
65 | # -- Options for HTML output -------------------------------------------------
66 | html_theme = "sphinx_rtd_theme"
67 |
68 | # Add any paths that contain custom static files (such as style sheets) here,
69 | # relative to this directory. They are copied after the builtin static files,
70 | # so a file named "default.css" will overwrite the builtin "default.css".
71 | html_static_path = ["_static"]
72 | html_css_files = ["css/custom.css"]
73 |
--------------------------------------------------------------------------------
/docs/exceptions.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: hypixelio
2 |
3 | API Reference
4 | =============
5 |
6 | Errors
7 | ------
8 |
9 | .. autoexception:: hypixelio.exceptions.InvalidArgumentError
10 |
11 |
12 | .. autoexception:: hypixelio.exceptions.RateLimitError
13 |
14 |
15 | .. autoexception:: hypixelio.exceptions.PlayerNotFoundError
16 |
17 |
18 | .. autoexception:: hypixelio.exceptions.GuildNotFoundError
19 |
20 |
21 | .. autoexception:: hypixelio.exceptions.HypixelAPIError
22 |
23 |
24 | .. autoexception:: hypixelio.exceptions.MojangAPIError
25 |
26 |
27 | .. autoexception:: hypixelio.exceptions.CrafatarAPIError
28 |
--------------------------------------------------------------------------------
/docs/extensions/resourcelinks.py:
--------------------------------------------------------------------------------
1 | # Credit to sphinx.ext.extlinks for being a good starter
2 | # Copyright 2007-2020 by the Sphinx team
3 | # Licensed under BSD.
4 |
5 | import typing as t
6 |
7 | import sphinx
8 | from docutils import nodes, utils
9 | from docutils.nodes import Node, system_message
10 | from docutils.parsers.rst.states import Inliner
11 | from sphinx.application import Sphinx
12 | from sphinx.util.nodes import split_explicit_title
13 | from sphinx.util.typing import RoleFunction
14 |
15 |
16 | def make_link_role(resource_links: t.Dict[str, str]) -> RoleFunction:
17 | def role(
18 | typ: str,
19 | rawtext: str,
20 | text: str,
21 | lineno: int,
22 | inliner: Inliner,
23 | options: t.Dict = {}, # noqa: B006
24 | content: t.List[str] = [], # noqa: B006
25 | ) -> t.Tuple[t.List[Node], t.List[system_message]]:
26 | text = utils.unescape(text)
27 |
28 | has_explicit_title, title, key = split_explicit_title(text)
29 |
30 | full_url = resource_links[key]
31 |
32 | if not has_explicit_title:
33 | title = full_url
34 |
35 | pnode = nodes.reference(title, title, internal=False, refuri=full_url)
36 | return [pnode], []
37 |
38 | return role
39 |
40 |
41 | def add_link_role(app: Sphinx) -> None:
42 | app.add_role("resource", make_link_role(app.config.resource_links))
43 |
44 |
45 | def setup(app: Sphinx) -> t.Dict[str, t.Any]:
46 | app.add_config_value("resource_links", {}, "env")
47 | app.connect("builder-inited", add_link_role)
48 | return {"version": sphinx.__display_version__, "parallel_read_safe": True}
49 |
--------------------------------------------------------------------------------
/docs/faq.rst:
--------------------------------------------------------------------------------
1 | FAQs
2 | ====
3 |
4 | No frequently asked questions are present here! But, we are open to the public.
5 | Feel free to suggest a new question, open an issue or submit one via pull requests.
6 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to HypixelIO!
2 | ======================
3 |
4 | HypixelIO is a modern, robust, and efficient wrapper for the Hypixel API. With support for both Async
5 | and blocking programming, This has everything for you to work with.
6 |
7 | .. toctree::
8 | :maxdepth: 2
9 | :titlesonly:
10 |
11 | Welcome
12 | Home
13 | whats_new
14 | faq
15 |
16 | Features
17 | --------
18 |
19 | - Modern way of handling requests
20 | - Both async and sync support.
21 | - Simple rate handling, and caching.
22 | - Speed optimized
23 | - Easy to use with a modern and simple design
24 |
25 | Getting started
26 | -----------------
27 |
28 | Is this your first time using the library? This is the place to get started!
29 |
30 | You can get quickly started with the library over here :doc:`quickstart`.
31 |
32 | You can find more examples in the :resource:`repository `. You can also add your own!
33 | Just create a Pull request, and get it added.
34 |
35 |
36 | Getting help
37 | ------------
38 |
39 | Stuck with library, and need help? These resources might help.
40 |
41 | - Try the :doc:`faq` first, it's got answers to all common questions.
42 | - If you're looking for something specific, try the :ref:`index ` or :ref:`searching `.
43 | - Report bugs in the :resource:`issue tracker `.
44 | - Ask in our :resource:`GitHub discussions page `.
45 |
46 | Modules and manuals
47 | -------------------
48 |
49 | Here are the modules which constitute the API. All the API documentation, right here to help
50 | you understand and use this library.
51 |
52 | .. toctree::
53 | :maxdepth: 1
54 |
55 | API reference
56 | Asynchronous API reference <_async/api.rst>
57 | Exceptions API reference
58 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 |
11 | set SOURCEDIR=.
12 | set BUILDDIR=build
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
20 | echo.installed, then set the SPHINXBUILD environment variable to point
21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
22 | echo.may add the Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/docs/quickstart.md:
--------------------------------------------------------------------------------
1 | #
2 |
3 | ```{include} ../README.md
4 |
5 | ```
6 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx-rtd-theme==1.0.0
2 | myst-parser==0.18.0
3 | recommonmark==0.7.1
4 | Sphinx==5.1.1
5 |
--------------------------------------------------------------------------------
/docs/whats_new.md:
--------------------------------------------------------------------------------
1 | ```{include} ../CHANGELOG.md
2 |
3 | ```
4 |
--------------------------------------------------------------------------------
/examples/async-player.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import os
3 | from textwrap import dedent
4 |
5 | from hypixelio import AsyncClient
6 |
7 |
8 | async def fetch() -> None:
9 | # Login to the API
10 | client = AsyncClient(api_key=os.environ["HYPIXEL_KEY"])
11 |
12 | # Get a player object
13 | player = await client.get_player(name="janaSunrise")
14 |
15 | # Close the session
16 | await client.close()
17 |
18 | # Extract the data from the object
19 | name, uuid, achievements = player.name, player.uuid, player.achievements
20 |
21 | # Print the data
22 | print(
23 | dedent(
24 | f"""
25 | Name: {name}
26 | UUID: {uuid}
27 | Achievements: {achievements}
28 | """
29 | )
30 | )
31 |
32 |
33 | asyncio.run(fetch())
34 |
--------------------------------------------------------------------------------
/examples/get-guild-data.py:
--------------------------------------------------------------------------------
1 | import os
2 | from textwrap import dedent
3 |
4 | import hypixelio as hp
5 |
6 | # Init the Client
7 | client = hp.Client(api_key=os.environ["HYPIXEL_KEY"])
8 |
9 | # Get the guild object
10 | guild = client.get_guild(name="2k")
11 |
12 | # Get the essential data
13 | name, ranking, achievements = guild.name, guild.legacy_ranking, guild.achievements
14 |
15 | # Print the data
16 | print(
17 | dedent(
18 | f"""
19 | Name: {name}
20 | ranking: {ranking}
21 | achievements: {achievements}
22 | """
23 | )
24 | )
25 |
--------------------------------------------------------------------------------
/examples/get-player-data.py:
--------------------------------------------------------------------------------
1 | import os
2 | from textwrap import dedent
3 |
4 | import hypixelio as hp
5 |
6 | # Login to the API
7 | client = hp.Client(api_key=os.environ["HYPIXEL_KEY"])
8 |
9 | # Get a player object
10 | player = client.get_player(name="janaSunrise")
11 |
12 | # Extract the data from the object
13 | name, uuid, achievements = player.name, player.uuid, player.achievements
14 |
15 | # Print the data
16 | print(
17 | dedent(
18 | f"""
19 | Name: {name}
20 | UUID: {uuid}
21 | Achievements: {achievements}
22 | """
23 | )
24 | )
25 |
--------------------------------------------------------------------------------
/examples/get-watchdog-stats.py:
--------------------------------------------------------------------------------
1 | import os
2 | from textwrap import dedent
3 |
4 | import hypixelio as hp
5 |
6 | # Init the Client
7 | client = hp.Client(api_key=os.environ["HYPIXEL_KEY"])
8 |
9 | # Get the watchdog stats
10 | watchdog = client.get_watchdog_info()
11 |
12 | # Extract the Data
13 | last_minute_ban = watchdog.last_minute_ban
14 | total_bans = watchdog.total_bans
15 | rolling_daily = watchdog.rolling_daily
16 |
17 | # Display the data
18 | print(
19 | dedent(
20 | f"""
21 | Last minute ban: {last_minute_ban}
22 | Total Bans: {total_bans}
23 | Rolling daily: {rolling_daily}
24 | """
25 | )
26 | )
27 |
--------------------------------------------------------------------------------
/examples/user-to-uuid.py:
--------------------------------------------------------------------------------
1 | from hypixelio import Converters
2 |
3 | # Convert to UUID
4 | uuid = Converters.username_to_uuid("janaSunrise")
5 |
6 | # Show the UUID
7 | print(f"Your UUID is {uuid}")
8 |
--------------------------------------------------------------------------------
/examples/uuid-to-username.py:
--------------------------------------------------------------------------------
1 | from hypixelio import Converters
2 |
3 | # Convert to UUID
4 | username = Converters.uuid_to_username("c8438cdd126043448cca9e28646efbe7")
5 |
6 | # Show the UUID
7 | print(f"Your username is {username}")
8 |
--------------------------------------------------------------------------------
/hypixelio/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Tuple
2 |
3 | from . import constants
4 | from ._async import AsyncClient, AsyncConverters
5 | from ._async import Utils as AsyncUtils
6 | from .lib import Client, Converters, Utils
7 |
8 | __author__ = "Sunrit Jana"
9 | __email__ = "warriordefenderz@gmail.com"
10 | __version__ = "1.4.1"
11 | __license__ = "MIT license"
12 | __copyright__ = "Copyright 2021-present Sunrit Jana"
13 |
14 | __all__: Tuple[str, ...] = (
15 | "__author__",
16 | "__email__",
17 | "__version__",
18 | "__license__",
19 | "__copyright__",
20 | "AsyncClient",
21 | "AsyncConverters",
22 | "AsyncUtils",
23 | "Client",
24 | "Converters",
25 | "Utils",
26 | "constants",
27 | )
28 |
--------------------------------------------------------------------------------
/hypixelio/_async/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import AsyncClient
2 | from .converters import AsyncConverters
3 | from .utils import Utils
4 |
--------------------------------------------------------------------------------
/hypixelio/_async/client.py:
--------------------------------------------------------------------------------
1 | __all__ = ("AsyncClient",)
2 |
3 | import asyncio
4 | import random
5 | from types import TracebackType
6 | from typing import (
7 | Any,
8 | Dict,
9 | Optional,
10 | Type,
11 | Union,
12 | cast
13 | )
14 |
15 | import aiohttp
16 |
17 | from .converters import AsyncConverters
18 | from ..base import BaseClient
19 | from ..constants import HYPIXEL_API, TIMEOUT
20 | from ..exceptions import (
21 | GuildNotFoundError,
22 | HypixelAPIError,
23 | InvalidArgumentError,
24 | PlayerNotFoundError,
25 | RateLimitError,
26 | )
27 | from ..models.boosters import Boosters
28 | from ..models.find_guild import FindGuild
29 | from ..models.friends import Friends
30 | from ..models.games import Games
31 | from ..models.guild import Guild
32 | from ..models.key import Key
33 | from ..models.leaderboard import Leaderboard
34 | from ..models.player import Player
35 | from ..models.player_status import PlayerStatus
36 | from ..models.recent_games import RecentGames
37 | from ..models.skyblock import (
38 | SkyblockActiveAuction,
39 | SkyblockBazaar,
40 | SkyblockNews,
41 | SkyblockProfile,
42 | SkyblockUserAuction,
43 | )
44 | from ..models.watchdog import Watchdog
45 | from ..utils import form_url
46 |
47 |
48 | class AsyncClient(BaseClient):
49 | """
50 | The client for this wrapper that handles the requests, authentication, loading and usages of the end user.
51 |
52 | Examples
53 | --------
54 | Import the async client first.
55 |
56 | >>> from hypixelio._async import AsyncClient
57 |
58 | If you have a single API key, Here's how to authenticate
59 |
60 | >>> client = AsyncClient(api_key="123-456-789")
61 |
62 | You can use multiple API keys to authenticate too. (Better option for load balancing)
63 |
64 | >>> client = AsyncClient(api_key=["123-456", "789-000", "568-908"])
65 | """
66 |
67 | def __init__(self, api_key: Union[str, list]) -> None:
68 | """
69 | Parameters
70 | ----------
71 | api_key: Union[str, list]
72 | The API key generated in Hypixel server using the `/api new` command.
73 | """
74 | super().__init__(api_key)
75 |
76 | self._session: Optional[aiohttp.ClientSession] = None
77 | self._lock = asyncio.Lock()
78 |
79 | async def close(self) -> None:
80 | """Close the AIOHTTP sessions to prevent memory leaks."""
81 | if self._session is not None:
82 | await self._session.close()
83 |
84 | async def _fetch(
85 | self,
86 | url: str,
87 | data: Optional[Dict[str, Any]] = None,
88 | api_key: bool = True,
89 | ) -> dict:
90 | """
91 | Fetch the JSON response from the API along with the ability to include GET request parameters and support
92 | Authentication using API key too.
93 |
94 | Parameters
95 | ----------
96 | url: str
97 | The URL to be accessed from the API root URL.
98 | data: Optional[dict]
99 | The GET Request's Key-Value Pair. Example: {"uuid": "abc"} is converted to `?uuid=abc`. Defaults to None.
100 | api_key: bool
101 | If key is needed for the endpoint.
102 |
103 | Returns
104 | -------
105 | Tuple[dict, bool]
106 | The JSON response obtained after fetching the API, along with success value in the response.
107 | """
108 | if not self._session:
109 | self._session = aiohttp.ClientSession()
110 |
111 | # Check if ratelimit is hit
112 | if self._is_ratelimit_hit():
113 | raise RateLimitError(self.retry_after)
114 |
115 | if not data:
116 | data = {}
117 |
118 | # Assign a random key
119 | if api_key:
120 | self.headers["API-Key"] = random.choice(self._api_key)
121 |
122 | url = form_url(HYPIXEL_API, url, data)
123 |
124 | async with self._lock:
125 | async with self._session.get(
126 | url, headers=self.headers, timeout=TIMEOUT
127 | ) as response:
128 | # 404 handling
129 | if response.status == 429:
130 | raise HypixelAPIError("The route specified does not exis")
131 |
132 | # 429 status code handling
133 | if response.status == 429:
134 | self._handle_ratelimit(cast(dict, response.headers))
135 |
136 | # 403 Status code handling
137 | if response.status == 403:
138 | raise HypixelAPIError("Invalid key specified!")
139 |
140 | if api_key and "RateLimit-Limit" in response.headers:
141 | self._update_ratelimit(cast(dict, response.headers))
142 |
143 | try:
144 | json = await response.json()
145 | except Exception as exception:
146 | raise HypixelAPIError(f"{exception}")
147 | else:
148 | if not json["success"]:
149 | self._handle_api_failure(json)
150 |
151 | return json
152 |
153 | @staticmethod
154 | async def _filter_name_uuid(name: Optional[str] = None, uuid: Optional[str] = None) -> str:
155 | if not name and not uuid:
156 | raise InvalidArgumentError(
157 | "Named argument for player's either username or UUID not found."
158 | )
159 |
160 | if name:
161 | uuid = await AsyncConverters.username_to_uuid(name)
162 |
163 | return uuid # type: ignore
164 |
165 | # Context managers
166 | async def __aenter__(self) -> "AsyncClient":
167 | self._session = aiohttp.ClientSession()
168 |
169 | return self
170 |
171 | async def __aexit__(
172 | self,
173 | exc_type: Optional[Type[BaseException]],
174 | exc_val: Optional[BaseException],
175 | exc_tb: Optional[TracebackType],
176 | ) -> None:
177 | if self._session is not None:
178 | await self._session.close()
179 |
180 | # Hypixel API endpoint methods
181 | async def get_key_info(self, api_key: Optional[str] = None) -> Key:
182 | """
183 | Get info about a specific Hypixel API key.
184 |
185 | Parameters
186 | ----------
187 | api_key: Optional[str]
188 | The API key generated in Hypixel server using the `/api new` command. Defaults to pre-specified keys.
189 |
190 | Returns
191 | -------
192 | Key
193 | The Key object created for the API key specified.
194 | """
195 | if not api_key:
196 | api_key = random.choice(self._api_key)
197 |
198 | json = await self._fetch(self.url["api_key"], {"key": api_key})
199 | return Key(json["record"])
200 |
201 | async def get_boosters(self) -> Boosters:
202 | """
203 | Get the Hypixel coin boosters, and all the info about them.
204 |
205 | Returns
206 | -------
207 | Boosters
208 | The boosters object, with all the info from the API.
209 | """
210 | json = await self._fetch(self.url["boosters"])
211 |
212 | return Boosters(json["boosters"], json)
213 |
214 | async def get_player(
215 | self, name: Optional[str] = None, uuid: Optional[str] = None
216 | ) -> Player:
217 | """
218 | Get all info about a Hypixel player using his username or his player UUID.
219 |
220 | Parameters
221 | ----------
222 | name: Optional[str]
223 | The Optional string value for the Username. Defaults to None.
224 | uuid: Optional[str]
225 | The Optional string Value to the UUID. Defaults to None.
226 |
227 | Returns
228 | -------
229 | Player
230 | The player object with all the info obtained from the API.
231 | """
232 | uuid = await self._filter_name_uuid(name, uuid)
233 | json = await self._fetch(self.url["player"], {"uuid": uuid})
234 |
235 | if not json["player"]:
236 | raise PlayerNotFoundError("Null is returned", name)
237 |
238 | return Player(json["player"])
239 |
240 | async def get_friends(
241 | self, name: Optional[str] = None, uuid: Optional[str] = None
242 | ) -> Friends:
243 | """
244 | Get the friends, and all their info of specified Hypixel player.
245 |
246 | Parameters
247 | ----------
248 | name: Optional[str]
249 | The Optional string value for the Username of a hypixel player. Defaults to None.
250 | uuid: Optional[str]
251 | The UUID of a Certain Hypixel Player. Defaults to None.
252 |
253 | Returns
254 | -------
255 | Friends
256 | The Friend object with all info from the API.
257 | """
258 | uuid = await self._filter_name_uuid(name, uuid)
259 | json = await self._fetch(self.url["friends"], {"uuid": uuid})
260 |
261 | return Friends(json["records"])
262 |
263 | async def get_watchdog_info(self) -> Watchdog:
264 | """
265 | Get all the stats about the Watchdog (Punishment stats) for the last few days/
266 |
267 | Returns
268 | -------
269 | Watchdog
270 | The Watchdog object with all the info.
271 | """
272 | json = await self._fetch(self.url["watchdog"])
273 |
274 | return Watchdog(json)
275 |
276 | async def get_guild(
277 | self, name: Optional[str] = None, uuid: Optional[str] = None
278 | ) -> Guild:
279 | """
280 | Get info about a specific Hypixel guild using the Name, or the Guild's UUID.
281 |
282 | Parameters
283 | ----------
284 | name: Optional[str]
285 | The Name of the Guild. Defaults to None.
286 | uuid: Optional[str]
287 | The ID Of the guild. Defaults to None.
288 |
289 | Returns
290 | -------
291 | Guild
292 | The Guild object with the info fetched from the API.
293 | """
294 | if uuid:
295 | json = await self._fetch(self.url["guild"], {"id": uuid})
296 | elif name:
297 | json = await self._fetch(self.url["guild"], {"name": name})
298 | else:
299 | raise InvalidArgumentError(
300 | "Named argument for guild's name or UUID not found."
301 | )
302 |
303 | if not json["guild"]:
304 | raise GuildNotFoundError("Value returned is null")
305 |
306 | return Guild(json["guild"])
307 |
308 | async def get_games_info(self) -> Games:
309 | """
310 | Get the list of all Hypixel games, and their info.
311 |
312 | Returns
313 | -------
314 | Games
315 | The Games object with all the info.
316 | """
317 | json = await self._fetch(self.url["game_info"])
318 |
319 | return Games(json["games"], json["playerCount"])
320 |
321 | async def get_leaderboards(self) -> Leaderboard:
322 | """
323 | Get the leaderboard for the Hypixel games with their info.
324 |
325 | Returns
326 | -------
327 | Leaderboard
328 | The Leaderboard object with all info.
329 | """
330 | json = await self._fetch(self.url["leaderboards"])
331 |
332 | return Leaderboard(json["leaderboards"])
333 |
334 | async def find_guild(
335 | self, guild_name: Optional[str] = None, player_uuid: Optional[str] = None
336 | ) -> FindGuild:
337 | """
338 | Find a guild using the Guild's name or a Player's UUID.
339 |
340 | Parameters
341 | ----------
342 | guild_name: Optional[str]
343 | The name of the Guild. Defaults to None.
344 | player_uuid: Optional[str]
345 | The UUID of the Player to find his guild. Defaults to None.
346 |
347 | Returns
348 | -------
349 | FindGuild
350 | The ID of the guild being find.
351 | """
352 | if guild_name:
353 | json = await self._fetch(self.url["find_guild"], {"byName": guild_name})
354 | elif player_uuid:
355 | json = await self._fetch(self.url["find_guild"], {"byUuid": player_uuid})
356 | else:
357 | raise InvalidArgumentError(
358 | "Named argument for guild's name or UUID not found."
359 | )
360 |
361 | return FindGuild(json)
362 |
363 | async def get_player_status(
364 | self, name: Optional[str] = None, uuid: Optional[str] = None
365 | ) -> PlayerStatus:
366 | """
367 | Get the status about a Player using his username or UUID.
368 |
369 | Parameters
370 | ----------
371 | name: Optional[str]
372 | The Optional string value for the Username. Defaults to None.
373 | uuid: Optional[str]
374 | The Optional string Value to the UUID. Defaults to None.
375 |
376 | Returns
377 | -------
378 | PlayerStatus
379 | The Player status object consisting of all info from the API.
380 | """
381 | uuid = await self._filter_name_uuid(name, uuid)
382 | json = await self._fetch(self.url["status"], {"uuid": uuid})
383 |
384 | return PlayerStatus(json)
385 |
386 | async def get_player_recent_games(
387 | self, name: Optional[str] = None, uuid: Optional[str] = None
388 | ) -> RecentGames:
389 | """
390 | Get the recent games played by a Hypixel player using his Username or UUID.
391 |
392 | Parameters
393 | ----------
394 | name: Optional[str]
395 | The Optional string value for the Username. Defaults to None.
396 | uuid: Optional[str]
397 | The Optional string Value to the UUID. Defaults to None.
398 |
399 | Returns
400 | -------
401 | RecentGames
402 | The recent games for the respective player specified.
403 | """
404 | uuid = await self._filter_name_uuid(name, uuid)
405 | json = await self._fetch(self.url["recent_games"], {"uuid": uuid})
406 |
407 | return RecentGames(json)
408 |
409 | async def get_skyblock_news(self) -> SkyblockNews:
410 | json = await self._fetch(self.url["skyblock_news"])
411 |
412 | return SkyblockNews(json)
413 |
414 | async def get_skyblock_profile(
415 | self, name: Optional[str] = None, uuid: Optional[str] = None
416 | ) -> SkyblockProfile:
417 | """
418 | Get the skyblock information and profile about a specific user as passed in the requirements.
419 |
420 | Parameters
421 | ----------
422 | name: Optional[str]
423 | The player's name in Hypixel
424 | uuid: Optional[str]
425 | The player's global UUID
426 |
427 | Returns
428 | -------
429 | SkyblockProfile
430 | The skyblock profile model for the specified user.
431 | """
432 | uuid = await self._filter_name_uuid(name, uuid)
433 | json = await self._fetch(self.url["skyblock_profile"], {"profile": uuid})
434 |
435 | if not json["profile"]:
436 | raise PlayerNotFoundError("The skyblock player does not exist", uuid)
437 |
438 | return SkyblockProfile(json)
439 |
440 | async def get_skyblock_user_auctions(
441 | self, name: Optional[str] = None, uuid: Optional[str] = None
442 | ) -> SkyblockUserAuction:
443 | """
444 | Get the skyblock auction info about a specific user.
445 |
446 | Parameters
447 | ----------
448 | name: Optional[str]
449 | The player's name in Hypixel
450 | uuid: Optional[str]
451 | The player's global UUID
452 |
453 | Returns
454 | -------
455 | SkyblockUserAuction
456 | The skyblock auction model for the user.
457 | """
458 | uuid = await self._filter_name_uuid(name, uuid)
459 | json = await self._fetch(self.url["skyblock_auctions"], {"profile": uuid})
460 |
461 | if not json["auctions"]:
462 | raise PlayerNotFoundError("The skyblock player does not exist!", uuid)
463 |
464 | return SkyblockUserAuction(json)
465 |
466 | async def get_skyblock_active_auctions(
467 | self, page: int = 0
468 | ) -> SkyblockActiveAuction:
469 | """
470 | Get the list of active auctions in skyblock and use the data.
471 |
472 | Parameters
473 | ----------
474 | page: int
475 | The skyblock auction page to lookup.
476 |
477 | Returns
478 | -------
479 | SkyblockActiveAuction
480 | The active auction model.
481 | """
482 | json = await self._fetch(self.url["skyblock_active_auctions"], {"page": page})
483 | return SkyblockActiveAuction(json)
484 |
485 | async def get_skyblock_bazaar(self) -> SkyblockBazaar:
486 | """
487 | Get the skyblock bazaar items
488 |
489 | Returns
490 | -------
491 | SkyblockBazaar
492 | The bazaar model object representing each produc
493 | """
494 | json = await self._fetch(self.url["skyblock_bazaar"])
495 | return SkyblockBazaar(json)
496 |
497 | async def get_resources_achievements(self) -> dict:
498 | data = await self._fetch(self.url["achievements"], api_key=False)
499 | return data["achievements"]
500 |
501 | async def get_resources_challenges(self) -> dict:
502 | data = await self._fetch(self.url["challenges"], api_key=False)
503 | return data["challenges"]
504 |
505 | async def get_resources_quests(self) -> dict:
506 | data = await self._fetch(self.url["quests"], api_key=False)
507 | return data["quests"]
508 |
509 | async def get_resources_guild_achievements(self) -> dict:
510 | data = await self._fetch(self.url["guild_achievements"], api_key=False)
511 | return {"one_time": data["one_time"], "tiered": data["tiered"]}
512 |
513 | async def get_skyblock_skills(self) -> dict:
514 | data = await self._fetch(self.url["skyblock_skills"], api_key=False)
515 | return {
516 | "skills": data["skills"],
517 | "collections": data["collections"],
518 | }
519 |
520 | async def get_skyblock_collections(self) -> dict:
521 | data = await self._fetch(self.url["skyblock_collections"], api_key=False)
522 | return data["collections"]
523 |
--------------------------------------------------------------------------------
/hypixelio/_async/converters.py:
--------------------------------------------------------------------------------
1 | __all__ = ("AsyncConverters",)
2 |
3 | from typing import Any, Dict, Union, cast
4 |
5 | import aiohttp
6 |
7 | from ..constants import MOJANG_API, TIMEOUT
8 | from ..endpoints import API_PATH
9 | from ..exceptions import MojangAPIError, PlayerNotFoundError
10 |
11 |
12 | class AsyncConverters:
13 | url = API_PATH["MOJANG"]
14 |
15 | @classmethod
16 | async def _fetch(cls, url: str) -> Union[dict, list]:
17 | """
18 | The internal function for fetching info from the Mojang API.
19 |
20 | Parameters
21 | ----------
22 | url: str
23 | The Mojang URL, whose JSON is supposed to be fetched.
24 |
25 | Returns
26 | -------
27 | Union[dict, list]
28 | The JSON response from the Mojang API.
29 | """
30 | session = aiohttp.ClientSession()
31 |
32 | async with session.get(f"{MOJANG_API}{url}", timeout=TIMEOUT) as response:
33 | if response.status == 204:
34 | raise PlayerNotFoundError(
35 | "Error code 204 returned during conversion to UUID.", None
36 | )
37 |
38 | if response.status == 400:
39 | raise PlayerNotFoundError("Badly formed UUID error.", None)
40 |
41 | try:
42 | json = await response.json()
43 | except Exception:
44 | raise MojangAPIError()
45 | else:
46 | if "error" in json:
47 | raise MojangAPIError(f"An error occurred! {json['errorMessage']}")
48 |
49 | return json
50 |
51 | @classmethod
52 | async def username_to_uuid(cls, username: str) -> str:
53 | """
54 | This is a method, to convert username in minecraft, for its respective UUID.
55 |
56 | Parameters
57 | ----------
58 | username: str
59 | This is the minecraft user, which is passed to this function for the UUID Conversion.
60 |
61 | Returns
62 | -------
63 | str
64 | returns the converted UUID for the respective username.
65 | """
66 | json = cast(
67 | Dict[str, Any],
68 | await AsyncConverters._fetch(
69 | AsyncConverters.url["username_to_uuid"].format(username)
70 | ),
71 | )
72 |
73 | return json["id"]
74 |
75 | @classmethod
76 | async def uuid_to_username(cls, uuid: str) -> str:
77 | """
78 | Method to convert the UUID for your profile to the username for your Minecraft account.
79 |
80 | Parameters
81 | ----------
82 | uuid: str
83 | This is the minecraft UUID, which is passed to this function for the UUID to username Conversion.
84 |
85 | Returns
86 | -------
87 | str
88 | The username for the respective minecraft UUID is returned.
89 | """
90 | json = await AsyncConverters._fetch(
91 | AsyncConverters.url["uuid_to_username"].format(uuid)
92 | )
93 |
94 | return json[-1]["name"]
95 |
--------------------------------------------------------------------------------
/hypixelio/_async/utils.py:
--------------------------------------------------------------------------------
1 | __all__ = ("Utils",)
2 |
3 | from typing import Optional, Union
4 |
5 | import aiohttp
6 |
7 | from .converters import AsyncConverters as Converters
8 | from ..constants import TIMEOUT
9 | from ..endpoints import API_PATH
10 | from ..exceptions import CrafatarAPIError, InvalidArgumentError
11 |
12 |
13 | class Utils:
14 | mojang_url = API_PATH["MOJANG"]
15 | url = API_PATH["CRAFATAR"]
16 |
17 | @classmethod
18 | async def _crafatar_fetch(cls, url: str) -> str:
19 | """
20 | Method to fetch the JSON from the Crafatar API.
21 |
22 | Parameters
23 | ----------
24 | url: str
25 | The Crafatar URL, whose JSON is supposed to be fetched.
26 |
27 | Returns
28 | -------
29 | ClientResponse
30 | The JSON response from the Crafatar API.
31 | """
32 | session = aiohttp.ClientSession()
33 |
34 | async with session.get(
35 | f"https://crafatar.com/{url}", timeout=TIMEOUT
36 | ) as response:
37 | if response.status == 422:
38 | raise InvalidArgumentError(
39 | "Invalid URL passed. Either user does not exist, or URL is malformed."
40 | )
41 |
42 | try:
43 | return await response.text()
44 | except Exception:
45 | raise CrafatarAPIError()
46 |
47 | @staticmethod
48 | async def _filter_name_uuid(
49 | name: Optional[str] = None, uuid: Optional[str] = None
50 | ) -> str:
51 | if not name and not uuid:
52 | raise InvalidArgumentError(
53 | "Please provide a named argument of the player's username or player's UUID."
54 | )
55 |
56 | if name:
57 | uuid = await Converters.username_to_uuid(name)
58 |
59 | return uuid # type: ignore
60 |
61 | @classmethod
62 | def _form_crafatar_url(cls, route: str) -> str:
63 | """
64 | This function forms the crafatar API URL for fetching skins of users.
65 |
66 | Parameters
67 | ----------
68 | route: str
69 | The URL path to form for crafatar API.
70 |
71 | Returns
72 | -------
73 | str
74 | The API URL formed to fetch.
75 | """
76 | return f"https://crafatar.com{route}"
77 |
78 | @classmethod
79 | async def get_name_history(
80 | cls,
81 | name: Optional[str] = None,
82 | uuid: Optional[str] = None,
83 | changed_at: bool = False,
84 | ) -> Union[list, dict]:
85 | """
86 | Get the name history with records for a player.
87 |
88 | Parameters
89 | ----------
90 | name: Optional[str]
91 | The username of the player. Defaults to None.
92 | uuid: Optional[str]
93 | The UUID of the player. Defaults to None.
94 | changed_at: bool
95 | Toggle to true, if you need when the player changed name. Defaults to False.
96 |
97 | Returns
98 | -------
99 | Union[list, dict]
100 | The list or dictionary with the name history and records.
101 | """
102 | uuid = await cls._filter_name_uuid(name, uuid)
103 | json = await Converters._fetch(Utils.mojang_url["name_history"].format(uuid))
104 |
105 | if changed_at:
106 | return json
107 |
108 | usernames = []
109 | for data in json:
110 | usernames.append(data["name"])
111 |
112 | return usernames
113 |
114 | @classmethod
115 | async def get_avatar(
116 | cls, name: Optional[str] = None, uuid: Optional[str] = None
117 | ) -> str:
118 | """
119 | Get the avatar of the specified player.
120 |
121 | Parameters
122 | ----------
123 | name: Optional[str]
124 | The username of the player. Defaults to None.
125 | uuid: Optional[str]
126 | The UUID of the player. Defaults to None.
127 |
128 | Returns
129 | -------
130 | str
131 | The URL containing the image of the avatar.
132 | """
133 | uuid = await cls._filter_name_uuid(name, uuid)
134 | await Utils._crafatar_fetch(Utils.url["avatar"].format(uuid))
135 |
136 | return Utils._form_crafatar_url(Utils.url["avatar"].format(uuid))
137 |
138 | @classmethod
139 | async def get_head(
140 | cls, name: Optional[str] = None, uuid: Optional[str] = None
141 | ) -> str:
142 | """
143 | Get the head skin of the specified player.
144 |
145 | Parameters
146 | ----------
147 | name: Optional[str]
148 | The username of the player. Defaults to None.
149 | uuid: Optional[str]
150 | The UUID of the player. Defaults to None.
151 |
152 | Returns
153 | -------
154 | str
155 | The URL containing the image of the head.
156 | """
157 | uuid = await cls._filter_name_uuid(name, uuid)
158 | await Utils._crafatar_fetch(Utils.url["head"].format(uuid))
159 |
160 | return Utils._form_crafatar_url(Utils.url["head"].format(uuid))
161 |
162 | @classmethod
163 | async def get_body(
164 | cls, name: Optional[str] = None, uuid: Optional[str] = None
165 | ) -> str:
166 | """
167 | Get the whole body's skin of the specified player
168 |
169 | Parameters
170 | ----------
171 | name: Optional[str]
172 | The username of the player. Defaults to None.
173 | uuid: Optional[str]
174 | The UUID of the player. Defaults to None.
175 |
176 | Returns
177 | -------
178 | str
179 | The URL containing the image of the whole body.
180 | """
181 | uuid = await cls._filter_name_uuid(name, uuid)
182 | await Utils._crafatar_fetch(Utils.url["body"].format(uuid))
183 |
184 | return Utils._form_crafatar_url(Utils.url["body"].format(uuid))
185 |
--------------------------------------------------------------------------------
/hypixelio/base.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from abc import ABC, abstractmethod
3 | from datetime import datetime, timedelta
4 | from typing import Any, Dict, Optional, Union
5 |
6 | from .endpoints import API_PATH
7 | from .exceptions import HypixelAPIError, RateLimitError
8 |
9 |
10 | # TODO: Move to `requests.session` for better performance and avoid creating a new session for every request.
11 | class BaseClient(ABC):
12 | def __init__(self, api_key: Union[str, list]):
13 | self.url = API_PATH["HYPIXEL"]
14 |
15 | if not isinstance(api_key, list):
16 | self._api_key = [api_key]
17 |
18 | # Ratelimiting config
19 | self.requests_remaining = -1
20 | self.total_requests = 0
21 | self._ratelimit_reset = datetime(1998, 1, 1)
22 | self.retry_after = datetime(1998, 1, 1)
23 |
24 | # Headers
25 | from hypixelio import __version__ as hypixelio_version
26 |
27 | python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
28 | self.headers = {
29 | "User-Agent": f"HypixelIO v{hypixelio_version} Client (https://github.com/janaSunrise/HypixelIO) "
30 | f"Python/{python_version}"
31 | }
32 |
33 | # Define the dunder methods
34 | def __repr__(self):
35 | return (
36 | f"<{self.__class__.__qualname__} requests_remaining={self.requests_remaining} total_requests="
37 | f"{self.total_requests} retry_after={self.retry_after}>"
38 | )
39 |
40 | def _update_ratelimit(self, resp_headers: Dict[str, Any]) -> None:
41 | """Utility to update ratelimiting variables"""
42 |
43 | if "RateLimit-Limit" in resp_headers:
44 | if self.total_requests == 0:
45 | self.total_requests = int(resp_headers["RateLimit-Limit"])
46 |
47 | self.requests_remaining = int(resp_headers["RateLimit-Remaining"])
48 | self._ratelimit_reset = datetime.now() + timedelta(
49 | seconds=int(resp_headers["RateLimit-Reset"])
50 | )
51 |
52 | def _is_ratelimit_hit(self) -> bool:
53 | """Utility to check if ratelimit has been hit"""
54 |
55 | is_ratelimit_hit = self.requests_remaining != -1 \
56 | and (self.requests_remaining == 0 and self._ratelimit_reset > datetime.now()) \
57 | or self.retry_after \
58 | and (self.retry_after > datetime.now())
59 |
60 | return is_ratelimit_hit
61 |
62 | def _handle_ratelimit(self, resp_headers: Dict[str, Any]) -> None:
63 | """Raise error if ratelimit has been hit"""
64 |
65 | self.requests_remaining = 0
66 | self.retry_after = datetime.now() + timedelta(
67 | seconds=int(resp_headers["Retry-After"])
68 | )
69 |
70 | raise RateLimitError(self.retry_after)
71 |
72 | @staticmethod
73 | def _handle_api_failure(json: Dict[str, Any]) -> None:
74 | """Handle raising error if API response is not successful."""
75 |
76 | raise HypixelAPIError(reason=json["cause"])
77 |
78 | @staticmethod
79 | @abstractmethod
80 | def _filter_name_uuid(name: Optional[str] = None, uuid: Optional[str] = None) -> str:
81 | ...
82 |
83 | # Utility for keys
84 | def add_key(self, api_key: Union[str, list]) -> None:
85 | """
86 | Add a Hypixel API Key to the list of the API keys.
87 |
88 | Parameters
89 | ----------
90 | api_key: Union[str, list]
91 | The API key(s) to be added to the lis
92 |
93 | Returns
94 | -------
95 | None
96 | """
97 | if isinstance(api_key, str):
98 | api_key = [api_key]
99 |
100 | for key in api_key:
101 | if key in self._api_key:
102 | continue
103 |
104 | self._api_key.append(key)
105 |
106 | def remove_key(self, api_key: Union[str, list]) -> None:
107 | """
108 | Remove a Hypixel API Key from the list of the API keys.
109 |
110 | Parameters
111 | ----------
112 | api_key: Union[str, list]
113 | The API key(s) to be removed from the lis
114 |
115 | Returns
116 | -------
117 | None
118 | """
119 | if isinstance(api_key, str):
120 | api_key = [api_key]
121 |
122 | for key in api_key:
123 | if key not in self._api_key:
124 | continue
125 |
126 | self._api_key.remove(key)
127 |
--------------------------------------------------------------------------------
/hypixelio/constants.py:
--------------------------------------------------------------------------------
1 | """All the constant variables used in the Hypixel library."""
2 | __all__ = (
3 | "HYPIXEL_API",
4 | "MOJANG_API",
5 | "RANKS",
6 | "RANK_COLORS",
7 | "BEDWARS_PRESTIGE_COLOR",
8 | "BEDWARS_PRESTIGE_RANKS",
9 | "GUILD_COLORS__TAGS",
10 | "SKYWARS_PRESTIGES_RANKS",
11 | "SKYWARS_PRESTIGE_COLOR",
12 | "TIMEOUT",
13 | )
14 |
15 | HYPIXEL_API = "https://api.hypixel.net"
16 | MOJANG_API = "https://api.mojang.com"
17 |
18 | TIMEOUT = 10
19 | DEFAULT_HEADERS = {
20 | "Accept-Encoding": "gzip, deflate" # To enable gzip compression and reduce bandwidth
21 | }
22 |
23 | RANKS = {
24 | "NONE": None,
25 | "VIP": "VIP",
26 | "VIP_PLUS": "VIP+",
27 | "MVP": "MVP",
28 | "MVP_PLUS": "MVP+",
29 | "SUPERSTAR": "MVP++",
30 | "YOUTUBER": "YOUTUBE",
31 | "PIG+++": "PIG+++",
32 | "BUILD TEAM": "BUILD TEAM",
33 | "HELPER": "HELPER",
34 | "MODERATOR": "MOD",
35 | "ADMIN": "ADMIN",
36 | "SLOTH": "SLOTH",
37 | "OWNER": "OWNER",
38 | }
39 |
40 | RANK_COLORS = {
41 | "VIP": int("55FF55", 16),
42 | "VIP+": int("55FF55", 16),
43 | "MVP": int("55FFFF", 16),
44 | "MVP+": int("55FFFF", 16),
45 | "MVP++": int("FFAA00", 16),
46 | "YOUTUBE": int("FF5555", 16),
47 | "PIG+++": int("FF69DC", 16),
48 | "BUILD TEAM": int("00AAAA", 16),
49 | "EVENTS": int("FFAA00", 16),
50 | "HELPER": int("5555FF", 16),
51 | "MOD": int("00AA00", 16),
52 | "ADMIN": int("AA0000", 16),
53 | "SLOTH": int("AA0000", 16),
54 | "OWNER": int("AA0000", 16),
55 | None: int("607D8B", 16),
56 | }
57 |
58 | BEDWARS_PRESTIGE_RANKS = (
59 | "Stone",
60 | "Iron",
61 | "Gold",
62 | "Diamond",
63 | "Emerald",
64 | "Sapphire",
65 | "Ruby",
66 | "Crystal",
67 | "Opal",
68 | "Amethyst",
69 | "Rainbow",
70 | "Iron Prime",
71 | "Gold Prime",
72 | "Diamond Prime",
73 | "Emerald Prime",
74 | "Sapphire Prime",
75 | "Ruby Prime",
76 | "Crystal Prime",
77 | "Opal Prime",
78 | "Amethyst Prime",
79 | "Mirror",
80 | "Light",
81 | "Dawn",
82 | "Dusk",
83 | "Air",
84 | "Wind",
85 | "Nebula",
86 | "Thunder",
87 | "Earth",
88 | "Water",
89 | "Fire",
90 | )
91 |
92 | SKYWARS_PRESTIGES_RANKS = (
93 | "Stone",
94 | "Iron",
95 | "Gold",
96 | "Diamond",
97 | "Emerald",
98 | "Sapphire",
99 | "Ruby",
100 | "Crystal",
101 | "Opal",
102 | "Amethyst",
103 | "Rainbow",
104 | "Mystic",
105 | )
106 |
107 | BEDWARS_PRESTIGE_COLOR = (
108 | int("607D8B", 16),
109 | int("95A5A6", 16),
110 | int("FFAC0F", 16),
111 | int("55FFFF", 16),
112 | int("00AA00", 16),
113 | int("00AAAA", 16),
114 | int("AA0000", 16),
115 | int("FF69DC", 16),
116 | int("2562E9", 16),
117 | int("AA00AA", 16),
118 | int("1ABC9C", 16),
119 | int("607D8B", 16),
120 | int("95A5A6", 16),
121 | int("FFAC0F", 16),
122 | int("55FFFF", 16),
123 | int("00AA00", 16),
124 | int("00AAAA", 16),
125 | int("AA0000", 16),
126 | int("FF69DC", 16),
127 | int("2562E9", 16),
128 | int("AA00AA", 16),
129 | )
130 |
131 | SKYWARS_PRESTIGE_COLOR = (
132 | int("607D8B", 16),
133 | int("95A5A6", 16),
134 | int("FFAC0F", 16),
135 | int("55FFFF", 16),
136 | int("00AA00", 16),
137 | int("00AAAA", 16),
138 | int("AA0000", 16),
139 | int("FF69DC", 16),
140 | int("2562E9", 16),
141 | int("AA00AA", 16),
142 | int("AA00AA", 16),
143 | )
144 |
145 | GUILD_COLORS__TAGS = {
146 | "GRAY": int("607D8B", 16),
147 | "GOLD": int("FFAC0F", 16),
148 | "DARK_AQUA": int("00AAAA", 16),
149 | "DARK_GREEN": int("00AA00", 16),
150 | "YELLOW": int("FFFF55", 16),
151 | }
152 |
--------------------------------------------------------------------------------
/hypixelio/endpoints.py:
--------------------------------------------------------------------------------
1 | """List of all the various endpoint paths for all the APIs used."""
2 |
3 | API_PATH = {
4 | "HYPIXEL": {
5 | "api_key": "/key",
6 | "boosters": "/boosters",
7 | "player": "/player",
8 | "friends": "/friends",
9 | "watchdog": "/watchdogstats",
10 | "guild": "/guild",
11 | "game_info": "/gameCounts",
12 | "leaderboards": "/leaderboards",
13 | "find_guild": "/findGuild",
14 | "status": "/status",
15 | "recent_games": "/recentgames",
16 | "skyblock_auctions": "/skyblock/auction",
17 | "skyblock_active_auctions": "/skyblock/auctions",
18 | "skyblock_bazaar": "/skyblock/bazaar",
19 | "skyblock_profile": "/skyblock/profile",
20 | "skyblock_news": "/skyblock/news",
21 | "skyblock_skills": "/resources/skyblock/skills",
22 | "skyblock_collections": "/resources/skyblock/collections",
23 | "achievements": "/resources/achievements",
24 | "challenges": "/resources/challenges",
25 | "quests": "/resources/quests",
26 | "guild_achievements": "/resources/guilds/achievements",
27 | },
28 | "MOJANG": {
29 | "username_to_uuid": "/users/profiles/minecraft/{}",
30 | "uuid_to_username": "/user/profiles/{}/names",
31 | "name_history": "/user/profiles/{}/names",
32 | },
33 | "CRAFATAR": {
34 | "avatar": "/avatars/{}",
35 | "head": "/renders/head/{}",
36 | "body": "/renders/body/{}",
37 | "skins": "/skins/{}",
38 | },
39 | }
40 |
--------------------------------------------------------------------------------
/hypixelio/exceptions.py:
--------------------------------------------------------------------------------
1 | """Custom exceptions used for the librar."""
2 | __all__ = (
3 | "InvalidArgumentError",
4 | "PlayerNotFoundError",
5 | "HypixelAPIError",
6 | "RateLimitError",
7 | "GuildNotFoundError",
8 | "CrafatarAPIError",
9 | "MojangAPIError",
10 | )
11 |
12 | import typing as t
13 | from datetime import datetime
14 |
15 |
16 | class InvalidArgumentError(Exception):
17 | """Raised when invalid argument is present, or no argument is specified."""
18 |
19 | ...
20 |
21 |
22 | class APIError(Exception):
23 | """Base class for all API exceptions."""
24 |
25 | def __init__(self, service: str, reason: t.Optional[str] = None) -> None:
26 | error = f"There was an issue with the {service} API."
27 | if reason:
28 | error += f" Reason: {reason}."
29 |
30 | super().__init__(error)
31 | self.error = error
32 |
33 | def __str__(self) -> str:
34 | return self.error
35 |
36 |
37 | class HypixelAPIError(APIError):
38 | """Raised when there is an issue with the Hypixel API or during fetch."""
39 |
40 | def __init__(self, reason: t.Optional[str] = None) -> None:
41 | super().__init__("Hypixel", reason)
42 |
43 |
44 | class CrafatarAPIError(APIError):
45 | """Raised during issues faced by Crafatar API."""
46 |
47 | def __init__(self, reason: t.Optional[str] = None) -> None:
48 | super().__init__("Crafatar", reason)
49 |
50 |
51 | class MojangAPIError(APIError):
52 | """Raised when the Mojang API is facing some problems."""
53 |
54 | def __init__(self, reason: t.Optional[str] = None) -> None:
55 | super().__init__("Mojang", reason)
56 |
57 |
58 | # Rate-limit exception
59 | class RateLimitError(Exception):
60 | """Raised when the ratelimit for the hypixel API is hit."""
61 |
62 | def __init__(self, retry_after: datetime) -> None:
63 | error = (
64 | "The ratelimit for the hypixel API was hit. Try again after"
65 | f"{retry_after.strftime('%Y-%m-%d %H:%M:%S')}."
66 | )
67 |
68 | super().__init__(error)
69 | self.error = error
70 |
71 | def __str__(self) -> str:
72 | return self.error
73 |
74 |
75 | # Hypixel-related exceptions
76 | class PlayerNotFoundError(Exception):
77 | """Raised when the specified player is not found."""
78 |
79 | def __init__(
80 | self, reason: t.Optional[str] = None, user: t.Optional[str] = None
81 | ) -> None:
82 | error = "Player not found."
83 | if reason:
84 | error += f" {reason}."
85 |
86 | super().__init__(error)
87 |
88 | self.error = error
89 | self.user = user
90 |
91 | def __str__(self) -> str:
92 | return self.error
93 |
94 |
95 | class GuildNotFoundError(Exception):
96 | """Raised when the specified guild is not found."""
97 |
98 | def __init__(self, reason: t.Optional[str] = None) -> None:
99 | """
100 | Parameters
101 | ----------
102 | reason: str
103 | The reason for the Error. Defaults to None.
104 | """
105 | error = "Guild not found."
106 | if reason:
107 | error += f" {reason}."
108 |
109 | super().__init__(error)
110 | self.error = error
111 |
112 | def __str__(self) -> str:
113 | return self.error
114 |
--------------------------------------------------------------------------------
/hypixelio/lib/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import Client
2 | from .converters import Converters
3 | from .utils import Utils
4 |
--------------------------------------------------------------------------------
/hypixelio/lib/client.py:
--------------------------------------------------------------------------------
1 | __all__ = ("Client",)
2 |
3 | import random
4 | from types import TracebackType
5 | from typing import Any, Dict, Optional, Type, Union, cast
6 |
7 | import requests
8 |
9 | from .converters import Converters
10 | from ..base import BaseClient
11 | from ..constants import DEFAULT_HEADERS, HYPIXEL_API, TIMEOUT
12 | from ..exceptions import (
13 | GuildNotFoundError,
14 | HypixelAPIError,
15 | InvalidArgumentError,
16 | PlayerNotFoundError,
17 | RateLimitError,
18 | )
19 | from ..models.boosters import Boosters
20 | from ..models.find_guild import FindGuild
21 | from ..models.friends import Friends
22 | from ..models.games import Games
23 | from ..models.guild import Guild
24 | from ..models.key import Key
25 | from ..models.leaderboard import Leaderboard
26 | from ..models.player import Player
27 | from ..models.player_status import PlayerStatus
28 | from ..models.recent_games import RecentGames
29 | from ..models.skyblock import (
30 | SkyblockActiveAuction,
31 | SkyblockBazaar,
32 | SkyblockNews,
33 | SkyblockProfile,
34 | SkyblockUserAuction,
35 | )
36 | from ..models.watchdog import Watchdog
37 | from ..utils import form_url
38 |
39 |
40 | class Client(BaseClient):
41 | """
42 | Client for handling requests, authentication, and usage of the Hypixel API for the end user.
43 |
44 | Examples
45 | --------
46 | If you have a single API key, Here's how to authenticate
47 |
48 | >>> import hypixelio
49 | >>> client = hypixelio.Client(api_key="123-456-789")
50 |
51 | You can use multiple API keys to authenticate too. (Better option for load balancing)
52 |
53 | >>> client = hypixelio.Client(api_key=["123-456", "789-000", "568-908"])
54 | """
55 |
56 | def __init__(self, api_key: Union[str, list]) -> None:
57 | """
58 | Parameters
59 | ----------
60 | api_key: Union[str, list]
61 | The API key generated in Hypixel server using the `/api new` command.
62 | """
63 | super().__init__(api_key)
64 |
65 | self._session = requests.Session()
66 | self._session.headers.update(DEFAULT_HEADERS)
67 |
68 | def _fetch(
69 | self,
70 | url: str,
71 | data: Optional[Dict[str, Any]] = None,
72 | *,
73 | api_key: bool = True,
74 | ) -> Dict[str, Any]:
75 | """
76 | Fetch the JSON response from the API along with the ability to include GET request parameters and support
77 | Authentication using API key too.
78 |
79 | Parameters
80 | ----------
81 | url: str
82 | The URL to be accessed from the API root URL.
83 | data: Optional[dict]
84 | The GET request's key-value pair. eg: `{"uuid": "abc"}` is converted to `?uuid=abc`. Defaults to None.
85 | api_key: bool
86 | If key is needed for the endpoin
87 |
88 | Returns
89 | -------
90 | Dict[str, Any]
91 | The JSON response obtained after fetching the API, along with success value in the response.
92 | """
93 | # Check if ratelimit is hit
94 | if self._is_ratelimit_hit():
95 | raise RateLimitError(self.retry_after)
96 |
97 | # If no data for JSON
98 | if not data:
99 | data = {}
100 |
101 | # Assign a random key if the Key parameter exists.
102 | if api_key:
103 | self.headers["API-Key"] = random.choice(self._api_key)
104 |
105 | # Form the URL to fetch
106 | url = form_url(HYPIXEL_API, url, data)
107 |
108 | # Core fetch logic
109 | with self._session.get(url, timeout=TIMEOUT, headers=self.headers) as response:
110 | # 404 handling
111 | if response.status_code == 404:
112 | raise HypixelAPIError("The route specified does not exis")
113 |
114 | # 429 Code handle
115 | if response.status_code == 429:
116 | self._handle_ratelimit(cast(dict, response.headers))
117 |
118 | # 403 Code handle
119 | if response.status_code == 403:
120 | raise HypixelAPIError("Invalid key specified!")
121 |
122 | # Ratelimit handling
123 | if api_key and "RateLimit-Limit" in response.headers:
124 | self._update_ratelimit(cast(dict, response.headers))
125 |
126 | try:
127 | json = response.json()
128 | except Exception as exc:
129 | raise HypixelAPIError(f"{exc}")
130 | else:
131 | if not json["success"]:
132 | self._handle_api_failure(json)
133 |
134 | return json
135 |
136 | def __enter__(self) -> "Client":
137 | return self
138 |
139 | def __exit__(
140 | self,
141 | exc_type: Optional[Type[BaseException]],
142 | exc_val: Optional[BaseException],
143 | exc_tb: Optional[TracebackType],
144 | ) -> None:
145 | pass
146 |
147 | # Purge the session when the object is deleted
148 | def __del__(self) -> None:
149 | self._session.close()
150 |
151 | @staticmethod
152 | def _filter_name_uuid(name: Optional[str] = None, uuid: Optional[str] = None) -> str:
153 | if not name and not uuid:
154 | raise InvalidArgumentError(
155 | "Named argument for player's either username or UUID not found."
156 | )
157 |
158 | if name:
159 | uuid = Converters.username_to_uuid(name)
160 |
161 | return uuid # type: ignore
162 |
163 | # Hypixel API endpoint methods.
164 | def get_key_info(self, api_key: Optional[str] = None) -> Key:
165 | """
166 | Get info about a specific Hypixel API key.
167 |
168 | Parameters
169 | ----------
170 | api_key: Optional[str]
171 | The API key generated in Hypixel server using the `/api new` command. Defaults to pre-specified keys.
172 |
173 | Returns
174 | -------
175 | Key
176 | The Key object created for the API key specified.
177 | """
178 | api_key = api_key or random.choice(self._api_key)
179 |
180 | json = self._fetch(self.url["api_key"], {"key": api_key})
181 | return Key(json["record"])
182 |
183 | def get_boosters(self) -> Boosters:
184 | """
185 | Get the Hypixel coin boosters, and all the info about them.
186 |
187 | Returns
188 | -------
189 | Boosters
190 | The boosters object, with all the info from the API.
191 | """
192 | json = self._fetch(self.url["boosters"])
193 |
194 | return Boosters(json["boosters"], json)
195 |
196 | def get_player(
197 | self, name: Optional[str] = None, uuid: Optional[str] = None
198 | ) -> Player:
199 | """
200 | Get all info about a Hypixel player using his username or his player UUID.
201 |
202 | Parameters
203 | ----------
204 | name: Optional[str]
205 | The Optional string value for the Username. Defaults to None.
206 | uuid: Optional[str]
207 | The Optional string value to the UUID. Defaults to None.
208 |
209 | Returns
210 | -------
211 | Player
212 | The player object with all the info obtained from the API.
213 | """
214 | uuid = self._filter_name_uuid(name, uuid)
215 | json = self._fetch(self.url["player"], {"uuid": uuid})
216 |
217 | if not json["player"]:
218 | raise PlayerNotFoundError("Null is returned", name)
219 |
220 | return Player(json["player"])
221 |
222 | def get_friends(
223 | self, name: Optional[str] = None, uuid: Optional[str] = None
224 | ) -> Friends:
225 | """
226 | Get the friends, and all their info of specified Hypixel player.
227 |
228 | Parameters
229 | ----------
230 | name: Optional[str]
231 | The Optional string value for the Username of a hypixel player. Defaults to None.
232 | uuid: Optional[str]
233 | The UUID of a Certain Hypixel Player. Defaults to None.
234 |
235 | Returns
236 | -------
237 | Friends
238 | The Friend object with all info from the API.
239 | """
240 | uuid = self._filter_name_uuid(name, uuid)
241 | json = self._fetch(self.url["friends"], {"uuid": uuid})
242 |
243 | return Friends(json["records"])
244 |
245 | def get_watchdog_info(self) -> Watchdog:
246 | """
247 | Get all the stats about the Watchdog (Punishment stats) for the last few days/
248 |
249 | Returns
250 | -------
251 | Watchdog
252 | The Watchdog object with all the info.
253 | """
254 | json = self._fetch(self.url["watchdog"])
255 |
256 | return Watchdog(json)
257 |
258 | def get_guild(
259 | self,
260 | name: Optional[str] = None,
261 | uuid: Optional[str] = None,
262 | player_uuid: Optional[str] = None,
263 | ) -> Guild:
264 | """
265 | Get info about a specific Hypixel guild using the Name, or the Guild's UUID.
266 |
267 | Parameters
268 | ----------
269 | name: Optional[str]
270 | The Name of the Guild. Defaults to None.
271 | uuid: Optional[str]
272 | The ID Of the guild. Defaults to None.
273 | player_uuid: Optional[str]
274 | The UUID of the player to get guild using. Defaults to None.
275 |
276 | Returns
277 | -------
278 | Guild
279 | The Guild object with the info fetched from the API.
280 | """
281 | if uuid:
282 | json = self._fetch(self.url["guild"], {"id": uuid})
283 | elif name:
284 | json = self._fetch(self.url["guild"], {"name": name})
285 | elif player_uuid:
286 | json = self._fetch(self.url["guild"], {"player": player_uuid})
287 | else:
288 | raise InvalidArgumentError(
289 | "Named argument for guild's name or UUID not found."
290 | )
291 |
292 | if not json["guild"]:
293 | raise GuildNotFoundError("Value returned is null")
294 |
295 | return Guild(json["guild"])
296 |
297 | def get_games_info(self) -> Games:
298 | """
299 | Get the list of all Hypixel games, and their info.
300 |
301 | Returns
302 | -------
303 | Games
304 | The Games object with all the info.
305 | """
306 | json = self._fetch(self.url["game_info"])
307 |
308 | return Games(json["games"], json["playerCount"])
309 |
310 | def get_leaderboards(self) -> Leaderboard:
311 | """
312 | Get the leaderboard for the Hypixel games with their info.
313 |
314 | Returns
315 | -------
316 | Leaderboard
317 | The Leaderboard object with all info.
318 | """
319 | json = self._fetch(self.url["leaderboards"])
320 |
321 | return Leaderboard(json["leaderboards"])
322 |
323 | def find_guild(
324 | self, guild_name: Optional[str] = None, player_uuid: Optional[str] = None
325 | ) -> FindGuild:
326 | """
327 | Find a guild using the Guild's name or a Player's UUID.
328 |
329 | Parameters
330 | ----------
331 | guild_name: Optional[str]
332 | The name of the Guild. Defaults to None.
333 | player_uuid: Optional[str]
334 | The UUID of the Player to find his guild. Defaults to None.
335 |
336 | Returns
337 | -------
338 | FindGuild
339 | The ID of the guild being find.
340 | """
341 | if guild_name:
342 | json = self._fetch(self.url["find_guild"], {"byName": guild_name})
343 | elif player_uuid:
344 | json = self._fetch(self.url["find_guild"], {"byUuid": player_uuid})
345 | else:
346 | raise InvalidArgumentError(
347 | "Named argument for guild's name or UUID not found."
348 | )
349 |
350 | return FindGuild(json)
351 |
352 | def get_player_status(
353 | self, name: Optional[str] = None, uuid: Optional[str] = None
354 | ) -> PlayerStatus:
355 | """
356 | Get the status about a Player using his username or UUID.
357 |
358 | Parameters
359 | ----------
360 | name: Optional[str]
361 | The Optional string value for the Username. Defaults to None.
362 | uuid: Optional[str]
363 | The Optional string Value to the UUID. Defaults to None.
364 |
365 | Returns
366 | -------
367 | PlayerStatus
368 | The Player status object consisting of all info from the API.
369 | """
370 | uuid = self._filter_name_uuid(name, uuid)
371 | json = self._fetch(self.url["status"], {"uuid": uuid})
372 |
373 | return PlayerStatus(json)
374 |
375 | def get_player_recent_games(
376 | self, name: Optional[str] = None, uuid: Optional[str] = None
377 | ) -> RecentGames:
378 | """
379 | Get the recent games played by a Hypixel player using his Username or UUID.
380 |
381 | Parameters
382 | ----------
383 | name: Optional[str]
384 | The Optional string value for the Username. Defaults to None.
385 | uuid: Optional[str]
386 | The Optional string Value to the UUID. Defaults to None.
387 |
388 | Returns
389 | -------
390 | RecentGames
391 | The recent games for the respective player specified.
392 | """
393 | uuid = self._filter_name_uuid(name, uuid)
394 | json = self._fetch(self.url["recent_games"], {"uuid": uuid})
395 |
396 | return RecentGames(json)
397 |
398 | def get_skyblock_news(self) -> SkyblockNews:
399 | json = self._fetch(self.url["skyblock_news"])
400 |
401 | return SkyblockNews(json)
402 |
403 | def get_skyblock_profile(
404 | self, name: Optional[str] = None, uuid: Optional[str] = None
405 | ) -> SkyblockProfile:
406 | """
407 | Get the skyblock information and profile about a specific user as passed in the requirements.
408 |
409 | Parameters
410 | ----------
411 | name: Optional[str]
412 | The player's name in Hypixel
413 | uuid: Optional[str]
414 | The player's global UUID
415 |
416 | Returns
417 | -------
418 | SkyblockProfile
419 | The skyblock profile model for the specified user.
420 | """
421 | uuid = self._filter_name_uuid(name, uuid)
422 | json = self._fetch(self.url["skyblock_profile"], {"profile": uuid})
423 |
424 | if not json["profile"]:
425 | raise PlayerNotFoundError("The skyblock player does not exist!", uuid)
426 |
427 | return SkyblockProfile(json)
428 |
429 | def get_skyblock_user_auctions(
430 | self, name: Optional[str] = None, uuid: Optional[str] = None
431 | ) -> SkyblockUserAuction:
432 | """
433 | Get the skyblock auction info about a specific user.
434 |
435 | Parameters
436 | ----------
437 | name: Optional[str]
438 | The player's name in Hypixel
439 | uuid: Optional[str]
440 | The player's global UUID
441 |
442 | Returns
443 | -------
444 | SkyblockUserAuction
445 | The skyblock auction model for the user.
446 | """
447 | uuid = self._filter_name_uuid(name, uuid)
448 | json = self._fetch(self.url["skyblock_auctions"], {"profile": uuid})
449 |
450 | if not json["auctions"]:
451 | raise PlayerNotFoundError("The skyblock player does not exist!", uuid)
452 |
453 | return SkyblockUserAuction(json)
454 |
455 | def get_skyblock_active_auctions(self, page: int = 0) -> SkyblockActiveAuction:
456 | """
457 | Get the list of active auctions in skyblock and use the data.
458 |
459 | Parameters
460 | ----------
461 | page: int
462 | The skyblock auction page to lookup.
463 |
464 | Returns
465 | -------
466 | SkyblockActiveAuction
467 | The active auction model.
468 | """
469 | json = self._fetch(self.url["skyblock_active_auctions"], {"page": page})
470 | return SkyblockActiveAuction(json)
471 |
472 | def get_skyblock_bazaar(self) -> SkyblockBazaar:
473 | """
474 | Get the skyblock bazaar items
475 |
476 | Returns
477 | -------
478 | SkyblockBazaar
479 | The bazaar model object representing each produc
480 | """
481 | json = self._fetch(self.url["skyblock_bazaar"])
482 | return SkyblockBazaar(json)
483 |
484 | def get_resources_achievements(self) -> dict:
485 | data = self._fetch(self.url["achievements"], api_key=False)
486 | return data["achievements"]
487 |
488 | def get_resources_challenges(self) -> dict:
489 | data = self._fetch(self.url["challenges"], api_key=False)
490 | return data["challenges"]
491 |
492 | def get_resources_quests(self) -> dict:
493 | data = self._fetch(self.url["quests"], api_key=False)
494 | return data["quests"]
495 |
496 | def get_resources_guild_achievements(self) -> dict:
497 | data = self._fetch(self.url["guild_achievements"], api_key=False)
498 | return {"one_time": data["one_time"], "tiered": data["tiered"]}
499 |
500 | def get_skyblock_skills(self) -> dict:
501 | data = self._fetch(self.url["skyblock_skills"], api_key=False)
502 | return {
503 | "skills": data["skills"],
504 | "collections": data["collections"],
505 | }
506 |
507 | def get_skyblock_collections(self) -> dict:
508 | data = self._fetch(self.url["skyblock_collections"], api_key=False)
509 | return data["collections"]
510 |
--------------------------------------------------------------------------------
/hypixelio/lib/converters.py:
--------------------------------------------------------------------------------
1 | __all__ = ("Converters",)
2 |
3 | from typing import Any, Dict, Union, cast
4 |
5 | import requests
6 |
7 | from ..constants import MOJANG_API, TIMEOUT
8 | from ..endpoints import API_PATH
9 | from ..exceptions import MojangAPIError, PlayerNotFoundError
10 |
11 |
12 | class Converters:
13 | url = API_PATH["MOJANG"]
14 |
15 | @classmethod
16 | def _fetch(cls, url: str) -> Union[dict, list]:
17 | """
18 | The internal function for fetching info from the Mojang API.
19 |
20 | Parameters
21 | ----------
22 | url: str
23 | The Mojang URL, whose JSON is supposed to be fetched.
24 |
25 | Returns
26 | -------
27 | Union[dict, list]
28 | The JSON response from the Mojang API.
29 | """
30 | with requests.Session() as session:
31 | with session.get(MOJANG_API + url, timeout=TIMEOUT) as response:
32 | if response.status_code == 204:
33 | raise PlayerNotFoundError(
34 | "Error code 204 returned during conversion to UUID", None
35 | )
36 |
37 | if response.status_code == 400:
38 | raise PlayerNotFoundError("Badly formed UUID error", None)
39 |
40 | try:
41 | json = response.json()
42 | except Exception:
43 | raise MojangAPIError()
44 | else:
45 | if "error" in json:
46 | raise MojangAPIError(f"An error occurred! {json['errorMessage']}")
47 |
48 | return json
49 |
50 | @classmethod
51 | def username_to_uuid(cls, username: str) -> str:
52 | """
53 | This is a method, to convert username in minecraft, for its respective UUID.
54 |
55 | Parameters
56 | ----------
57 | username: str
58 | This is the minecraft user, which is passed to this function for the UUID Conversion.
59 |
60 | Returns
61 | -------
62 | str
63 | returns the converted UUID for the respective username.
64 | """
65 | json = cast(
66 | Dict[str, Any],
67 | Converters._fetch(Converters.url["username_to_uuid"].format(username)),
68 | )
69 | print(json)
70 |
71 | return json["id"]
72 |
73 | @classmethod
74 | def uuid_to_username(cls, uuid: str) -> str:
75 | """
76 | Method to convert the UUID for your profile to the username for your Minecraft accoun
77 |
78 | Parameters
79 | ----------
80 | uuid: str
81 | This is the minecraft UUID, which is passed to this function for the UUID to username Conversion.
82 |
83 | Returns
84 | -------
85 | str
86 | The username for the respective minecraft UUID is returned.
87 | """
88 | json = Converters._fetch(Converters.url["uuid_to_username"].format(uuid))
89 |
90 | return json[-1]["name"]
91 |
--------------------------------------------------------------------------------
/hypixelio/lib/utils.py:
--------------------------------------------------------------------------------
1 | __all__ = ("Utils",)
2 |
3 |
4 | import typing as t
5 |
6 | import requests
7 | from requests.models import Response
8 |
9 | from ..constants import TIMEOUT
10 | from ..endpoints import API_PATH
11 | from ..exceptions import CrafatarAPIError, InvalidArgumentError
12 | from ..lib.converters import Converters
13 |
14 |
15 | class Utils:
16 | mojang_url = API_PATH["MOJANG"]
17 | url = API_PATH["CRAFATAR"]
18 |
19 | @classmethod
20 | def _crafatar_fetch(cls, url: str) -> Response:
21 | """
22 | Method to fetch the JSON from the Crafatar API.
23 |
24 | Parameters
25 | ----------
26 | url: str
27 | The Crafatar URL, whose JSON is supposed to be fetched.
28 |
29 | Returns
30 | -------
31 | Response
32 | The JSON response from the Crafatar API.
33 | """
34 | with requests.get(f"https://crafatar.com/{url}", timeout=TIMEOUT) as response:
35 | if response.status_code == 422:
36 | raise InvalidArgumentError(
37 | "Invalid URL passed. Either user does not exist, or URL is malformed."
38 | )
39 |
40 | try:
41 | return response
42 | except Exception:
43 | raise CrafatarAPIError()
44 |
45 | @staticmethod
46 | def _filter_name_uuid(
47 | name: t.Optional[str] = None, uuid: t.Optional[str] = None
48 | ) -> str:
49 | if name is None and uuid is None:
50 | raise InvalidArgumentError(
51 | "Named argument for player's either username or UUID not found."
52 | )
53 |
54 | if name:
55 | uuid = Converters.username_to_uuid(name)
56 |
57 | return uuid # type: ignore
58 |
59 | @classmethod
60 | def _form_crafatar_url(cls, route: str) -> str:
61 | """
62 | This function forms the crafatar API URL for fetching USER skins.
63 |
64 | Parameters
65 | ----------
66 | route: str
67 | The URL path to visit.
68 |
69 | Returns
70 | -------
71 | str
72 | The well formed API URL for fetching.
73 | """
74 | return f"https://crafatar.com{route}"
75 |
76 | @classmethod
77 | def get_name_history(
78 | cls,
79 | name: t.Optional[str] = None,
80 | uuid: t.Optional[str] = None,
81 | changed_at: bool = False,
82 | ) -> t.Union[list, dict]:
83 | """
84 | Get the name history with records of a player.
85 |
86 | Parameters
87 | ----------
88 | name: t.Optional[str]
89 | The username of the player. Defaults to None.
90 | uuid: t.Optional[str]
91 | The UUID of the player. Defaults to None.
92 | changed_at: bool
93 | Toggle to true, if you need when the player changed name. Defaults to False.
94 |
95 | Returns
96 | -------
97 | t.Union[list, dict]
98 | The list or dictionary with the name history and records.
99 | """
100 | uuid = cls._filter_name_uuid(name, uuid)
101 | json = Converters._fetch(Utils.mojang_url["name_history"].format(uuid))
102 |
103 | # Return JSON if time is specified.
104 | if changed_at:
105 | return json
106 |
107 | # Return all usernames.
108 | usernames = []
109 | for data in json:
110 | usernames.append(data["name"])
111 |
112 | return usernames
113 |
114 | @classmethod
115 | def get_avatar(
116 | cls, name: t.Optional[str] = None, uuid: t.Optional[str] = None
117 | ) -> str:
118 | """
119 | Get the avatar of the specified player.
120 |
121 | Parameters
122 | ----------
123 | name: t.Optional[str]
124 | The username of the player. Defaults to None.
125 | uuid: t.Optional[str]
126 | The UUID of the player. Defaults to None.
127 |
128 | Returns
129 | -------
130 | str
131 | The URL containing the image of the avatar.
132 | """
133 | uuid = cls._filter_name_uuid(name, uuid)
134 | Utils._crafatar_fetch(Utils.url["avatar"].format(uuid))
135 |
136 | return Utils._form_crafatar_url(Utils.url["avatar"].format(uuid))
137 |
138 | @classmethod
139 | def get_head(
140 | cls, name: t.Optional[str] = None, uuid: t.Optional[str] = None
141 | ) -> str:
142 | """
143 | Get the head skin of the specified player.
144 |
145 | Parameters
146 | ----------
147 | name: t.Optional[str]
148 | The username of the player. Defaults to None.
149 | uuid: t.Optional[str]
150 | The UUID of the player. Defaults to None.
151 |
152 | Returns
153 | -------
154 | str
155 | The URL containing the image of the head.
156 | """
157 | uuid = cls._filter_name_uuid(name, uuid)
158 | Utils._crafatar_fetch(Utils.url["head"].format(uuid))
159 |
160 | return Utils._form_crafatar_url(Utils.url["head"].format(uuid))
161 |
162 | @classmethod
163 | def get_body(
164 | cls, name: t.Optional[str] = None, uuid: t.Optional[str] = None
165 | ) -> str:
166 | """
167 | Get the whole body's skin of the specified player
168 |
169 | Parameters
170 | ----------
171 | name: t.Optional[str]
172 | The username of the player. Defaults to None.
173 | uuid: t.Optional[str]
174 | The UUID of the player. Defaults to None.
175 |
176 | Returns
177 | -------
178 | str
179 | The URL containing the image of the whole body.
180 | """
181 | uuid = cls._filter_name_uuid(name, uuid)
182 | Utils._crafatar_fetch(Utils.url["body"].format(uuid))
183 |
184 | return Utils._form_crafatar_url(Utils.url["body"].format(uuid))
185 |
--------------------------------------------------------------------------------
/hypixelio/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janaSunrise/HypixelIO/388ebcc145e754639daafb0811d709c87b2ef3e6/hypixelio/models/__init__.py
--------------------------------------------------------------------------------
/hypixelio/models/boosters.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Dict, Iterator, List
2 |
3 | from hypixelio.utils import unix_time_to_datetime
4 |
5 |
6 | class BoosterInfo:
7 | def __init__(self, info: Dict[str, Any]) -> None:
8 | """
9 | Parameters
10 | ----------
11 | info: dict
12 | This contains the JSON Response from the API for the Info about a specific booster.
13 | """
14 | self.id = info["_id"]
15 | self.purchaser_uuid = info["purchaserUuid"]
16 |
17 | self.amount = info["amount"]
18 | self.original_length = info["originalLength"]
19 | self.length = info["length"]
20 |
21 | self.game_type_code = info["gameType"]
22 | self.date_activated = unix_time_to_datetime(info["dateActivated"])
23 |
24 | self.stacked = "stacked" in info
25 |
26 | def __eq__(self, other: "BoosterInfo") -> bool:
27 | return self.id == other.id and self.purchaser_uuid == other.purchaser_uuid
28 |
29 | def __hash__(self) -> int:
30 | return hash((self.id, self.purchaser_uuid))
31 |
32 | def __str__(self) -> str:
33 | return self.purchaser_uuid
34 |
35 | def __repr__(self) -> str:
36 | return f'<{self.__class__.__name__} id="{self.id}" purchaser="{self.purchaser_uuid}" stacked={self.stacked}>'
37 |
38 |
39 | class Boosters:
40 | def __init__(self, boosters: List[Dict[str, Any]], json: Dict[str, Any]) -> None:
41 | """
42 | Parameters
43 | ----------
44 | boosters: list
45 | The list of the Coin Boosters in the Hypixel server.
46 | """
47 | # All the boosters
48 | self.boosters = [BoosterInfo(booster) for booster in boosters]
49 |
50 | # State of the boosters
51 | self.state = json["boosterState"]
52 |
53 | # Decrementing
54 | self.decrementing = self.state["decrementing"]
55 |
56 | def __len__(self) -> int:
57 | return len(self.boosters)
58 |
59 | def __getitem__(self, key: int) -> BoosterInfo:
60 | return self.boosters[key]
61 |
62 | def __setitem__(self, key: int, value: BoosterInfo) -> None:
63 | self.boosters[key] = value
64 |
65 | def __iter__(self) -> Iterator:
66 | return iter(self.boosters)
67 |
68 | def __eq__(self, other: "Boosters") -> bool:
69 | if len(self.boosters) != len(other.boosters):
70 | return False
71 |
72 | for booster in list(zip(self.boosters, other.boosters)):
73 | if booster[0].id != booster[1].id:
74 | return False
75 |
76 | return True
77 |
78 | def __str__(self) -> str:
79 | return str(len(self.boosters))
80 |
81 | def __repr__(self) -> str:
82 | return f"<{self.__class__.__name__} booster_count={len(self.boosters)}>"
83 |
--------------------------------------------------------------------------------
/hypixelio/models/find_guild.py:
--------------------------------------------------------------------------------
1 | class FindGuild:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The JSON data received from the Hypixel API.
8 | """
9 | self.id = data["guild"]
10 |
11 | def __str__(self) -> str:
12 | return self.id
13 |
14 | def __repr__(self) -> str:
15 | return f'<{self.__class__.__name__} id="{self.id}">'
16 |
17 | def __hash__(self) -> int:
18 | return hash(self.id)
19 |
20 | def __eq__(self, other: "FindGuild") -> bool:
21 | return self.id == other.id
22 |
--------------------------------------------------------------------------------
/hypixelio/models/friends.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 |
3 | from hypixelio.utils import unix_time_to_datetime
4 |
5 |
6 | class FriendData:
7 | def __init__(self, friend: dict) -> None:
8 | """
9 | Parameters
10 | ----------
11 | friend: dict
12 | This contains the JSON Response for the Friend's list element API Request.
13 | """
14 | self.request_id = friend["_id"]
15 |
16 | self.sender_id = friend["uuidSender"]
17 | self.receiver_id = friend["uuidReceiver"]
18 |
19 | self.sent_at = unix_time_to_datetime(friend["started"])
20 |
21 | def __repr__(self) -> str:
22 | return (
23 | f'<{self.__class__.__name__} id="{self.request_id}" sent="{self.sent_at}">'
24 | )
25 |
26 | def __str__(self) -> str:
27 | return self.request_id
28 |
29 | def __hash__(self) -> int:
30 | return hash((self.sender_id, self.receiver_id))
31 |
32 | def __eq__(self, other: "FriendData") -> bool:
33 | return (
34 | self.receiver_id == other.receiver_id and self.sender_id == other.sender_id
35 | )
36 |
37 |
38 | class Friends:
39 | def __init__(self, friends: list) -> None:
40 | """
41 | Parameters
42 | ----------
43 | friends: list
44 | This contains the Returned JSON Response List for the List of the friends of an user.
45 | """
46 | self.friends = [FriendData(friend) for friend in friends]
47 |
48 | def __len__(self) -> int:
49 | return len(self.friends)
50 |
51 | def __getitem__(self, key: int) -> FriendData:
52 | return self.friends[key]
53 |
54 | def __setitem__(self, key: int, value: FriendData) -> None:
55 | self.friends[key] = value
56 |
57 | def __iter__(self) -> t.Iterator:
58 | return iter(self.friends)
59 |
60 | def __repr__(self) -> str:
61 | return f"<{self.__class__.__name__} friends_count={len(self.friends)}>"
62 |
63 | def __hash__(self) -> int:
64 | return hash(tuple(self.friends))
65 |
66 | def __eq__(self, other: "Friends") -> bool:
67 | if len(self.friends) != len(other.friends):
68 | return False
69 |
70 | for friend in list(zip(self.friends, other.friends)):
71 | if friend[0].request_id != friend[1].request_id:
72 | return False
73 | return True
74 |
--------------------------------------------------------------------------------
/hypixelio/models/games.py:
--------------------------------------------------------------------------------
1 | class GameCount:
2 | def __init__(self, game: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | game: dict
7 | The Game JSON data response received from the Hypixel API.
8 | """
9 | self.players = game["players"]
10 | self.modes = game.get("modes")
11 |
12 | def __repr__(self) -> str:
13 | return (
14 | f'<{self.__class__.__name__} players="{self.players}" modes={self.modes}>'
15 | )
16 |
17 |
18 | class Games:
19 | def __init__(self, games: dict, player_count: int) -> None:
20 | """
21 | Parameters
22 | ----------
23 | games: dict
24 | The Games JSON list data response received from the Hypixel API.
25 | player_count: int
26 | The player count in the whole Hypixel Server.
27 | """
28 | self.player_count = player_count
29 |
30 | self.main_lobby = GameCount(games["MAIN_LOBBY"])
31 | self.tournament_lobby = GameCount(games["TOURNAMENT_LOBBY"])
32 |
33 | self.uhc = GameCount(games["UHC"])
34 | self.super_smash = GameCount(games["SUPER_SMASH"])
35 | self.legacy = GameCount(games["LEGACY"])
36 | self.build_battle = GameCount(games["BUILD_BATTLE"])
37 | self.battleground = GameCount(games["BATTLEGROUND"])
38 | self.walls3 = GameCount(games["WALLS3"])
39 | self.housing = GameCount(games["HOUSING"])
40 | self.speed_uhc = GameCount(games["SPEED_UHC"])
41 | self.duels = GameCount(games["DUELS"])
42 | self.replay = GameCount(games["REPLAY"])
43 | self.survival_games = GameCount(games["SURVIVAL_GAMES"])
44 | self.prototype = GameCount(games["PROTOTYPE"])
45 | self.murder_mystery = GameCount(games["MURDER_MYSTERY"])
46 | self.mcgo = GameCount(games["MCGO"])
47 | self.bedwars = GameCount(games["BEDWARS"])
48 | self.skyblock = GameCount(games["SKYBLOCK"])
49 | self.arcade = GameCount(games["ARCADE"])
50 | self.pit = GameCount(games["PIT"])
51 | self.tnt_games = GameCount(games["TNTGAMES"])
52 | self.skywars = GameCount(games["SKYWARS"])
53 | self.limbo = GameCount(games["LIMBO"])
54 |
55 | self.idle = GameCount(games["IDLE"])
56 | self.queue = GameCount(games["QUEUE"])
57 |
58 | def __repr__(self) -> str:
59 | return (
60 | f"<{self.__class__.__name__} lobby={self.main_lobby} idle={self.idle} queue={self.queue} "
61 | f"players={self.player_count}>"
62 | )
63 |
--------------------------------------------------------------------------------
/hypixelio/models/guild.py:
--------------------------------------------------------------------------------
1 | from hypixelio.utils import unix_time_to_datetime
2 |
3 |
4 | class Guild:
5 | def __init__(self, data: dict) -> None:
6 | """
7 | Parameters
8 | ----------
9 | data: dict
10 | The JSON data received from the Hypixel API.
11 | """
12 | self.hypixel_id = data["_id"]
13 | self.name = data["name"]
14 |
15 | # Coins the guild holds
16 | self.coins = data["coins"]
17 |
18 | # When the guild was created
19 | self.created = unix_time_to_datetime(data["created"])
20 |
21 | # Member count of the guild
22 | self.members = data["members"]
23 |
24 | # Old ranking data, and experience
25 | self.legacy_ranking = data["legacyRanking"]
26 | self.experience = data["exp"]
27 |
28 | self.achievements = data["achievements"]
29 | self.experience_by_game = data["guildExpByGameType"]
30 |
31 | def __str__(self) -> str:
32 | return self.name
33 |
34 | def __repr__(self) -> str:
35 | return f'<{self.__class__.__name__} id="{self.hypixel_id}" name="{self.name}" experience="{self.experience}">'
36 |
37 | def __hash__(self) -> int:
38 | return hash(self.hypixel_id)
39 |
40 | def __eq__(self, other: "Guild") -> bool:
41 | return self.hypixel_id == other.name
42 |
--------------------------------------------------------------------------------
/hypixelio/models/key.py:
--------------------------------------------------------------------------------
1 | class Key:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The JSON data received from the Hypixel API.
8 | """
9 | self.key = data["key"]
10 | self.owner_uuid = data["owner"]
11 |
12 | # Queries info
13 | self.query_limit = data["limit"]
14 | self.queries_in_past_minute = data["queriesInPastMin"]
15 | self.total_queries = data["totalQueries"]
16 |
17 | def __str__(self) -> str:
18 | return self.key
19 |
20 | def __repr__(self) -> str:
21 | return f'<{self.__class__.__name__} key="{self.key}" owner="{self.owner_uuid}">'
22 |
23 | def __hash__(self) -> int:
24 | return hash(self.key)
25 |
26 | def __eq__(self, other: "Key") -> bool:
27 | return self.key == other.key
28 |
--------------------------------------------------------------------------------
/hypixelio/models/leaderboard.py:
--------------------------------------------------------------------------------
1 | class LeaderboardData:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The Leaderboard JSON data per game response received from the Hypixel API.
8 | """
9 | self.path = data["path"]
10 | self.prefix = data["prefix"]
11 |
12 | self.title = data["title"]
13 |
14 | self.location = data["location"]
15 | self.count = data["count"]
16 |
17 | self.leaders_uuid = data["leaders"]
18 |
19 | def __repr__(self) -> str:
20 | return f'<{self.__class__.__name__} title="{self.title}" location="{self.location}">'
21 |
22 | def __str__(self) -> str:
23 | return self.title
24 |
25 |
26 | class Leaderboard:
27 | def __init__(self, board: dict) -> None:
28 | """
29 | Parameters
30 | ----------
31 | board: dict
32 | The Leaderboard JSON data response received from the Hypixel API.
33 | """
34 | self.arena = [LeaderboardData(arena) for arena in board["ARENA"]]
35 | self.mcgo = [LeaderboardData(arena) for arena in board["MCGO"]]
36 | self.battleground = [LeaderboardData(arena) for arena in board["BATTLEGROUND"]]
37 | self.survival_games = [
38 | LeaderboardData(arena) for arena in board["SURVIVAL_GAMES"]
39 | ]
40 | self.uhc = [LeaderboardData(arena) for arena in board["UHC"]]
41 | self.walls = [LeaderboardData(arena) for arena in board["WALLS"]]
42 | self.paintball = [LeaderboardData(arena) for arena in board["PAINTBALL"]]
43 | self.skywars = [LeaderboardData(arena) for arena in board["SKYWARS"]]
44 | self.murder_mystery = [
45 | LeaderboardData(arena) for arena in board["MURDER_MYSTERY"]
46 | ]
47 | self.super_smash = [LeaderboardData(arena) for arena in board["SUPER_SMASH"]]
48 | self.duels = [LeaderboardData(arena) for arena in board["DUELS"]]
49 | self.speed_uhc = [LeaderboardData(arena) for arena in board["SPEED_UHC"]]
50 | self.tnt_games = [LeaderboardData(arena) for arena in board["TNTGAMES"]]
51 | self.bedwars = [LeaderboardData(arena) for arena in board["BEDWARS"]]
52 | self.gingerbread = [LeaderboardData(arena) for arena in board["GINGERBREAD"]]
53 | self.build_battle = [LeaderboardData(arena) for arena in board["BUILD_BATTLE"]]
54 | self.arcade = [LeaderboardData(arena) for arena in board["ARCADE"]]
55 | self.skyclash = [LeaderboardData(arena) for arena in board["SKYCLASH"]]
56 | self.quakecraft = [LeaderboardData(arena) for arena in board["QUAKECRAFT"]]
57 | self.true_combat = [LeaderboardData(arena) for arena in board["TRUE_COMBAT"]]
58 | self.walls3 = [LeaderboardData(arena) for arena in board["WALLS3"]]
59 | self.vampirez = [LeaderboardData(arena) for arena in board["VAMPIREZ"]]
60 |
--------------------------------------------------------------------------------
/hypixelio/models/player.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 | from dataclasses import dataclass
3 |
4 | from hypixelio.utils import get_rank, unix_time_to_datetime
5 |
6 |
7 | @dataclass
8 | class PlayerSocialMedia:
9 | youtube: t.Optional[str]
10 | twitter: t.Optional[str]
11 | instagram: t.Optional[str]
12 | twitch: t.Optional[str]
13 | discord: t.Optional[str]
14 | hypixel_forums: t.Optional[str]
15 |
16 | # Convert JSON data to a PlayerSocialMedia object
17 | @classmethod
18 | def from_json(cls, data: t.Optional[dict]) -> t.Optional["PlayerSocialMedia"]:
19 | if not data:
20 | return None
21 |
22 | return cls(
23 | youtube=data.get("YOUTUBE"),
24 | twitter=data.get("TWITTER"),
25 | instagram=data.get("INSTAGRAM"),
26 | twitch=data.get("TWITCH"),
27 | discord=data.get("DISCORD"),
28 | hypixel_forums=data.get("HYPIXEL_FORUMS"),
29 | )
30 |
31 |
32 | class Player:
33 | def __init__(self, data: dict) -> None:
34 | """
35 | Parameters
36 | ----------
37 | data: dict
38 | The JSON data received from the Hypixel API.
39 | """
40 | self.hypixel_id = data["_id"]
41 | self.uuid = data["uuid"]
42 |
43 | self.name = data["displayname"]
44 | self.known_aliases = data["knownAliases"]
45 |
46 | self.first_login = unix_time_to_datetime(data["firstLogin"])
47 | self.last_login = unix_time_to_datetime(data["lastLogin"])
48 | self.last_logout = unix_time_to_datetime(data["lastLogout"])
49 |
50 | self.one_time_achievements = data["achievementsOneTime"]
51 | self.achievement_points = data["achievementPoints"]
52 | self.achievements = data["achievements"]
53 |
54 | self.experience = data["networkExp"]
55 | self.level = self._calc_player_level(self.experience)
56 |
57 | self.karma = data["karma"]
58 | self.mc_version_rp = data.get("mcVersionRp")
59 |
60 | self.challenges = data["challenges"]["all_time"]
61 | self.most_recent_game = data["mostRecentGameType"]
62 |
63 | self.total_rewards = data.get("totalRewards")
64 | self.total_daily_rewards = data.get("totalDailyRewards")
65 | self.reward_streak = data.get("rewardStreak")
66 | self.reward_score = data.get("rewardScore")
67 | self.reward_high_score = data.get("rewardHighScore")
68 |
69 | self.pet_stats = data.get("petStats")
70 | self.current_gadget = data.get("currentGadget")
71 |
72 | self.social_media = PlayerSocialMedia.from_json(
73 | data.get("socialMedia", {}).get("links")
74 | )
75 |
76 | self.rank = self._get_rank(data)
77 |
78 | @staticmethod
79 | def _calc_player_level(xp: t.Union[float, int]) -> float:
80 | return 1 + (-8750.0 + (8750**2 + 5000 * xp) ** 0.5) / 2500
81 |
82 | @staticmethod
83 | def _get_rank(data: dict) -> t.Optional[str]:
84 | return get_rank(
85 | data.get("rank"),
86 | data.get("prefix"),
87 | data.get("monthlyPackageRank"),
88 | data.get("newPackageRank"),
89 | data.get("packageRank"),
90 | )
91 |
92 | def __str__(self) -> str:
93 | return self.name
94 |
95 | def __repr__(self) -> str:
96 | return f'<{self.__class__.__name__} id="{self.hypixel_id}" name="{self.name}" experience="{self.experience}">'
97 |
98 | def __hash__(self) -> int:
99 | return hash(self.uuid)
100 |
101 | def __eq__(self, other: "Player") -> bool:
102 | return self.uuid == other.uuid
103 |
--------------------------------------------------------------------------------
/hypixelio/models/player_status.py:
--------------------------------------------------------------------------------
1 | class PlayerStatus:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The JSON data received from the Hypixel API.
8 | """
9 | self.uuid = data["uuid"]
10 |
11 | self.online = data["session"]["online"]
12 | self.game_type = data["session"].get("gameType")
13 | self.mode = data["session"].get("mode")
14 | self.map = data["session"].get("map")
15 |
16 | def __str__(self) -> str:
17 | return self.uuid
18 |
19 | def __repr__(self) -> str:
20 | return f'<{self.__class__.__name__} uuid="{self.uuid}" online="{self.online}">'
21 |
22 | def __hash__(self) -> int:
23 | return hash(self.uuid)
24 |
25 | def __eq__(self, other: "PlayerStatus") -> bool:
26 | return self.uuid == other.uuid
27 |
--------------------------------------------------------------------------------
/hypixelio/models/recent_games.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 |
3 | from hypixelio.utils import unix_time_to_datetime
4 |
5 |
6 | class RecentGameInfo:
7 | def __init__(self, game: dict) -> None:
8 | """
9 | Parameters
10 | ----------
11 | game: dict
12 | The game's JSON data received from the Hypixel API.
13 | """
14 | self.date = unix_time_to_datetime(game["date"])
15 | self.end_datetime = unix_time_to_datetime(game["ended"])
16 |
17 | self.game_type = game["gameType"]
18 | self.mode = game.get("mode")
19 | self.map = game["map"]
20 |
21 | def __hash__(self) -> int:
22 | return hash((self.date, self.map))
23 |
24 | def __str__(self) -> str:
25 | return self.game_type
26 |
27 | def __repr__(self) -> str:
28 | return f'<{self.__class__.__name__} game_type="{self.game_type}" mode="{self.mode}" map="{self.map}">'
29 |
30 |
31 | class RecentGames:
32 | def __init__(self, data: dict) -> None:
33 | """
34 | Parameters
35 | ----------
36 | data: dict
37 | The JSON data received from the Hypixel API.
38 | """
39 | self.uuid = data["uuid"]
40 | self.games = [RecentGameInfo(game) for game in data["games"]]
41 |
42 | # Length of the list
43 | def __len__(self) -> int:
44 | return len(self.games)
45 |
46 | # Get items
47 | def __getitem__(self, key: int) -> RecentGameInfo:
48 | return self.games[key]
49 |
50 | def __setitem__(self, key: int, value: RecentGameInfo) -> None:
51 | self.games[key] = value
52 |
53 | # Looping
54 | def __iter__(self) -> t.Iterator:
55 | return iter(self.games)
56 |
57 | # Other dunder methods
58 | def __str__(self) -> str:
59 | return self.uuid
60 |
61 | def __repr__(self) -> str:
62 | return f'<{self.__class__.__name__} uuid="{self.uuid}">'
63 |
64 | def __hash__(self) -> int:
65 | return hash(self.uuid)
66 |
67 | def __eq__(self, other: "RecentGames") -> bool:
68 | return self.uuid == other.uuid
69 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/__init__.py:
--------------------------------------------------------------------------------
1 | from .active_auctions import SkyblockActiveAuction
2 | from .bazaar import SkyblockBazaar
3 | from .news import SkyblockNews
4 | from .profile import SkyblockProfile
5 | from .user_auction import SkyblockUserAuction
6 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/active_auctions/__init__.py:
--------------------------------------------------------------------------------
1 | from .active_auctions import SkyblockActiveAuction
2 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/active_auctions/active_auctions.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 |
3 | from hypixelio.models.skyblock.auction import SkyblockAuction
4 |
5 |
6 | class SkyblockActiveAuction:
7 | def __init__(self, data: dict) -> None:
8 | """
9 | Parameters
10 | ----------
11 | data: dict
12 | The data from the Hypixel API endpoint.
13 | """
14 | self.page_number = data["page"]
15 | self.total_pages = data["totalPages"]
16 | self.total_auctions = data["totalAuctions"]
17 |
18 | self.auctions = [SkyblockAuction(auction) for auction in data["auctions"]]
19 |
20 | def __len__(self) -> int:
21 | return len(self.auctions)
22 |
23 | def __getitem__(self, key: int) -> SkyblockAuction:
24 | return self.auctions[key]
25 |
26 | def __setitem__(self, key: int, value: SkyblockAuction) -> None:
27 | self.auctions[key] = value
28 |
29 | def __iter__(self) -> t.Iterator:
30 | return iter(self.auctions)
31 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/auction.py:
--------------------------------------------------------------------------------
1 | class SkyblockAuction:
2 | def __init__(self, auction_data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | auction_data: dict
7 | The auction JSON model to be parsed.
8 | """
9 | self.id = auction_data["_id"]
10 | self.uuid = auction_data["uuid"]
11 |
12 | self.auctioneer = auction_data["auctioneer"]
13 |
14 | self.item_name = auction_data["item_name"]
15 | self.item_lore = auction_data["item_lore"]
16 |
17 | self.category = auction_data["category"]
18 | self.tier = auction_data["tier"]
19 | self.starting_big = auction_data["starting_bid"]
20 |
21 | self.claimed_bidders = auction_data.get("claimed_bidders")
22 | self.bids = auction_data.get("bids")
23 | self.highest_bid = auction_data.get("highest_bid_amount")
24 |
25 | def __str__(self) -> str:
26 | return self.id
27 |
28 | def __repr__(self) -> str:
29 | return f'<{self.__class__.__name__} id="{self.id}" uuid="{self.uuid}">'
30 |
31 | def __hash__(self) -> int:
32 | return hash(self.id)
33 |
34 | def __eq__(self, other: "SkyblockAuction") -> bool:
35 | return self.id == other.id
36 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/bazaar/__init__.py:
--------------------------------------------------------------------------------
1 | from .skyblock_bazaar import SkyblockBazaar
2 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/bazaar/bazaar_item.py:
--------------------------------------------------------------------------------
1 | class SkyblockBazaarItem:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The product data from the Hypixel bazaar API endpoint.
8 | """
9 | self.product_id = data["productId"]
10 |
11 | self.sell_price = data["sellPrice"]
12 | self.sell_volume = data["sellVolume"]
13 | self.sell_moving_week = data["sellMovingWeek"]
14 | self.sell_orders = data["sellOrders"]
15 |
16 | self.buy_price = data["buyPrice"]
17 | self.buy_volume = data["buyVolume"]
18 | self.buy_moving_week = data["buyMovingWeek"]
19 | self.buy_orders = data["buyOrders"]
20 |
21 | def __str__(self) -> str:
22 | return self.product_id
23 |
24 | def __repr__(self) -> str:
25 | return (
26 | f'<{self.__class__.__name__} id="{self.product_id}" sell_price="{self.sell_price}" '
27 | f'buy_price="{self.buy_price}">'
28 | )
29 |
30 | def __hash__(self) -> int:
31 | return hash(self.product_id)
32 |
33 | def __eq__(self, other: "SkyblockBazaarItem") -> bool:
34 | return self.product_id == other.product_id
35 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/bazaar/skyblock_bazaar.py:
--------------------------------------------------------------------------------
1 | from .bazaar_item import SkyblockBazaarItem
2 |
3 |
4 | class SkyblockBazaar:
5 | def __init__(self, data: dict) -> None:
6 | """
7 | Parameters
8 | ----------
9 | data: dict
10 | The data from the Hypixel API endpoint.
11 | """
12 | self.last_updated = data["lastUpdated"]
13 | self.products = {}
14 |
15 | for key, value in data["products"].items():
16 | bazaar_item_object = SkyblockBazaarItem(value["quick_status"])
17 | self.products[key] = bazaar_item_object
18 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/news.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 |
3 |
4 | @dataclass
5 | class NewsItem:
6 | title: str
7 | text: str
8 | link: str
9 | item: dict
10 |
11 |
12 | class SkyblockNews:
13 | def __init__(self, data: dict) -> None:
14 | """
15 | Parameters
16 | ----------
17 | data: dict
18 | The JSON data to be parsed.
19 | """
20 | news = []
21 |
22 | for news_data in data["items"]:
23 | news.append(
24 | NewsItem(
25 | news_data["title"],
26 | news_data["text"],
27 | news_data["link"],
28 | news_data["item"],
29 | )
30 | )
31 |
32 | self.news = news
33 |
34 | def __repr__(self) -> str:
35 | return f"<{self.__class__.__qualname__} news_amount={len(self.news)}>"
36 |
37 | def __len__(self) -> int:
38 | return len(self.news)
39 |
40 | def __iter__(self):
41 | return iter(self.news)
42 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/profile/__init__.py:
--------------------------------------------------------------------------------
1 | from .profile import SkyblockProfile
2 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/profile/profile.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 |
3 | from .profile_member import SkyblockProfileMember
4 |
5 |
6 | class SkyblockProfile:
7 | def __init__(self, data: dict) -> None:
8 | """
9 | Parameters
10 | ----------
11 | data: dict
12 | The JSON data received from the Hypixel API.
13 | """
14 | self.__profile_json = data["profile"]
15 |
16 | self.profile_id = self.__profile_json["profile_id"]
17 | self.members = [
18 | SkyblockProfileMember(self.__profile_json["members"][member])
19 | for member in self.__profile_json["members"]
20 | ]
21 | self.community_upgrades = self.__profile_json["community_upgrades"]
22 |
23 | def __str__(self) -> str:
24 | return self.profile_id
25 |
26 | def __repr__(self) -> str:
27 | return f'<{self.__class__.__name__} id="{self.profile_id}" member_count="{len(self.members)}">'
28 |
29 | def __hash__(self) -> int:
30 | return hash(self.profile_id)
31 |
32 | def __eq__(self, other: "SkyblockProfile") -> bool:
33 | return self.profile_id == other.profile_id
34 |
35 | def __len__(self) -> int:
36 | return len(self.members)
37 |
38 | def __getitem__(self, key: int) -> SkyblockProfileMember:
39 | return self.members[key]
40 |
41 | def __setitem__(self, key: int, value: SkyblockProfileMember) -> None:
42 | self.members[key] = value
43 |
44 | def __iter__(self) -> t.Iterator:
45 | return iter(self.members)
46 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/profile/profile_member.py:
--------------------------------------------------------------------------------
1 | class SkyblockProfileMember:
2 | def __init__(self, member_data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The JSON data received from the Hypixel API.
8 | """
9 | self.coin_purse = member_data["coin_purse"]
10 | self.death_count = member_data["death_count"]
11 | self.fairy_souls_collected = member_data.get("fairy_souls_collected")
12 | self.fishing_treasure_count = member_data.get("fishing_treasure_caught")
13 |
14 | self.stats = member_data["stats"]
15 | self.objectives = member_data["objectives"]
16 |
17 | self.crafted_generators = member_data.get("crafted_generators")
18 | self.visited_zones = member_data.get("visited_zones")
19 | self.achievement_spawned_island_types = member_data.get(
20 | "achievement_spawned_island_types"
21 | )
22 |
23 | self.slayer_quest = member_data.get("slayer_quest")
24 | self.slayer_bosses = member_data.get("slayer_bosses")
25 |
26 | self.pets = member_data["pets"]
27 | self.griffin = member_data["griffin"]
28 |
29 | self.unlocked_collection_tiers = member_data.get("unlocked_coll_tiers")
30 |
31 | self.skills = {
32 | "alchemy": member_data.get("experience_skill_alchemy"),
33 | "farming": member_data.get("experience_skill_farming"),
34 | "taming": member_data.get("experience_skill_taming"),
35 | "enchanting": member_data.get("experience_skill_enchanting"),
36 | "fishing": member_data.get("experience_skill_fishing"),
37 | "foraging": member_data.get("experience_skill_foraging"),
38 | "carpentry": member_data.get("experience_skill_carpentry"),
39 | "runecrafting": member_data.get("experience_skill_runecrafting"),
40 | "combat": member_data.get("experience_skill_combat"),
41 | "mining": member_data.get("experience_skill_mining"),
42 | }
43 |
44 | self.collection = member_data.get("collection")
45 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/user_auction/__init__.py:
--------------------------------------------------------------------------------
1 | from .user_auction import SkyblockUserAuction
2 |
--------------------------------------------------------------------------------
/hypixelio/models/skyblock/user_auction/user_auction.py:
--------------------------------------------------------------------------------
1 | import typing as t
2 |
3 | from hypixelio.models.skyblock.auction import SkyblockAuction
4 |
5 |
6 | class SkyblockUserAuction:
7 | def __init__(self, data: dict) -> None:
8 | """
9 | Parameters
10 | ----------
11 | data: dict
12 | The data from the Hypixel API endpoint.
13 | """
14 | self.auctions = [SkyblockAuction(auction) for auction in data["auctions"]]
15 |
16 | def __len__(self) -> int:
17 | return len(self.auctions)
18 |
19 | def __getitem__(self, key: int) -> SkyblockAuction:
20 | return self.auctions[key]
21 |
22 | def __setitem__(self, key: int, value: SkyblockAuction) -> None:
23 | self.auctions[key] = value
24 |
25 | def __iter__(self) -> t.Iterator:
26 | return iter(self.auctions)
27 |
--------------------------------------------------------------------------------
/hypixelio/models/watchdog.py:
--------------------------------------------------------------------------------
1 | class Watchdog:
2 | def __init__(self, data: dict) -> None:
3 | """
4 | Parameters
5 | ----------
6 | data: dict
7 | The JSON data received from the Hypixel API.
8 | """
9 | self.last_minute_ban = data["watchdog_lastMinute"]
10 |
11 | self.staff_rolling_daily = data["staff_rollingDaily"]
12 | self.rolling_daily = data["watchdog_rollingDaily"]
13 |
14 | self.staff_total_bans = data["staff_total"]
15 | self.total_bans = data["watchdog_total"]
16 |
17 | def __str__(self) -> str:
18 | return self.last_minute_ban
19 |
20 | def __repr__(self) -> str:
21 | return f"<{self.__class__.__name__} last_minute_ban={self.last_minute_ban} rolling_daily={self.rolling_daily}>"
22 |
--------------------------------------------------------------------------------
/hypixelio/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janaSunrise/HypixelIO/388ebcc145e754639daafb0811d709c87b2ef3e6/hypixelio/py.typed
--------------------------------------------------------------------------------
/hypixelio/utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | import re
3 | from datetime import datetime
4 | from typing import Any, Dict, Optional, Union
5 |
6 | from .constants import RANKS, RANK_COLORS
7 |
8 |
9 | def form_url(main_url: str, url: str, data: Optional[Dict[str, Any]] = None) -> str:
10 | if not data:
11 | data = {}
12 |
13 | url = main_url + url if url.startswith("/") else url
14 | url += "?" + "&".join(
15 | [f"{dict_key}={dict_value}" for dict_key, dict_value in data.items()]
16 | )
17 |
18 | return url
19 |
20 |
21 | # Convert unix time to datetime
22 | def unix_time_to_datetime(unix_time: int) -> datetime:
23 | return datetime.fromtimestamp(float(unix_time) / 1000)
24 |
25 |
26 | # Other useful functions
27 | def get_ratio(
28 | positive_stat: Union[int, float], negative_stat: Union[int, float]
29 | ) -> Union[int, float]:
30 | try:
31 | ratio = positive_stat / negative_stat
32 | return round(ratio, 2)
33 | except ZeroDivisionError:
34 | return float("inf") if positive_stat > 0 else 0
35 |
36 |
37 | def get_ratio_next(ratio: Union[int, float]) -> Union[int, float]:
38 | if ratio == float("inf"):
39 | return ratio
40 | return math.trunc(ratio) + 1
41 |
42 |
43 | def get_level_percentage(level: float) -> Union[int, float]:
44 | return round((level - math.trunc(level)) * 100, 2)
45 |
46 |
47 | def get_network_level(experience: Union[int, float]) -> Union[int, float]:
48 | return math.trunc(get_network_level_exact(experience))
49 |
50 |
51 | def get_network_level_exact(experience: Union[int, float]) -> Union[int, float]:
52 | return (math.sqrt(experience + 15312.5) - 88.38834764831843) / 35.35533905932738
53 |
54 |
55 | def get_rank(
56 | rank: Optional[str] = None,
57 | prefix_raw: Optional[str] = None,
58 | monthly_package_rank: Optional[str] = None,
59 | new_package_rank: Optional[str] = None,
60 | package_rank: Optional[str] = None,
61 | ) -> Optional[str]:
62 | real_rank = None
63 |
64 | if prefix_raw:
65 | prefix = re.sub(r"§.", "", prefix_raw)[1:-1]
66 | real_rank = RANKS.get(prefix, prefix)
67 | elif rank and rank != "NORMAL" and not real_rank:
68 | real_rank = RANKS.get(rank, rank)
69 | elif new_package_rank and not real_rank:
70 | real_rank = RANKS.get(new_package_rank, new_package_rank)
71 | elif package_rank and not real_rank:
72 | real_rank = RANKS.get(package_rank, package_rank)
73 | elif (monthly_package_rank and monthly_package_rank != "NONE") and not real_rank:
74 | real_rank = RANKS.get(monthly_package_rank, monthly_package_rank)
75 |
76 | return real_rank
77 |
78 |
79 | def get_rank_color(rank: str) -> int:
80 | return RANK_COLORS[rank]
81 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=42", "wheel"]
3 | build-backend = "setuptools.build_meta"
4 |
--------------------------------------------------------------------------------
/pyrightconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": ["build", "dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import re
2 | from itertools import chain
3 | from pathlib import Path
4 |
5 | import setuptools
6 |
7 | # Constant variables
8 | BASE_DIR = Path(__file__).resolve().parent
9 |
10 | README = Path(BASE_DIR / "README.md").read_text()
11 |
12 | URL = "https://github.com/janaSunrise/HypixelIO"
13 |
14 | # Version locating and assignment
15 | VERSION = re.search(
16 | r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
17 | Path(BASE_DIR / "hypixelio/__init__.py").read_text(),
18 | re.MULTILINE,
19 | ).group( # type: ignore
20 | 1
21 | )
22 |
23 | # Version error
24 | if not VERSION:
25 | raise RuntimeError("VERSION is not set.")
26 |
27 | # Dependencies configuration
28 | extras_require = {"speedups": ["aiodns==3.0.0", "Brotli==1.0.9", "cchardet==2.1.7"]}
29 | extras_require["all"] = list(chain.from_iterable(extras_require.values()))
30 |
31 | # Main setup
32 | setuptools.setup(
33 | name="HypixelIO",
34 | version=VERSION,
35 | author="Sunrit Jana",
36 | author_email="warriordefenderz@gmail.com",
37 | description="A modern, efficient and better way of interacting with the Hypixel API!",
38 | long_description=README,
39 | long_description_content_type="text/markdown",
40 | license="MIT",
41 | url=URL,
42 | project_urls={"Documentation": URL, "Issue tracker": f"{URL}/issues"},
43 | packages=setuptools.find_packages(exclude=["tests", "tests.*", "tools", "tools.*"]),
44 | package_data={"hypixelio": ["py.typed"]},
45 | install_requires=[
46 | "requests==2.28.2",
47 | "aiohttp==3.8.3",
48 | ],
49 | extras_require=extras_require,
50 | classifiers=[
51 | "Programming Language :: Python :: 3",
52 | "Programming Language :: Python :: 3.7",
53 | "Programming Language :: Python :: 3.8",
54 | "Programming Language :: Python :: 3.9",
55 | "Programming Language :: Python :: Implementation :: CPython",
56 | "License :: OSI Approved :: MIT License",
57 | "Operating System :: OS Independent",
58 | "Intended Audience :: Developers",
59 | "Natural Language :: English",
60 | "Typing :: Typed",
61 | ],
62 | python_requires=">=3.7",
63 | )
64 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janaSunrise/HypixelIO/388ebcc145e754639daafb0811d709c87b2ef3e6/tests/__init__.py
--------------------------------------------------------------------------------
/tests/mock_data/player_data.py:
--------------------------------------------------------------------------------
1 | PLAYER_MOCK = {
2 | "_id": 1234,
3 | "uuid": "2ad3kfei9ikmd",
4 | "displayname": "TestPlayer123",
5 | "knownAliases": ["1234", "test", "TestPlayer123"],
6 | "firstLogin": 123456,
7 | "lastLogin": 150996,
8 | "lastLogout": 178901,
9 | "achievementsOneTime": ["MVP", "MVP2", "BedwarsMVP"],
10 | "achievementPoints": 300,
11 | "achievements": {"bedwars_level": 5, "general_challenger": 7, "bedwars_wins": 18},
12 | "networkExp": 3400,
13 | "challenges": {"all_time": {"DUELS_challenge": 1, "BEDWARS_challenge": 6}},
14 | "mostRecentGameType": "BEDWARS",
15 | "socialMedia": {"links": {"DISCORD": "test#1234"}},
16 | "karma": 8888,
17 | "mcVersionRp": "1.8.9",
18 | "petStats": {},
19 | "currentGadget": "Dummy thingy",
20 | "rank": None,
21 | }
22 |
--------------------------------------------------------------------------------
/tests/test_converters.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from hypixelio import Converters as Conv
4 |
5 |
6 | class TestUsernameToUUID(unittest.TestCase):
7 | """Test for the Username to UUID conversion."""
8 |
9 | def test_conversion(self) -> None:
10 | test_cases = (
11 | ("janaSunrise", "c8438cdd126043448cca9e28646efbe7"),
12 | ("JanaSunrise123", "b4ead04f4e2d484ba70257d5729aa773"),
13 | ("VSCode_", "2a13b3a34bf343fa9d8db0f87187da39"),
14 | )
15 |
16 | for username, uuid in test_cases:
17 | self.assertEqual(Conv.username_to_uuid(username), uuid)
18 |
19 |
20 | class TestUUIDToUsername(unittest.TestCase):
21 | """Test for the UUID to Username conversion."""
22 |
23 | def test_conversion(self) -> None:
24 | test_cases = (
25 | ("janaSunrise", "c8438cdd126043448cca9e28646efbe7"),
26 | ("JanaSunrise123", "b4ead04f4e2d484ba70257d5729aa773"),
27 | ("VSCode_", "2a13b3a34bf343fa9d8db0f87187da39"),
28 | )
29 |
30 | for username, uuid in test_cases:
31 | self.assertEqual(username, Conv.uuid_to_username(uuid))
32 |
--------------------------------------------------------------------------------
/tests/test_player.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from typing import cast
4 |
5 | from hypixelio import Client
6 | from hypixelio.exceptions import HypixelAPIError, PlayerNotFoundError
7 | from hypixelio.models.player import Player
8 | from tests.mock_data.player_data import PLAYER_MOCK
9 |
10 | API_KEY = cast(str, os.getenv("HYPIXEL_KEY"))
11 |
12 | if not API_KEY:
13 | raise Exception("Please set the HYPIXEL_KEY environment variable.")
14 |
15 |
16 | class TestPlayer(unittest.TestCase):
17 | """Tests for testing the player data."""
18 |
19 | def test_invalid_player_name(self) -> None:
20 | test_cases = ("ewdijenwmim", "de3in7euw9s38h23782iwksjnhuwiks")
21 |
22 | client = Client(api_key=API_KEY)
23 |
24 | for test in test_cases:
25 | with self.assertRaises(PlayerNotFoundError):
26 | client.get_player(name=test)
27 |
28 | def test_invalid_player_uuid(self) -> None:
29 | test_cases = ("ewdijenwmim", "de3in7euw9s38h23782iwksjnhuwiks")
30 |
31 | client = Client(api_key=API_KEY)
32 |
33 | for test in test_cases:
34 | with self.assertRaises(HypixelAPIError):
35 | client.get_player(uuid=test)
36 |
37 | def test_player_data(self) -> None:
38 | client = Client(api_key=API_KEY)
39 | player = client.get_player(name="VSCode_")
40 |
41 | self.assertIsInstance(player.hypixel_id, str)
42 | self.assertIsInstance(player.achievement_points, int)
43 | self.assertIsInstance(player.one_time_achievements, list)
44 |
45 | def test_player_achievements(self) -> None:
46 | data = {
47 | "achievements": {
48 | "bedwars_level": 5,
49 | "general_challenger": 7,
50 | "bedwars_wins": 18,
51 | }
52 | }
53 |
54 | player = Player(PLAYER_MOCK)
55 | for key, value in player.achievements.items():
56 | self.assertEqual(value, data["achievements"][key])
57 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length=120
3 | import-order-style=pycharm
4 | application-import-names=hypixelio,tests,examples
5 | exclude=
6 | .venv/**,
7 | .git/**
8 | ignore=
9 | # Ignore missing return type annotations for special methods
10 | ANN204,
11 | # Ignore missing type annotations
12 | ANN101, # Init
13 | ANN102, # cls
14 | ANN002, # *args
15 | ANN003, # **kwargs
16 | E731, # Allow lambdas
17 | MD033, # Allow markdown inline HTML
18 | F401, # Allow `__init__` imports
19 |
--------------------------------------------------------------------------------