Python-3xui 0.0.4__tar.gz → 0.0.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Python-3xui
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: 3x-ui wrapper for python
5
5
  Project-URL: Homepage, https://github.com/Artem-Potapov/3x-py
6
6
  Project-URL: Issues, https://github.com/Artem-Potapov/3x-py/issues
@@ -16,6 +16,7 @@ Requires-Dist: async-lru~=2.2.0
16
16
  Requires-Dist: dotenv~=0.9.9
17
17
  Requires-Dist: httpx~=0.28.1
18
18
  Requires-Dist: pydantic<3,~=2.12.5
19
+ Requires-Dist: pyotp~=2.9.0
19
20
  Provides-Extra: testing
20
21
  Requires-Dist: pytest; extra == 'testing'
21
22
  Requires-Dist: pytest-asyncio; extra == 'testing'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Python-3xui"
3
- version = "0.0.4"
3
+ version = "0.0.5"
4
4
  authors = [
5
5
  { name="JustMe_001", email="justme001.causation755@passinbox.com" },
6
6
  ]
@@ -23,7 +23,8 @@ dependencies = [
23
23
  "pydantic ~= 2.12.5, < 3",
24
24
  "httpx ~=0.28.1",
25
25
  "dotenv ~= 0.9.9",
26
- "async_lru ~= 2.2.0"
26
+ "async_lru ~= 2.2.0",
27
+ "pyotp ~= 2.9.0"
27
28
  ]
28
29
 
29
30
 
@@ -62,7 +62,8 @@ class XUIClient:
62
62
 
63
63
  def __init__(self, base_website: str, base_port: int, base_path: str,
64
64
  *, username: str | None = None, password: str | None = None,
65
- two_fac_code: str | None = None, session_duration: int = 3600) -> None:
65
+ two_fac_code: str | None = None, session_duration: int = 3600,
66
+ custom_prod_string: str = "testing") -> None:
66
67
  """Initialize the XUIClient.
67
68
 
68
69
  Args:
@@ -76,7 +77,7 @@ class XUIClient:
76
77
  """
77
78
  from . import endpoints # look, I know it's bad, but we need to evade cyclical imports
78
79
  self.connected: bool = False
79
- self.PROD_STRING = "tester-777"
80
+ self.PROD_STRING = custom_prod_string
80
81
  self.session: AsyncClient | None = None
81
82
  self.base_host: str = base_website
82
83
  self.base_port: int = base_port
@@ -97,9 +98,9 @@ class XUIClient:
97
98
  #init self.totp
98
99
  if self.two_fac_secret:
99
100
  if self.two_fac_secret.isdigit() and len(self.two_fac_secret) <= 8:
100
- #print("WARNING: You seem to have entered a 2FA **code**, not a 2FA secret."
101
- #"Although entering the secret is dangerous, there is no other way to provide a consistent way"
102
- #"for continuous login. This code will only work for this specific login.")
101
+ print("WARNING: You seem to have entered a 2FA **code**, not a 2FA secret."
102
+ "Although entering the secret is dangerous, there is no other way to provide a consistent way"
103
+ "for continuous login. This code will only work for this specific login.")
103
104
  self.totp = None
104
105
  else:
105
106
  self.totp = pyotp.TOTP(self.two_fac_secret)
@@ -115,9 +116,9 @@ class XUIClient:
115
116
  Returns:
116
117
  The singleton XUIClient instance.
117
118
  """
118
- #print("initializing client")
119
+ print("initializing client")
119
120
  if cls._instance is None:
120
- #print("nu instance")
121
+ print("nu instance")
121
122
  cls._instance = super(XUIClient, cls).__new__(cls)
122
123
  return cls._instance
123
124
 
@@ -140,15 +141,15 @@ class XUIClient:
140
141
  Raises:
141
142
  RuntimeError: If max retries exceeded or session is invalid.
142
143
  """
143
- #print(f"SAFE REQUEST, {method}, is running to a URL of {kwargs["url"]}")
144
- #print(str(self.session.base_url) + str(kwargs["url"]))
144
+ print(f"SAFE REQUEST, {method}, is running to a URL of {kwargs["url"]}")
145
+ print(str(self.session.base_url) + str(kwargs["url"]))
145
146
  async for attempt in async_range(self.max_retries):
146
147
  resp = await self.session.request(method=method, **kwargs)
147
148
  if resp.status_code // 100 != 2: #because it can return either 201 or 202
148
149
  if resp.status_code == 404:
149
150
  now: float = datetime.now(UTC).timestamp()
150
151
  if self.session_start is None or now - self.session_start > self.session_duration:
151
- #print("Guys, we're not logged in, fixing that rn")
152
+ print("Guys, we're not logged in, fixing that rn")
152
153
  await self.login()
153
154
  continue
154
155
  else:
@@ -270,8 +271,8 @@ class XUIClient:
270
271
  if self.two_fac_secret:
271
272
  payload["twoFactorCode"] = self.two_fac_secret
272
273
 
