dankmemer.py 0.1.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dankmemer/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ __version__ = "0.1.0a0"
2
+
3
+ from .client import DankMemerClient
4
+ from .routes.items import Item, ItemsFilter
5
+ from .utils import Fuzzy
6
+
7
+ __all__ = ["DankMemerClient", "Fuzzy", "ItemsFilter", "Item"]
dankmemer/client.py ADDED
@@ -0,0 +1,100 @@
1
+ import asyncio
2
+ import time
3
+ from datetime import timedelta
4
+ from typing import Optional
5
+
6
+ import aiohttp
7
+
8
+ from .routes import items
9
+
10
+
11
+ class DankMemerClient:
12
+ """
13
+ An asynchronous client for accessing the DankAlert API.
14
+
15
+ This client manages and provides access to various API endpoints
16
+ (e.g. items, npcs, skills, tools) along with built-in caching. In addition, it supports
17
+ anti-rate-limit behavior: when 'useAntirateLimit' is enabled (the default), the client
18
+ ensures that no more than 60 requests are made every 60 seconds.
19
+
20
+ Recommended usage:
21
+ As a context manager:
22
+ async with DankMemerClient(cache_ttl_hours=24) as client:
23
+ items = await client.items.query()
24
+
25
+ Without a context manager:
26
+ client = DankMemerClient(cache_ttl_hours=24)
27
+ items = await client.items.query()
28
+ await client.session.close()
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ *,
34
+ useAntirateLimit: bool = True,
35
+ base_url="https://api.dankalert.xyz/dank",
36
+ session: Optional[aiohttp.ClientSession] = None,
37
+ cache_ttl_hours: int = 24,
38
+ ):
39
+ self.use_anti_ratelimit = useAntirateLimit
40
+ self.base_url = base_url.rstrip("/")
41
+ self.session = session or aiohttp.ClientSession()
42
+ if cache_ttl_hours < 1:
43
+ raise ValueError("cache_ttl_hours must be at least 1 hour")
44
+ self.cache_ttl = timedelta(hours=cache_ttl_hours)
45
+
46
+ # Rate-limiting (current API: 60 requests per 60 seconds)
47
+ self._rate_limit_lock = asyncio.Lock()
48
+ self.max_requests: int = 60
49
+ self.request_period: float = 60.0
50
+ self._request_times: list[float] = []
51
+
52
+ self.items = items.ItemsRoute(self, self.cache_ttl)
53
+
54
+ async def __aenter__(self):
55
+ return self
56
+
57
+ async def __aexit__(self, exc_type, exc, tb):
58
+ if self.session:
59
+ await self.session.close()
60
+
61
+ async def request(self, route: str, params: dict = None):
62
+ """
63
+ Makes an HTTP GET request to the specified route with the given query parameters,
64
+ while enforcing the API's rate limiting if useAntirateLimit is enabled.
65
+
66
+ :param route: The API route to request (appended to the base URL).
67
+ :param params: Optional dictionary of query parameters.
68
+ :return: The parsed JSON response.
69
+ """
70
+
71
+ # Rate-limiting check:
72
+ # We perform a single check on the _request_times list and sleep if needed.
73
+ # Because all access to _request_times is serialized using an asyncio lock,
74
+ # and our expected usage pattern doesn't involve huge bursts of concurrent requests,
75
+ # a single check is sufficient to enforce the rate limit.
76
+ # If the system were to experience significant bursts, a while loop could be added
77
+ # to continuously re-check the condition after sleeping. However, given our design,
78
+ # the one-time check and sleep approach provides a simpler and maintainable solution.
79
+
80
+ if self.use_anti_ratelimit:
81
+ async with self._rate_limit_lock:
82
+ now: float = time.monotonic()
83
+ self._request_times = [
84
+ t for t in self._request_times if now - t < self.request_period
85
+ ]
86
+ if len(self._request_times) >= self.max_requests:
87
+ wait_time: float = self.request_period - (
88
+ now - self._request_times[0]
89
+ )
90
+ await asyncio.sleep(wait_time)
91
+ now = time.monotonic()
92
+ self._request_times = [
93
+ t for t in self._request_times if now - t < self.request_period
94
+ ]
95
+ self._request_times.append(now)
96
+
97
+ url = f"{self.base_url}/{route}"
98
+ async with self.session.get(url, params=params) as response:
99
+ response.raise_for_status()
100
+ return await response.json()
dankmemer/utils.py ADDED
@@ -0,0 +1,15 @@
1
+ class Fuzzy:
2
+ """
3
+ Represents a fuzzy match criterion.
4
+
5
+ :param value: The string value to match.
6
+ :param cutoff: The minimum matching ratio (0-100) required for a successful fuzzy match.
7
+ Defaults to 80.
8
+ """
9
+
10
+ def __init__(self, value: str, cutoff: int = 80) -> None:
11
+ self.value: str = value
12
+ self.cutoff: int = cutoff
13
+
14
+ def __repr__(self) -> str:
15
+ return f"Fuzzy({self.value!r}, cutoff={self.cutoff})"
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: dankmemer.py
3
+ Version: 0.1.0a0
4
+ Summary: A lightweight async wrapper for the DankAlert API.
5
+ Home-page: https://github.com/RayyanW786/dankmemer.py
6
+ Author: RayyanW786
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.7
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: aiohttp>=3.7.4
16
+ Requires-Dist: rapidfuzz>=1.6.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=6.0; extra == "dev"
19
+ Requires-Dist: pytest-asyncio>=0.14.0; extra == "dev"
20
+ Dynamic: license-file
@@ -0,0 +1,8 @@
1
+ dankmemer/__init__.py,sha256=gWN5UgfQV1UEiyvWpD8YPXHG0bD4aBlL6ItfinLTar8,200
2
+ dankmemer/client.py,sha256=uGf4ZGL4e23oFCpZ362tGlPmEeEjoBbKxHFQDBFQEds,4039
3
+ dankmemer/utils.py,sha256=CW8B5STAYJ_8Y1QJdx76CtrPgjqIHmwxIwQalYP_ZyY,481
4
+ dankmemer_py-0.1.0a0.dist-info/licenses/LICENSE,sha256=G6ElBv8aqRO3YwXQk6_7OfGcO2IFY5Nyi0PZBCWK9Ig,1084
5
+ dankmemer_py-0.1.0a0.dist-info/METADATA,sha256=IFFO4VC-wuX0C5n-43q7bNVwn3OvPE9vOlzu9CYGIa0,706
6
+ dankmemer_py-0.1.0a0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
7
+ dankmemer_py-0.1.0a0.dist-info/top_level.txt,sha256=ctC8iNcUp-0WbPZI7Oi_054klfpWhS4LgxAUtIPYgqM,10
8
+ dankmemer_py-0.1.0a0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rayyan
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.
@@ -0,0 +1 @@
1
+ dankmemer