Ryzenth 2.0.4__py3-none-any.whl → 2.0.5__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.
Ryzenth/__init__.py CHANGED
@@ -19,11 +19,11 @@
19
19
 
20
20
  from . import *
21
21
  from .__version__ import __version__
22
- from ._client import ApiKeyFrom, RyzenthApiClient, SmallConvertDot, UrHellFrom
22
+ from ._client import ApiKeyFrom, FromConvertDot, RyzenthApiClient, UrHellFrom
23
23
 
24
24
  __all__ = [
25
25
  "ApiKeyFrom",
26
26
  "RyzenthApiClient",
27
27
  "UrHellFrom",
28
- "SmallConvertDot"
28
+ "FromConvertDot"
29
29
  ]
Ryzenth/__version__.py CHANGED
@@ -4,7 +4,7 @@ import platform
4
4
  def get_user_agent() -> str:
5
5
  return f"Ryzenth/Python-{platform.python_version()}"
6
6
 
7
- __version__ = "2.0.4"
7
+ __version__ = "2.0.5"
8
8
  __author__ = "TeamKillerX"
9
9
  __title__ = "Ryzenth"
10
10
  __description__ = "Ryzenth Python API Wrapper"
Ryzenth/_client.py CHANGED
@@ -17,18 +17,24 @@
17
17
  # You should have received a copy of the GNU Affero General Public License
18
18
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
19
 
20
- import os
20
+ import asyncio
21
+ import json
21
22
  import platform
23
+ import random
24
+ import time
22
25
  import typing as t
26
+ from os import environ, getenv
23
27
 
24
28
  import aiohttp
29
+ import httpx
25
30
  from box import Box
26
31
 
27
32
  from .__version__ import get_user_agent
28
33
  from ._asynchisded import RyzenthXAsync
29
34
  from ._errors import ForbiddenError, InternalError, ToolNotFoundError, WhatFuckError
35
+ from ._shared import TOOL_DOMAIN_MAP
30
36
  from ._synchisded import RyzenthXSync
31
- from .helper import Decorators
37
+ from .helper import AutoRetry, Decorators
32
38
 
33
39
 
34
40
  class ApiKeyFrom:
@@ -38,7 +44,7 @@ class ApiKeyFrom:
38
44
  api_key = None
39
45
 
40
46
  if not api_key:
41
- api_key = os.environ.get("RYZENTH_API_KEY")
47
+ api_key = environ.get("RYZENTH_API_KEY")
42
48
 
43
49
  if not api_key:
44
50
  api_key = "akeno_UKQEQMt991kh2Ehh7JqJYKapx8CCyeC" if is_ok else None
@@ -58,43 +64,44 @@ class UrHellFrom:
58
64
  def something(self):
59
65
  pass
60
66
 
61
- class SmallConvertDot:
67
+ class FromConvertDot:
62
68
  def __init__(self, obj):
63
69
  self.obj = obj
64
70
 
65
71
  def to_dot(self):
66
72
  return Box(self.obj if self.obj is not None else {})
67
73
 
68
- TOOL_DOMAIN_MAP = {
69
- "itzpire": "https://itzpire.com",
70
- "ryzenth": "https://randydev-ryu-js.hf.space",
71
- }
72
-
73
74
  class RyzenthApiClient:
74
75
  def __init__(
75
76
  self,
76
77
  *,
77
- api_key: str,
78
78
  tools_name: list[str],
79
- use_default_headers: bool = False
79
+ api_key: dict[str, list[dict]],
80
+ rate_limit: int = 5,
81
+ use_default_headers: bool = False,
82
+ use_httpx: bool = False
80
83
  ) -> None:
81
- if not api_key:
82
- raise WhatFuckError("API Key cannot be empty.")
84
+ if not isinstance(api_key, dict) or not api_key:
85
+ raise WhatFuckError("API Key must be a non-empty dict of tool_name → list of headers")
83
86
  if not tools_name:
84
87
  raise WhatFuckError("A non-empty list of tool names must be provided for 'tools_name'.")
85
88
 
86
- self._api_key: str = api_key
89
+ self._api_keys = api_key
87
90
  self._use_default_headers: bool = use_default_headers
88
- self._session: aiohttp.ClientSession = aiohttp.ClientSession(
89
- headers={
90
- "User-Agent": get_user_agent(),
91
- **({"x-api-key": self._api_key} if self._use_default_headers else {})
92
- }
93
- )
91
+ self._rate_limit = rate_limit
92
+ self._request_counter = 0
93
+ self._last_reset = time.monotonic()
94
+ self._use_httpx = use_httpx
95
+
94
96
  self._tools: dict[str, str] = {
95
97
  name: TOOL_DOMAIN_MAP.get(name)
96
98
  for name in tools_name
97
99
  }