273
- #print(self.session.base_url)
274
- #print("WE'RE LOGGING IN")
274
+ print(self.session.base_url)
275
+ print("WE'RE LOGGING IN")
275
276
  resp = await self.session.post("/login", data=payload)
276
277
  if resp.status_code == 200:
277
278
  resp_json = resp.json()
@@ -329,7 +330,7 @@ class XUIClient:
329
330
  exc_val: The exception value, if an exception occurred.
330
331
  exc_tb: The exception traceback, if an exception occurred.
331
332
  """
332
- #print("disconnectin'")
333
+ print("disconnectin'")
333
334
  await self.disconnect()
334
335
  return
335
336
 
@@ -350,6 +351,7 @@ class XUIClient:
350
351
  inbounds = await self.inbounds_end.get_all()
351
352
  usable_inbounds: list[Inbound] = []
352
353
  for inb in inbounds:
354
+ #TODO: make prod_strings regex instead of STR
353
355
  if self.PROD_STRING.lower() in inb.remark.lower():
354
356
  usable_inbounds.append(inb)
355
357
  if len(usable_inbounds) == 0:
@@ -369,12 +371,12 @@ class XUIClient:
369
371
  timer from 5 to 60*60*24 in the code.
370
372
  """
371
373
  while self.connected:
372
- #print("You're seeing this message because I forgot to remove it in api.update_inbounds() !")
373
- #print("Please change the timer from 5 to 60*60*24!")
374
+ print("You're seeing this message because I forgot to remove it in api.update_inbounds() !")
375
+ print("Please change the timer from 5 to 60*60*24!")
374
376
  self.get_production_inbounds.cache_clear()
375
377
  await self.get_production_inbounds() #fill the cache
376
- await asyncio.sleep(60*10)
377
- ##print(stat)
378
+ await asyncio.sleep(10)
379
+ #print(stat)
378
380
 
379
381
  #========================clients management========================
380
382
  async def get_client_with_tgid(self, tgid: int, inbound_id: int | None = None) -> List[ClientStats]:
@@ -409,6 +411,7 @@ class XUIClient:
409
411
  This method creates a new client with the given Telegram ID and
410
412
  adds it to the production inbounds. The client is configured with
411
413
  default settings and the additional remark.
414
+ Note that the sub id is created by util.generate_email_from_tgid_inbid, so use that to retrieve.
412
415
 
413
416
  Args:
414
417
  telegram_id: The Telegram ID of the client.
@@ -510,7 +513,7 @@ class XUIClient:
510
513
  email = util.generate_email_from_tgid_inbid(telegram_id, inbound.id)
511
514
  resp = await self.clients_end.delete_client_by_email(email, inbound.id)
512
515
  responses.append(resp)
513
- #print("Inbound deleted")
516
+ print("Inbound deleted")
514
517
 
515
518
  return responses
516
519
 
@@ -28,7 +28,7 @@ class BaseModel(pydantic.BaseModel):
28
28
  model_config = pydantic.ConfigDict(ignored_types=(cached_property, ))
29
29
 
30
30
  def model_post_init(self, context: Any, /) -> None:
31
- ##print(f"Model {self.__class__}, {self} initialized")
31
+ #print(f"Model {self.__class__}, {self} initialized")
32
32
  ...
33
33
 
34
34
 
@@ -232,17 +232,17 @@ class Clients(BaseEndpoint):
232
232
  else:
233
233
  raise TypeError
234
234
  # send request
235
- #print(type(final))
236
- #print(final)
235
+ print(type(final))
236
+ print(final)
237
237
  data = final.model_dump(by_alias=True)
238
- #print(type(data))
239
- #print(json.dumps(data))
240
- #print(f"{self._url}{endpoint}")
238
+ print(type(data))
239
+ print(json.dumps(data))
240
+ print(f"{self._url}{endpoint}")
241
241
  resp = await self.client.safe_post(f"{self._url}{endpoint}", data=data)
242
242
 
243
243
  #YOU NEED TO PASS SETTINGS AS A STRING, NOT AS A DICT, YOU FUCKING DUMBASS!
244
- #print(resp)
245
- #print(resp.json())
244
+ print(resp)
245
+ print(resp.json())
246
246
  return resp
247
247
 
248
248
  async def _request_update_client(self, client: models.InboundClients | models.SingleInboundClient,
@@ -218,7 +218,7 @@ async def check_xui_response_validity(response: JsonType | httpx.Response) -> st
218
218
  if "database" in msg.lower() and "locked" in msg.lower() and not success:
219
219
  logging.log(logging.WARNING, "Database is locked, retrying...")
220
220
  return "DB_LOCKED"
221
- #print(f"Unsuccessful operation! Message: {json_resp["msg"]}")
221
+ print(f"Unsuccessful operation! Message: {json_resp["msg"]}")
222
222
  return "ERROR"
223
223
  raise RuntimeError("Validator got something very unexpected (Please don't shove responses with non-20X status codes in here...)")
224
224
 
File without changes
File without changes
File without changes