100
+ self._session = (
101
+ httpx.AsyncClient()
102
+ if use_httpx else
103
+ aiohttp.ClientSession()
104
+ )
98
105
 
99
106
  def get_base_url(self, tool: str) -> str:
100
107
  check_ok = self._tools.get(tool, None)
@@ -102,59 +109,114 @@ class RyzenthApiClient:
102
109
  raise ToolNotFoundError(f"Base URL for tool '{tool}' not found.")
103
110
  return check_ok
104
111
 
112
+ def _get_headers_for_tool(self, tool: str) -> dict:
113
+ base = {"User-Agent": get_user_agent()}
114
+ if self._use_default_headers and tool in self._api_keys:
115
+ base.update(random.choice(self._api_keys[tool]))
116
+ return base
117
+
118
+ async def _throttle(self):
119
+ now = time.monotonic()
120
+ if now - self._last_reset >= 1:
121
+ self._last_reset = now
122
+ self._request_counter = 0
123
+
124
+ if self._request_counter >= self._rate_limit:
125
+ await asyncio.sleep(1 - (now - self._last_reset))
126
+ self._last_reset = time.monotonic()
127
+ self._request_counter = 0
128
+
129
+ self._request_counter += 1
130
+
105
131
  @classmethod
106
132
  def from_env(cls) -> "RyzenthApiClient":
107
- api_key: t.Optional[str] = os.environ.get("RYZENTH_API_KEY")
108
- if not api_key:
109
- raise WhatFuckError("API Key cannot be empty.")
110
- return cls(api_key=api_key)
111
-
112
- async def _status_resp_error(self, resp):
113
- if resp.status == 403:
114
- raise ForbiddenError("Access Forbidden: You may be blocked or banned.")
115
- if resp.status == 500:
116
- raise InternalError("Error requests status code 5000")
133
+ tools_raw = getenv("RYZENTH_TOOLS")
134
+ api_key_raw = getenv("RYZENTH_API_KEY_JSON")
135
+ rate_limit_raw = getenv("RYZENTH_RATE_LIMIT", "5")
136
+ use_headers = getenv("RYZENTH_USE_HEADERS", "true")
137
+ use_httpx = getenv("RYZENTH_USE_HTTPX", "false")
138
+
139
+ if not tools_raw or not api_key_raw:
140
+ raise WhatFuckError("Environment variables RYZENTH_TOOLS and RYZENTH_API_KEY_JSON are required.")
141
+
142
+ tools = [t.strip() for t in tools_raw.split(",")]
143
+ api_keys = json.loads(api_key_raw)
144
+ rate_limit = int(rate_limit_raw)
145
+ use_default_headers = use_headers.lower() == "true"
146
+ httpx_flag = use_httpx.lower() == "true"
147
+
148
+ return cls(
149
+ tools_name=tools,
150
+ api_key=api_keys,
151
+ rate_limit=rate_limit,
152
+ use_default_headers=use_default_headers,
153
+ use_httpx=httpx_flag
154
+ )
117
155
 
156
+ async def _status_resp_error(self, resp, status_httpx=False):
157
+ if status_httpx:
158
+ if resp.status_code == 403:
159
+ raise ForbiddenError("Access Forbidden: You may be blocked or banned.")
160
+ elif resp.status_code == 401:
161
+ raise ForbiddenError("Access Forbidden: Required API key or invalid params.")
162
+ elif resp.status_code == 500:
163
+ raise InternalError("Error requests status code 500")
164
+ else:
165
+ if resp.status == 403:
166
+ raise ForbiddenError("Access Forbidden: You may be blocked or banned.")
167
+ elif resp.status == 401:
168
+ raise ForbiddenError("Access Forbidden: Required API key or invalid params.")
169
+ elif resp.status == 500:
170
+ raise InternalError("Error requests status code 500")
171
+
172
+ @AutoRetry(max_retries=3, delay=1.5)
118
173
  async def get(
119
174
  self,
120
175
  tool: str,
121
176
  path: str,
122
- params: t.Optional[dict] = None
123
- ) -> dict:
177
+ params: t.Optional[dict] = None,
178
+ use_image_content: bool = False
179
+ ) -> t.Union[dict, bytes]:
180
+ await self._throttle()
124
181
  base_url = self.get_base_url(tool)
125
182
  url = f"{base_url}{path}"
126
- try:
127
- async with self._session.get(url, params=params) as resp:
128
- await self._status_resp_error(resp)
183
+ headers = self._get_headers_for_tool(tool)
184
+
185
+ if self._use_httpx:
186
+ resp = await self._session.get(url, params=params, headers=headers)
187
+ await self._status_resp_error(resp, status_httpx=True)
188
+ resp.raise_for_status()
189
+ return resp.content if use_image_content else resp.json()
190
+ else:
191
+ async with self._session.get(url, params=params, headers=headers) as resp:
192
+ await self._status_resp_error(resp, status_httpx=False)
129
193
  resp.raise_for_status()
130
- return await resp.json()
131
- except ForbiddenError as e:
132
- return {"error": str(e)}
133
- except aiohttp.ClientResponseError as e:
134
- return {"error": f"HTTP Error: {e.status} {e.message}"}
135
- except Exception as e:
136
- return {"error": str(e)}
194
+ return await resp.read() if use_image_content else await resp.json()
137
195
 
196
+ @AutoRetry(max_retries=3, delay=1.5)
138
197
  async def post(
139
198
  self,
140
199
  tool: str,
141
200
  path: str,
142
201
  data: t.Optional[dict] = None,
143
- json: t.Optional[dict] = None
144
- ) -> dict:
202
+ json: t.Optional[dict] = None,
203
+ use_image_content: bool = False
204
+ ) -> t.Union[dict, bytes]:
205
+ await self._throttle()
145
206
  base_url = self.get_base_url(tool)
146
207
  url = f"{base_url}{path}"
147
- try:
148
- async with self._session.post(url, data=data, json=json) as resp:
149
- await self._status_resp_error(resp)
208
+ headers = self._get_headers_for_tool(tool)
209
+
210
+ if self._use_httpx:
211
+ resp = await self._session.post(url, data=data, json=json, headers=headers)
212
+ await self._status_resp_error(resp, status_httpx=True)
213
+ resp.raise_for_status()
214
+ return resp.content if use_image_content else resp.json()
215
+ else:
216
+ async with self._session.post(url, data=data, json=json, headers=headers) as resp:
217
+ await self._status_resp_error(resp, status_httpx=False)
150
218
  resp.raise_for_status()
151
- return await resp.json()
152
- except ForbiddenError as e:
153
- return {"error": str(e)}
154
- except aiohttp.ClientResponseError as e:
155
- return {"error": f"HTTP Error: {e.status} {e.message}"}
156
- except Exception as e:
157
- return {"error": str(e)}
219
+ return await resp.read() if use_image_content else await resp.json()
158
220
 
159
221
  async def close(self):
160
222
  await self._session.close()
Ryzenth/_shared.py CHANGED
@@ -1,3 +1,11 @@
1
+ TOOL_DOMAIN_MAP = {
2
+ "itzpire": "https://itzpire.com",
3
+ "ryzenth": "https://randydev-ryu-js.hf.space",
4
+ "onrender": "https://x-api-js.onrender.com",
5
+ "openai": "https://api.openai.com/v1",
6
+ "gemini": "https://generativelanguage.googleapis.com/v1"
7
+ }
8
+
1
9
  # this API is different
2
10
 
3
11
  BASE_DICT_RENDER = {
@@ -17,7 +17,7 @@
17
17
  # You should have received a copy of the GNU Affero General Public License
18
18
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
19
 
20
- from ._decorators import Decorators
20
+ from ._decorators import AutoRetry, Decorators
21
21
  from ._federation import FbanAsync, FbanSync
22
22
  from ._fonts import FontsAsync, FontsSync
23
23
  from ._images import ImagesAsync, ImagesSync
@@ -41,5 +41,6 @@ __all__ = [
41
41
  "FontsSync",
42
42
  "HumanizeAsync",
43
43
  "HumanizeSync",
44
- "Decorators"
44
+ "Decorators",
45
+ "AutoRetry"
45
46
  ]
@@ -17,11 +17,33 @@
17
17
  # You should have received a copy of the GNU Affero General Public License
18
18
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
19
 
20
+ import asyncio
20
21
  from functools import wraps
21
22
 
23
+ import aiohttp
24
+ import httpx
25
+
22
26
  from ..types import QueryParameter
23
27
 
24
28
 
29
+ def AutoRetry(max_retries: int = 3, delay: float = 1.5):
30
+ def decorator(func):
31
+ @wraps(func)
32
+ async def wrapper(*args, **kwargs):
33
+ for attempt in range(max_retries):
34
+ try:
35
+ return await func(*args, **kwargs)
36
+ except (
37
+ httpx.HTTPError,
38
+ aiohttp.ClientError,
39
+ asyncio.TimeoutError
40
+ ) as e:
41
+ if attempt == max_retries - 1:
42
+ raise e
43
+ await asyncio.sleep(delay)
44
+ return wrapper
45
+ return decorator
46
+
25
47
  class Decorators:
26
48
  def __init__(self, class_func):
27
49
  self._clients_ai = class_func(..., is_ok=True)
Ryzenth/helper/_images.py CHANGED
@@ -17,6 +17,11 @@
17
17
  # You should have received a copy of the GNU Affero General Public License
18
18
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
19
 
20
+ import asyncio
21
+ import logging
22
+ import os
23
+ import uuid
24
+
20
25
  from .._errors import WhatFuckError
21
26
  from ..types import QueryParameter
22
27
 
@@ -43,7 +48,7 @@ class ImagesAsync:
43
48
 
44
49
  async def to_save(self, params: QueryParameter, file_path="fluxai.jpg"):
45
50
  content = await self.generate(params)
46
- return ResponseFileImage(content).to_save(file_path)
51
+ return await ResponseFileImage(content).to_save(file_path)
47
52
 
48
53
  class ImagesSync:
49
54
  def __init__(self, parent):
@@ -66,14 +71,32 @@ class ImagesSync:
66
71
 
67
72
  def to_save(self, params: QueryParameter, file_path="fluxai.jpg"):
68
73
  content = self.generate(params)
69
- return ResponseFileImage(content).to_save(file_path)
74
+ return ResponseFileImage(content).sync_to_save(file_path)
70
75
 
71
76
 
72
77
  class ResponseFileImage:
73
78
  def __init__(self, response_content: bytes):
74
79
  self.response_content = response_content
75
80
 
76
- def to_save(self, file_path="fluxai.jpg"):
81
+ def sync_to_save(self, file_path="fluxai.jpg"):
82
+ with open(file_path, "wb") as f:
83
+ f.write(self.response_content)
84
+ logging.info(f"File saved: {file_path}")
85
+ return file_path
86
+
87
+ async def to_save(self, file_path: str = None, auto_delete: bool = False, delay: int = 5):
88
+ if file_path is None:
89
+ file_path = f"{uuid.uuid4().hex}.jpg"
90
+
77
91
  with open(file_path, "wb") as f:
78
92
  f.write(self.response_content)
93
+ logging.info(f"File saved: {file_path}")
94
+
95
+ if auto_delete:
96
+ await asyncio.sleep(delay)
97
+ try:
98
+ os.remove(file_path)
99
+ return True
100
+ except FileNotFoundError:
101
+ return False
79
102
  return file_path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Ryzenth
3
- Version: 2.0.4
3
+ Version: 2.0.5
4
4
  Summary: Ryzenth Python Wrapper For Perfomance
5
5
  Author: TeamKillerX
6
6
  License: MIT
@@ -125,6 +125,11 @@ export RYZENTH_API_KEY=your-api-key
125
125
  ## Web scrapers
126
126
  * [`itzpire`](https://itzpire.com) - Team Developer
127
127
 
128
+ ## Tool Developer
129
+ ~ Artificial Intelligence
130
+ - [`OpenAI`](https://platform.openai.com/docs) - OpenAI Docs
131
+ - [`Gemini AI`](https://ai.google.dev) - Gemini AI Docs
132
+
128
133
  ## Credits
129
134
 
130
135
  * Built with love by [xtdevs](https://t.me/xtdevs)
@@ -1,15 +1,15 @@
1
- Ryzenth/__init__.py,sha256=ON7RbtPrgK-Fw414Vro5J2OAGocv0522rRIojMNX2Q0,1043
2
- Ryzenth/__version__.py,sha256=Q04fL4sPvcvdeXFCEVrdKyedK87dQO--pzGS5kN23dI,223
1
+ Ryzenth/__init__.py,sha256=WWKCkPiNNTFYgXb8pJ9kQh8wP1lhd6fFB1Q8KHfttAE,1041
2
+ Ryzenth/__version__.py,sha256=5YjEabYXSBMhmynOCbMaIrSSLMH3MSvKXUb1pPyOivg,223
3
3
  Ryzenth/_asynchisded.py,sha256=5ZjrXZzMSZw3T6kQ3eg-owgH1Y2dmGWJy9AOQqcoFUQ,5051
4
- Ryzenth/_client.py,sha256=LinE_H_5LpBT2k_ulgH693IV0lkh884wNxOotmHIA_g,5226
4
+ Ryzenth/_client.py,sha256=9NUaMIaz67ZT6pwJzmlGO3_soeZie5j6KOaKyA_6eBo,7965
5
5
  Ryzenth/_errors.py,sha256=bOqi0_DElcmRrBqyDim6K248Ys-JQRSOvd32sJGW3aw,1812
6
- Ryzenth/_shared.py,sha256=zlERjX4XmYsDbkei8BRQ_-G1ozPlsn0SSalsAN6roT0,1682
6
+ Ryzenth/_shared.py,sha256=NoN7hL26GW93ek_NReINJP5ZybYwnGq1M66HCLn9yx8,1947
7
7
  Ryzenth/_synchisded.py,sha256=Ns0F4iA4kWUg2j5u0Tyqj2E1mXIMs29IoQZCYW5G1Gw,4922
8
- Ryzenth/helper/__init__.py,sha256=BkP6fQ3IJnOqyXn07jD7anumVPlm8lVPNkFnK9b6XpE,1447
9
- Ryzenth/helper/_decorators.py,sha256=rEdJRoQrJfqd4LqNOiFfPwEQwMU4uVuEsoqRuQfw99I,2125
8
+ Ryzenth/helper/__init__.py,sha256=ZditYtDnZOtTJ_odPgWH0Nrj-qQJ868zjy41_DzK_ns,1473
9
+ Ryzenth/helper/_decorators.py,sha256=8uKdcseA7Cbq5sy2twgujZhbwqjaLwX13BT70diNRFs,2782
10
10
  Ryzenth/helper/_federation.py,sha256=pfqqGjg179f-olvW1Z7aX1nQf0GQJdSK4NDMaMDxmbA,14552
11
11
  Ryzenth/helper/_fonts.py,sha256=Yy5qWdumGf0Y7tcvEXGLSn87mKlr-x_gmO241a465j8,3035
12
- Ryzenth/helper/_images.py,sha256=sDIqo964oDeLvhf1XtJ1khNgSuYXSzmQY9w_jJS89fI,3073
12
+ Ryzenth/helper/_images.py,sha256=NltJZidfhFCvTf3sF5WBIuWlym1cKpr2WDrk0sJbh20,3739
13
13
  Ryzenth/helper/_moderator.py,sha256=fAi0Xxk6CSShXl94H0FT8oDn6FttVq3ysBiROjSesCw,5501
14
14
  Ryzenth/helper/_openai.py,sha256=YkoW40X7Hgo_gQCWqrxHAe_uTx1fR5AyFAZh2MMiNJo,2582
15
15
  Ryzenth/helper/_ryzenth.py,sha256=VPjo09JOjtzS74AxUwcXsaWFGY923_scqZ2ujzBEa3A,2874
@@ -21,8 +21,8 @@ Ryzenth/tests/test_moderator.py,sha256=wc9A_0gx3LobMD7CDS-h2eTNPNYxeJk_rqtd2QTt4
21
21
  Ryzenth/tests/test_send.py,sha256=yPQV3XRsPKBo4eSsz5kc2R6BEuru0zmMexYshX0Ac3s,573
22
22
  Ryzenth/tests/test_send_downloader.py,sha256=23Lkq6bkh5SVDZ2hRH1Q3nlqpl-dqqGMSznDkmgDbhc,1318
23
23
  Ryzenth/types/__init__.py,sha256=2q3Oy7wCtgHa1cVY1JVN6cJht7uEAva3yFijSiJxYdI,1392
24
- ryzenth-2.0.4.dist-info/licenses/LICENSE,sha256=C73aiGSgoCAVNzvAHs-TROaf5vV8yCj9nqpGrmfNHHo,1068
25
- ryzenth-2.0.4.dist-info/METADATA,sha256=pujr79wsNKFr6W8UJqVJODRvlN0HyuY-xh9PllIE3Jg,4642
26
- ryzenth-2.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- ryzenth-2.0.4.dist-info/top_level.txt,sha256=0vIhjOjoQuCxLeZO0of8VCx2jsri-bLHV28nh8wWDnc,8
28
- ryzenth-2.0.4.dist-info/RECORD,,
24
+ ryzenth-2.0.5.dist-info/licenses/LICENSE,sha256=C73aiGSgoCAVNzvAHs-TROaf5vV8yCj9nqpGrmfNHHo,1068
25
+ ryzenth-2.0.5.dist-info/METADATA,sha256=IhPRu_oM-7q5vdMhHnfF8fokRG9WGlwGsSxE_WT2oPE,4804
26
+ ryzenth-2.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ ryzenth-2.0.5.dist-info/top_level.txt,sha256=0vIhjOjoQuCxLeZO0of8VCx2jsri-bLHV28nh8wWDnc,8
28
+ ryzenth-2.0.5.dist-info/RECORD,,