xync-client 0.0.43.dev32__tar.gz → 0.0.43.dev35__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.
- {xync_client-0.0.43.dev32/xync_client.egg-info → xync_client-0.0.43.dev35}/PKG-INFO +1 -1
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/InAgent.py +3 -3
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/InAgent.py +10 -10
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/agent.py +34 -21
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/__init__.py +43 -28
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/loader.py +2 -1
- xync_client-0.0.43.dev35/xync_client/pg_storage.py +276 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35/xync_client.egg-info}/PKG-INFO +1 -1
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client.egg-info/SOURCES.txt +1 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/.env.sample +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/.gitignore +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/README.md +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/makefile +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/pyproject.toml +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/setup.cfg +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/TestAgent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/TestAsset.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/TestEx.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/TestOrder.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/Agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/AuthTrait.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/Base.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/Ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Abc/types.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitGet/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitGet/req.mjs +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/BitPapa/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/etype/cred.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/etype/order.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Bybit/ws.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Gate/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Gate/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/etype/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/etype/cred.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/etype/test.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/KuCoin/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/KuCoin/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/KuCoin/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Mexc/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Mexc/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Mexc/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Okx/etype/ad.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Okx/etype/pm.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Tinkoff/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Tinkoff/state.json +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Tinkoff/storage.json +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/api.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/agent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/auth.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/ex.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/pyd.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/pyro.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/__init__.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/pm_unifier.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/pyro.py +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client.egg-info/requires.txt +0 -0
- {xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
|
|
3
|
-
from xync_schema.models import
|
|
3
|
+
from xync_schema.models import Actor
|
|
4
4
|
|
|
5
5
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class BaseInAgentClient:
|
|
9
|
-
def __init__(self,
|
|
10
|
-
self.agent_client: BaseAgentClient =
|
|
9
|
+
def __init__(self, actor: Actor):
|
|
10
|
+
self.agent_client: BaseAgentClient = actor.client()
|
|
11
11
|
|
|
12
12
|
@abstractmethod
|
|
13
13
|
async def start_listen(self) -> bool: ...
|
|
@@ -6,8 +6,7 @@ from xync_schema import models
|
|
|
6
6
|
from xync_client.Abc.InAgent import BaseInAgentClient
|
|
7
7
|
from xync_client.Bybit.agent import AgentClient
|
|
8
8
|
from xync_client.Bybit.ws import prv
|
|
9
|
-
from xync_client.
|
|
10
|
-
from xync_client.loader import PG_DSN, bot
|
|
9
|
+
from xync_client.loader import PG_DSN
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class InAgentClient(BaseInAgentClient):
|
|
@@ -16,7 +15,7 @@ class InAgentClient(BaseInAgentClient):
|
|
|
16
15
|
async def start_listen(self):
|
|
17
16
|
t = await self.agent_client.ott()
|
|
18
17
|
ts = int(float(t["time_now"]) * 1000)
|
|
19
|
-
await prv(self.agent_client.agent.auth["deviceId"], t["result"], ts, listen)
|
|
18
|
+
await prv(self.agent_client.actor.agent.auth["deviceId"], t["result"], ts, listen)
|
|
20
19
|
|
|
21
20
|
# 3N: [T] - Уведомление об одобрении запроса на сделку
|
|
22
21
|
async def request_accepted_notify(self) -> int: ... # id
|
|
@@ -28,13 +27,14 @@ def listen(data: dict):
|
|
|
28
27
|
|
|
29
28
|
async def main():
|
|
30
29
|
_ = await init_db(PG_DSN, models, True)
|
|
31
|
-
pbot = PyroClient(bot)
|
|
32
|
-
await pbot.app.start()
|
|
33
|
-
await pbot.app.create_channel("tc")
|
|
34
|
-
await pbot.app.stop()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cl: InAgentClient =
|
|
30
|
+
# pbot = PyroClient(bot)
|
|
31
|
+
# await pbot.app.start()
|
|
32
|
+
# await pbot.app.create_channel("tc")
|
|
33
|
+
# await pbot.app.stop()
|
|
34
|
+
|
|
35
|
+
actor = await models.Actor.filter(ex_id=9, agent__auth__isnull=False).prefetch_related("ex", "agent").first()
|
|
36
|
+
cl: InAgentClient = actor.in_client()
|
|
37
|
+
_ = await cl.start_listen()
|
|
38
38
|
await cl.agent_client.close()
|
|
39
39
|
|
|
40
40
|
|
|
@@ -381,6 +381,24 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
381
381
|
print("-" if mad.side else "+", end=req.price, flush=True)
|
|
382
382
|
await sleep(60)
|
|
383
383
|
|
|
384
|
+
def overprice_filter(self, ads: list[Ad], ceil: float, k: Literal[-1, 1]):
|
|
385
|
+
# вырезаем ads с ценами выше потолка
|
|
386
|
+
if ads and (ceil - float(ads[0].price)) * k > 0:
|
|
387
|
+
if int(ads[0].userId) != self.actor.exid:
|
|
388
|
+
ads.pop(0)
|
|
389
|
+
self.overprice_filter(ads, ceil, k)
|
|
390
|
+
|
|
391
|
+
def get_cad(self, ads: list[Ad], ceil: float, k: Literal[-1, 1], place: int, cur_plc: int) -> Ad:
|
|
392
|
+
# чью цену будем обгонять, предыдущей или слещующей объявы?
|
|
393
|
+
cad: Ad = ads[place] if cur_plc > place else ads[cur_plc]
|
|
394
|
+
# а цена обгоняемой объявы не выше нашего потолка?
|
|
395
|
+
if (float(cad.price) - ceil) * k <= 0:
|
|
396
|
+
# тогда берем следующую
|
|
397
|
+
ads.pop(place)
|
|
398
|
+
cad = self.get_cad(ads, ceil, k, place, cur_plc)
|
|
399
|
+
# todo: добавить фильтр по лимитам min-max
|
|
400
|
+
return cad
|
|
401
|
+
|
|
384
402
|
async def battle(
|
|
385
403
|
self,
|
|
386
404
|
coinex: models.Coinex,
|
|
@@ -409,28 +427,27 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
409
427
|
|
|
410
428
|
while self.actor.person.user.status > 0:
|
|
411
429
|
ads: list[Ad] = await self.ads(coinex, curex, is_sell, list(creds.keys()))
|
|
412
|
-
overprice_filter(ads, ceil, k)
|
|
430
|
+
self.overprice_filter(ads, ceil, k)
|
|
431
|
+
if not ads:
|
|
432
|
+
print(coinex.exid, curex.exid, is_sell, "no ads!")
|
|
433
|
+
await sleep(15)
|
|
434
|
+
continue
|
|
413
435
|
cur_plc = [i for i, ad in enumerate(ads) if int(ad.userId) == self.actor.exid][0]
|
|
414
436
|
mad: Ad = ads.pop(cur_plc)
|
|
415
437
|
if not ads:
|
|
416
438
|
await sleep(60)
|
|
417
439
|
continue
|
|
418
|
-
|
|
419
|
-
cad: Ad = ads[place] if cur_plc > place else ads[cur_plc]
|
|
420
|
-
# а цена обгоняемой объявы не выше нашего потолка?
|
|
421
|
-
if (float(cad.price) - ceil) * k <= 0:
|
|
422
|
-
# тогда берем следующую
|
|
423
|
-
cad = ads[cur_plc]
|
|
440
|
+
cad = self.get_cad(ads, ceil, k, place, cur_plc)
|
|
424
441
|
new_price = f"%.{curex.cur.scale}f" % round(float(cad.price) - k * step(mad, cad), curex.cur.scale)
|
|
425
442
|
if mad.price == new_price: # Если нужная цена и так уже стоит
|
|
426
443
|
print(end="v" if is_sell else "^", flush=True)
|
|
427
|
-
await sleep(
|
|
444
|
+
await sleep(3)
|
|
428
445
|
continue
|
|
429
446
|
if cad.priceType: # Если цена конкурента плавающая, то повышаем себе не цену, а %
|
|
430
447
|
new_premium = str(round(float(cad.premium) - k * step(mad, cad), 2))
|
|
431
448
|
if mad.premium == new_premium: # Если нужный % и так уже стоит
|
|
432
449
|
print(end="v" if is_sell else "^", flush=True)
|
|
433
|
-
await sleep(
|
|
450
|
+
await sleep(3)
|
|
434
451
|
continue
|
|
435
452
|
mad.premium = new_premium
|
|
436
453
|
mad.priceType = cad.priceType
|
|
@@ -439,6 +456,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
439
456
|
req = AdUpdateRequest.model_validate({**mad.model_dump(), "price": new_price, "paymentIds": credex_ids})
|
|
440
457
|
try:
|
|
441
458
|
_res = self.ad_upd(req)
|
|
459
|
+
print("-" if is_sell else "+", end=req.price, flush=True)
|
|
442
460
|
except FailedRequestError as e:
|
|
443
461
|
if ExcCode(e.status_code) == ExcCode.FixPriceLimit:
|
|
444
462
|
if limits := re.search(
|
|
@@ -455,13 +473,14 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
455
473
|
req.quantity = round(asset.free - (asset.freeze or 0) - (asset.lock or 0), coinex.coin.scale)
|
|
456
474
|
_res = self.ad_upd(req)
|
|
457
475
|
elif ExcCode(e.status_code) == ExcCode.RareLimit:
|
|
458
|
-
await sleep(
|
|
476
|
+
await sleep(192)
|
|
477
|
+
elif ExcCode(e.status_code) == ExcCode.Timestamp:
|
|
478
|
+
await sleep(2)
|
|
459
479
|
else:
|
|
460
480
|
raise e
|
|
461
481
|
except (ReadTimeoutError, ConnectionDoesNotExistError):
|
|
462
482
|
logging.warning("Connection failed. Restarting..")
|
|
463
|
-
|
|
464
|
-
await sleep(60)
|
|
483
|
+
await sleep(42)
|
|
465
484
|
|
|
466
485
|
|
|
467
486
|
def step(mad, cad) -> float:
|
|
@@ -473,13 +492,6 @@ def step(mad, cad) -> float:
|
|
|
473
492
|
)
|
|
474
493
|
|
|
475
494
|
|
|
476
|
-
def overprice_filter(ads: list[Ad], ceil: float, k: Literal[-1, 1]):
|
|
477
|
-
# вырезаем ads с ценами выше потолка
|
|
478
|
-
if ads and (ceil - float(ads[0].price)) * k > 0:
|
|
479
|
-
ads.pop(0)
|
|
480
|
-
overprice_filter(ads, ceil, k)
|
|
481
|
-
|
|
482
|
-
|
|
483
495
|
def listen(data: dict):
|
|
484
496
|
print(data)
|
|
485
497
|
|
|
@@ -494,11 +506,12 @@ class ExcCode(IntEnum):
|
|
|
494
506
|
FixPriceLimit = 912120022
|
|
495
507
|
RareLimit = 912120050
|
|
496
508
|
InsufficientAmount = 912120024
|
|
509
|
+
Timestamp = 10002
|
|
497
510
|
|
|
498
511
|
|
|
499
512
|
async def main():
|
|
500
513
|
_ = await init_db(PG_DSN, models, True)
|
|
501
|
-
logging.basicConfig(level=logging.
|
|
514
|
+
logging.basicConfig(level=logging.INFO)
|
|
502
515
|
actor = (
|
|
503
516
|
await models.Actor.filter(ex_id=9, agent__isnull=False).prefetch_related("ex", "agent", "person__user").first()
|
|
504
517
|
)
|
|
@@ -514,7 +527,7 @@ async def main():
|
|
|
514
527
|
# )
|
|
515
528
|
# await cl.set_creds()
|
|
516
529
|
await gather(
|
|
517
|
-
cl.battle(usdt, rub, False, ["volet"], 83.
|
|
530
|
+
cl.battle(usdt, rub, False, ["volet"], 83.88), # гонка в стакане покупки - мы продаем
|
|
518
531
|
cl.battle(usdt, rub, True, ["volet"], 82), # гонка в стакане продажи - мы покупаем
|
|
519
532
|
cl.battle(eth, rub, False, ["volet"], 160_000),
|
|
520
533
|
cl.battle(eth, rub, True, ["volet"], 144_000),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import re
|
|
2
3
|
from asyncio import run, sleep
|
|
3
4
|
from enum import StrEnum
|
|
@@ -5,7 +6,7 @@ from os.path import dirname
|
|
|
5
6
|
from typing import Literal
|
|
6
7
|
|
|
7
8
|
from aiogram.types import BufferedInputFile
|
|
8
|
-
from playwright.async_api import async_playwright, Page
|
|
9
|
+
from playwright.async_api import async_playwright, Page, FloatRect, Locator
|
|
9
10
|
from pyotp import TOTP
|
|
10
11
|
from pyrogram import filters
|
|
11
12
|
from pyrogram.handlers import MessageHandler
|
|
@@ -14,8 +15,8 @@ from playwright._impl._errors import TimeoutError
|
|
|
14
15
|
from xync_schema.enums import UserStatus
|
|
15
16
|
from xync_schema.models import User, PmAgent
|
|
16
17
|
|
|
17
|
-
from xync_client.
|
|
18
|
-
from xync_client.loader import bot
|
|
18
|
+
from xync_client.pyro import PyroClient
|
|
19
|
+
from xync_client.loader import bot, dp
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ExtraCaptchaException(Exception): ...
|
|
@@ -57,7 +58,8 @@ async def report(uid: int, byts: bytes, msg: str, exc: bool = True):
|
|
|
57
58
|
|
|
58
59
|
class Client:
|
|
59
60
|
agent: PmAgent
|
|
60
|
-
|
|
61
|
+
bbot: PyroClient
|
|
62
|
+
ubot: PyroClient
|
|
61
63
|
page: Page
|
|
62
64
|
gpage: Page
|
|
63
65
|
|
|
@@ -70,10 +72,10 @@ class Client:
|
|
|
70
72
|
async def start(self, headed: bool = False):
|
|
71
73
|
self.agent = await PmAgent.get(user_id=self.uid, user__status__gt=0, pm__norm="volet").prefetch_related("user")
|
|
72
74
|
|
|
73
|
-
self.
|
|
74
|
-
await self.
|
|
75
|
+
self.ubot, self.bbot = PyroClient(self.agent.user), PyroClient(bot)
|
|
76
|
+
await self.ubot.app.start(), await self.bbot.app.start()
|
|
75
77
|
self.msg_listener = MessageHandler(self.got_msg, filters.chat(["ProtectimusBot"]))
|
|
76
|
-
self.
|
|
78
|
+
self.ubot.app.add_handler(self.msg_listener)
|
|
77
79
|
|
|
78
80
|
playwright = await async_playwright().start()
|
|
79
81
|
browser = await playwright.chromium.launch(
|
|
@@ -113,7 +115,7 @@ class Client:
|
|
|
113
115
|
await self.page.click("input#checkOtpButton")
|
|
114
116
|
await self.page.wait_for_url(Pages.HOME)
|
|
115
117
|
|
|
116
|
-
async def wait_for_code(self, typ: Literal["login", "send"], past: int = 0, timeout: int = 5) -> str:
|
|
118
|
+
async def wait_for_code(self, typ: Literal["login", "send", "cap_xy"], past: int = 0, timeout: int = 5) -> str:
|
|
117
119
|
while past < timeout:
|
|
118
120
|
if code := self.msgs.pop(f"otp_{typ}", None):
|
|
119
121
|
return code
|
|
@@ -124,7 +126,7 @@ class Client:
|
|
|
124
126
|
async def got_msg(self, _, msg: Message):
|
|
125
127
|
if "Your OTP code:" in msg.text:
|
|
126
128
|
self.msgs["otp_login"] = msg.text[-6:]
|
|
127
|
-
|
|
129
|
+
elif "Confirmation code:" in msg.text:
|
|
128
130
|
self.msgs["otp_send"] = msg.text[-6:]
|
|
129
131
|
elif "Status: Completed. Sender:" in msg.text:
|
|
130
132
|
self.msgs["got_payment"] = parse_transaction_info(msg.text)
|
|
@@ -137,7 +139,7 @@ class Client:
|
|
|
137
139
|
await self.page.fill("#srcAmount", str(amount))
|
|
138
140
|
await self.page.fill("#destWalletId", dest)
|
|
139
141
|
await self.page.wait_for_timeout(300)
|
|
140
|
-
await self.page.locator("input[type=submit]", has_text="continue").click()
|
|
142
|
+
await self.page.locator("form#mainForm input[type=submit]", has_text="continue").click()
|
|
141
143
|
if otp := self.agent.auth.get("otp"):
|
|
142
144
|
totp = TOTP(otp)
|
|
143
145
|
code = totp.now()
|
|
@@ -157,23 +159,16 @@ class Client:
|
|
|
157
159
|
|
|
158
160
|
async def gmail_page(self):
|
|
159
161
|
gp = await self.page.context.new_page()
|
|
160
|
-
await gp.goto(Pages.GMH, timeout=
|
|
162
|
+
await gp.goto(Pages.GMH, timeout=30000)
|
|
161
163
|
if not gp.url.startswith(Pages.GMH):
|
|
162
|
-
# ваще с 0 заходим
|
|
163
|
-
if await (
|
|
164
|
+
if await ( # ваще с 0 заходим
|
|
164
165
|
sgn_btn := gp.locator(
|
|
165
166
|
'header a[href^="https://accounts.google.com/AccountChooser/signinchooser"]:visible',
|
|
166
|
-
has_text="sign
|
|
167
|
+
has_text="sign",
|
|
167
168
|
)
|
|
168
169
|
).count():
|
|
169
170
|
await sgn_btn.click()
|
|
170
|
-
# если надо выбрать акк
|
|
171
|
-
lang = await gp.get_attribute("html", "lang")
|
|
172
|
-
sgn = {
|
|
173
|
-
"ru": "Выберите аккаунт",
|
|
174
|
-
"en": "Choose an account",
|
|
175
|
-
}
|
|
176
|
-
if await gp.locator("h1#headingText", has_text=sgn[lang]).count():
|
|
171
|
+
if gp.url.startswith("https://accounts.google.com/v3/signin/accountchooser"): # если надо выбрать акк
|
|
177
172
|
await gp.locator("li").first.click()
|
|
178
173
|
# если предлагает залогиниться
|
|
179
174
|
elif await gp.locator("h1#headingText", has_text="Sign In").count():
|
|
@@ -181,8 +176,7 @@ class Client:
|
|
|
181
176
|
await gp.locator("button", has_text="Next").click()
|
|
182
177
|
# осталось ввести пороль:
|
|
183
178
|
await gp.fill("input[type=password]", self.agent.user.gmail_auth["password"])
|
|
184
|
-
|
|
185
|
-
await gp.locator("button", has_text=nxt[lang]).click()
|
|
179
|
+
await gp.locator("#passwordNext").click()
|
|
186
180
|
await report(self.uid, await gp.screenshot(), "Аппрувни гмейл, у тебя 1.5 минуты", False)
|
|
187
181
|
await gp.wait_for_url(lambda u: u.startswith(Pages.GMH), timeout=90 * 1000) # убеждаемся что мы в почте
|
|
188
182
|
self.gpage = gp
|
|
@@ -212,13 +206,32 @@ class Client:
|
|
|
212
206
|
except Exception as e:
|
|
213
207
|
await report(self.uid, await self.page.screenshot(), repr(e))
|
|
214
208
|
|
|
209
|
+
async def send_cap_help(self, xcap: Locator):
|
|
210
|
+
bb = await xcap.bounding_box(timeout=2000)
|
|
211
|
+
byts = await self.page.screenshot(clip=bb)
|
|
212
|
+
infile = BufferedInputFile(byts, "cap_xy.png")
|
|
213
|
+
await self.bbot.send_img("put x, y", byts)
|
|
214
|
+
self.bbot.app.storage.user_id()
|
|
215
|
+
await bot.send_photo(uid, infile, caption=msg)
|
|
216
|
+
await report(self.uid, byts, "x, y", False)
|
|
217
|
+
dp.message.register(self.got_cap_xy)
|
|
218
|
+
|
|
219
|
+
async def got_cap_xy(self, _, msg):
|
|
220
|
+
self.msgs["typ_cap_xy"] = msg.text.split(",")
|
|
221
|
+
|
|
215
222
|
async def captcha_click(self):
|
|
216
223
|
captcha_url = self.page.url
|
|
217
224
|
cbx = self.page.frame_locator("#main-iframe").frame_locator("iframe").first.locator("div#checkbox")
|
|
218
225
|
await cbx.wait_for(state="visible"), await self.page.wait_for_timeout(500)
|
|
219
226
|
await cbx.click(delay=94)
|
|
227
|
+
xcap = self.page.frame_locator("#main-iframe").frame_locator("iframe").last.locator("div.challenge-view")
|
|
228
|
+
if await xcap.count():
|
|
229
|
+
await self.send_cap_help(xcap)
|
|
230
|
+
x, y = await self.wait_for_code("cap_xy", timeout=59)
|
|
231
|
+
|
|
220
232
|
try:
|
|
221
233
|
await self.page.wait_for_url(lambda url: url != captcha_url)
|
|
234
|
+
|
|
222
235
|
except TimeoutError: # if page no changed -> captcha is undone
|
|
223
236
|
await self.page.screenshot(path=dirname(__file__) + "/xtr_captcha.png")
|
|
224
237
|
raise ExtraCaptchaException(self.page.url)
|
|
@@ -236,20 +249,22 @@ class Client:
|
|
|
236
249
|
# closing
|
|
237
250
|
await self.page.context.close()
|
|
238
251
|
await self.page.context.browser.close()
|
|
239
|
-
self.
|
|
240
|
-
await self.
|
|
252
|
+
self.ubot.app.remove_handler(self.msg_listener)
|
|
253
|
+
await self.ubot.app.stop(), await self.bbot.app.stop()
|
|
241
254
|
|
|
242
255
|
|
|
243
|
-
async def _test(
|
|
256
|
+
async def _test():
|
|
244
257
|
from x_model import init_db
|
|
245
258
|
from xync_client.loader import PG_DSN
|
|
246
259
|
from xync_schema import models
|
|
247
260
|
|
|
248
261
|
_ = await init_db(PG_DSN, models, True)
|
|
262
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
263
|
+
uid = 193017646
|
|
249
264
|
va = Client(uid)
|
|
250
265
|
try:
|
|
251
266
|
await va.start(True)
|
|
252
|
-
await va.send(
|
|
267
|
+
await va.send("alena.artemeva25@gmail.com", 8.3456)
|
|
253
268
|
await va.wait_for_payments()
|
|
254
269
|
except TimeoutError as te:
|
|
255
270
|
await report(uid, await va.page.screenshot(), repr(te))
|
|
@@ -257,4 +272,4 @@ async def _test(uid: int, dest: str, amount):
|
|
|
257
272
|
|
|
258
273
|
|
|
259
274
|
if __name__ == "__main__":
|
|
260
|
-
run(_test(
|
|
275
|
+
run(_test())
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from aiogram import Bot
|
|
1
|
+
from aiogram import Bot, Dispatcher
|
|
2
2
|
from dotenv import load_dotenv
|
|
3
3
|
from os import getenv as env
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ if not (TOKEN := env("TOKEN")):
|
|
|
11
11
|
logging.info(TOKEN := env("TOKEN"))
|
|
12
12
|
|
|
13
13
|
bot: Bot = Bot(token=TOKEN)
|
|
14
|
+
dp: Dispatcher = Dispatcher()
|
|
14
15
|
PG_DSN = f"postgres://{env('POSTGRES_USER')}:{env('POSTGRES_PASSWORD')}@{env('POSTGRES_HOST', 'xyncdbs')}:{env('POSTGRES_PORT', 5432)}/{env('POSTGRES_DB', env('POSTGRES_USER'))}"
|
|
15
16
|
TG_API_ID = env("TG_API_ID")
|
|
16
17
|
TG_API_HASH = env("TG_API_HASH")
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from pyrogram import Client, raw, utils
|
|
4
|
+
from pyrogram.storage import Storage
|
|
5
|
+
from tortoise import fields
|
|
6
|
+
from tortoise.signals import pre_save
|
|
7
|
+
from x_model.models import Model
|
|
8
|
+
from xync_schema.models import User
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Session(Model):
|
|
12
|
+
name = fields.CharField(255, primary_key=True)
|
|
13
|
+
dc_id = fields.IntField(null=False)
|
|
14
|
+
api_id = fields.IntField(null=False)
|
|
15
|
+
test_mode = fields.BooleanField(null=True)
|
|
16
|
+
auth_key = fields.BinaryField()
|
|
17
|
+
date = fields.IntField(null=False)
|
|
18
|
+
user: fields.OneToOneRelation[User] = fields.OneToOneField("models.User", "sessions")
|
|
19
|
+
user_id: int
|
|
20
|
+
is_bot = fields.BooleanField(null=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Peer(Model):
|
|
24
|
+
session: fields.ForeignKeyRelation[Session] = fields.OneToOneField("models.Session", "peers", primary_key=True)
|
|
25
|
+
id: int = fields.BigIntField(True)
|
|
26
|
+
access_hash: int = fields.BigIntField()
|
|
27
|
+
type = fields.CharField(255)
|
|
28
|
+
phone_number = fields.CharField(255)
|
|
29
|
+
last_update_on: int = fields.BigIntField()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pre_save(Peer)
|
|
33
|
+
async def person(_meta, peer: Peer, _db, _updated: dict) -> None:
|
|
34
|
+
if not peer.last_update_on:
|
|
35
|
+
peer.last_update_on = int(time.time())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UpdateState(Model):
|
|
39
|
+
session: fields.ForeignKeyRelation[Session] = fields.OneToOneField("models.Session", "update_states", primary_key=True)
|
|
40
|
+
pts = fields.IntField()
|
|
41
|
+
qts = fields.IntField()
|
|
42
|
+
date = fields.IntField()
|
|
43
|
+
seq = fields.IntField()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Version(Model):
|
|
47
|
+
id: None
|
|
48
|
+
number = fields.IntField(primary_key=True)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
|
|
52
|
+
if peer_type in ["user", "bot"]:
|
|
53
|
+
return raw.types.InputPeerUser(
|
|
54
|
+
user_id=peer_id,
|
|
55
|
+
access_hash=access_hash
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if peer_type == "group":
|
|
59
|
+
return raw.types.InputPeerChat(
|
|
60
|
+
chat_id=-peer_id
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if peer_type in ["channel", "supergroup"]:
|
|
64
|
+
return raw.types.InputPeerChannel(
|
|
65
|
+
channel_id=utils.get_channel_id(peer_id),
|
|
66
|
+
access_hash=access_hash
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
raise ValueError(f"Invalid peer type: {peer_type}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class MultiPostgresStorage(Storage):
|
|
73
|
+
VERSION = 1
|
|
74
|
+
USERNAME_TTL = 8 * 60 * 60
|
|
75
|
+
|
|
76
|
+
def __init__(self, client: Client, database: dict):
|
|
77
|
+
super().__init__(client.name)
|
|
78
|
+
self.cn = f"postgresql+asyncpg://{database['db_user']}:{database['db_pass']}@{database['db_host']}:{database['db_port']}/{database['db_name']}"
|
|
79
|
+
self.name = client.name
|
|
80
|
+
|
|
81
|
+
async def open(self):
|
|
82
|
+
if not await Session.filter(session_name=self.name).exists():
|
|
83
|
+
_ = await Session.create(
|
|
84
|
+
session_name=self.name,
|
|
85
|
+
dc_id=None,
|
|
86
|
+
api_id=None,
|
|
87
|
+
test_mode=None,
|
|
88
|
+
auth_key=None,
|
|
89
|
+
date=int(time.time()),
|
|
90
|
+
user_id=None,
|
|
91
|
+
is_bot=None
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def save(self):
|
|
95
|
+
await self.date(int(time.time()))
|
|
96
|
+
|
|
97
|
+
async def close(self):
|
|
98
|
+
await self.cn.close()
|
|
99
|
+
|
|
100
|
+
async def delete(self):
|
|
101
|
+
# await UpdateState.filter(session__name=self.name).delete()
|
|
102
|
+
# delete(UsernameModel).where(UsernameModel.session_name == self.name)
|
|
103
|
+
# await Peer.filter(session__name=self.name).delete() # no need if delete cascade
|
|
104
|
+
await Session.filter(session__name=self.name).delete()
|
|
105
|
+
|
|
106
|
+
async def update_peers(self, peers: list[tuple[int, int, str, str]]):
|
|
107
|
+
for peer in peers:
|
|
108
|
+
existing_peer = await Peer.get_or_none(session__name=self.name, id=peer[0])
|
|
109
|
+
|
|
110
|
+
if existing_peer:
|
|
111
|
+
existing_peer.access_hash = peer[1]
|
|
112
|
+
existing_peer.type = peer[2]
|
|
113
|
+
existing_peer.phone_number = peer[3]
|
|
114
|
+
else:
|
|
115
|
+
new_peer = await Peer.create(
|
|
116
|
+
session__name=self.name, # todo: pk ?
|
|
117
|
+
id=peer[0],
|
|
118
|
+
access_hash=peer[1],
|
|
119
|
+
type=peer[2],
|
|
120
|
+
phone_number=peer[3]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def update_usernames(self, usernames: list[tuple[int, list[str]]]):
|
|
124
|
+
for telegram_id, _ in usernames:
|
|
125
|
+
await session.execute(
|
|
126
|
+
delete(UsernameModel).where(UsernameModel.session_name == self.name,
|
|
127
|
+
UsernameModel.id == telegram_id)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
for telegram_id, user_list in usernames:
|
|
131
|
+
for username in user_list:
|
|
132
|
+
User
|
|
133
|
+
new_username = UsernameModel(session_name=self.name, id=telegram_id, username=username)
|
|
134
|
+
session.add(new_username)
|
|
135
|
+
|
|
136
|
+
await session.commit()
|
|
137
|
+
|
|
138
|
+
async def get_peer_by_id(self, peer_id_or_username):
|
|
139
|
+
async with self.session_maker() as session:
|
|
140
|
+
if isinstance(peer_id_or_username, int):
|
|
141
|
+
peer = await session.execute(
|
|
142
|
+
select(PeerModel).filter_by(session_name=self.name, id=peer_id_or_username)
|
|
143
|
+
)
|
|
144
|
+
peer = peer.scalar_one_or_none()
|
|
145
|
+
if peer is None:
|
|
146
|
+
raise KeyError(f"ID not found: {peer_id_or_username}")
|
|
147
|
+
return get_input_peer(peer.id, peer.access_hash, peer.type)
|
|
148
|
+
elif isinstance(peer_id_or_username, str):
|
|
149
|
+
r = await session.execute(
|
|
150
|
+
select(
|
|
151
|
+
PeerModel.id,
|
|
152
|
+
PeerModel.access_hash,
|
|
153
|
+
PeerModel.type,
|
|
154
|
+
PeerModel.last_update_on
|
|
155
|
+
)
|
|
156
|
+
.join(UsernameModel, UsernameModel.id == PeerModel.id)
|
|
157
|
+
.filter(UsernameModel.username == peer_id_or_username,
|
|
158
|
+
UsernameModel.session_name == self.name,
|
|
159
|
+
PeerModel.session_name == self.name)
|
|
160
|
+
.order_by(PeerModel.last_update_on.desc())
|
|
161
|
+
)
|
|
162
|
+
r = r.fetchone()
|
|
163
|
+
if r is None:
|
|
164
|
+
raise KeyError(f"Username not found: {peer_id_or_username}")
|
|
165
|
+
if len(r) == 4:
|
|
166
|
+
peer_id, access_hash, peer_type, last_update_on = r
|
|
167
|
+
else:
|
|
168
|
+
raise ValueError(f"The result does not contain the expected tuple of values. Received: {r}")
|
|
169
|
+
if last_update_on:
|
|
170
|
+
if abs(time.time() - last_update_on) > self.USERNAME_TTL:
|
|
171
|
+
raise KeyError(f"Username expired: {peer_id_or_username}")
|
|
172
|
+
return get_input_peer(peer_id, access_hash, peer_type)
|
|
173
|
+
|
|
174
|
+
else:
|
|
175
|
+
raise ValueError("peer_id_or_username must be an integer (ID) or string (Username).")
|
|
176
|
+
|
|
177
|
+
async def get_peer_by_username(self, username: str):
|
|
178
|
+
async with self.session_maker() as session:
|
|
179
|
+
peer_alias = aliased(PeerModel)
|
|
180
|
+
username_alias = aliased(UsernameModel)
|
|
181
|
+
r = await session.execute(
|
|
182
|
+
select(peer_alias.id, peer_alias.access_hash, peer_alias.type, peer_alias.last_update_on)
|
|
183
|
+
.join(username_alias, username_alias.id == peer_alias.id)
|
|
184
|
+
.filter(username_alias.username == username, username_alias.session_name == self.name)
|
|
185
|
+
.order_by(peer_alias.last_update_on.desc())
|
|
186
|
+
)
|
|
187
|
+
r = r.fetchone()
|
|
188
|
+
if r is None:
|
|
189
|
+
raise KeyError(f"Username not found: {username}")
|
|
190
|
+
|
|
191
|
+
peer_id, access_hash, peer_type, last_update_on = r
|
|
192
|
+
return get_input_peer(peer_id, access_hash, peer_type)
|
|
193
|
+
|
|
194
|
+
async def update_state(self, value: Tuple[int, int, int, int, int] = object):
|
|
195
|
+
async with self.session_maker() as session:
|
|
196
|
+
if value == object:
|
|
197
|
+
result = await session.execute(
|
|
198
|
+
select(UpdateStateModel).filter_by(session_name=self.name)
|
|
199
|
+
)
|
|
200
|
+
return result.scalars().all()
|
|
201
|
+
else:
|
|
202
|
+
if isinstance(value, int):
|
|
203
|
+
await session.execute(
|
|
204
|
+
delete(UpdateStateModel)
|
|
205
|
+
.where(UpdateStateModel.session_name == self.name, UpdateStateModel.id == value)
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
state = await session.execute(
|
|
209
|
+
select(UpdateStateModel).filter_by(session_name=self.name, id=value[0])
|
|
210
|
+
)
|
|
211
|
+
state_instance = state.scalar_one_or_none()
|
|
212
|
+
|
|
213
|
+
if state_instance:
|
|
214
|
+
state_instance.pts = value[1]
|
|
215
|
+
state_instance.qts = value[2]
|
|
216
|
+
state_instance.date = value[3]
|
|
217
|
+
state_instance.seq = value[4]
|
|
218
|
+
else:
|
|
219
|
+
state_instance = UpdateStateModel(
|
|
220
|
+
id=value[0],
|
|
221
|
+
session_name=self.name,
|
|
222
|
+
pts=value[1],
|
|
223
|
+
qts=value[2],
|
|
224
|
+
date=value[3],
|
|
225
|
+
seq=value[4]
|
|
226
|
+
)
|
|
227
|
+
session.add(state_instance)
|
|
228
|
+
|
|
229
|
+
await session.commit()
|
|
230
|
+
|
|
231
|
+
async def get_peer_by_phone_number(self, phone_number: str):
|
|
232
|
+
if not (peer := await Peer.filter(
|
|
233
|
+
session__name=self.name, phone_number=phone_number
|
|
234
|
+
).values_list("id", "access_hash", "type")):
|
|
235
|
+
raise KeyError(f"Phone number not found: {phone_number}")
|
|
236
|
+
return get_input_peer(*peer)
|
|
237
|
+
|
|
238
|
+
async def _get(self, attr: str):
|
|
239
|
+
return await Session.get(name=self.name).values_list(attr, flat=True)
|
|
240
|
+
|
|
241
|
+
async def _set(self, attr: str, value: Any):
|
|
242
|
+
await Session.update_or_create({attr: value}, name=self.name)
|
|
243
|
+
|
|
244
|
+
async def _accessor(self, attr: str, value: Any = object):
|
|
245
|
+
if value == object:
|
|
246
|
+
return await self._get(attr)
|
|
247
|
+
else:
|
|
248
|
+
await self._set(attr, value)
|
|
249
|
+
|
|
250
|
+
async def dc_id(self, value: int = object):
|
|
251
|
+
return await self._accessor('dc_id', value)
|
|
252
|
+
|
|
253
|
+
async def api_id(self, value: int = object):
|
|
254
|
+
return await self._accessor('api_id', value)
|
|
255
|
+
|
|
256
|
+
async def test_mode(self, value: bool = object):
|
|
257
|
+
return await self._accessor('test_mode', value)
|
|
258
|
+
|
|
259
|
+
async def auth_key(self, value: bytes = object):
|
|
260
|
+
return await self._accessor('auth_key', value)
|
|
261
|
+
|
|
262
|
+
async def date(self, value: int = object):
|
|
263
|
+
return await self._accessor('date', value)
|
|
264
|
+
|
|
265
|
+
async def user_id(self, value: int = object):
|
|
266
|
+
return await self._accessor('user_id', value)
|
|
267
|
+
|
|
268
|
+
async def is_bot(self, value: bool = object):
|
|
269
|
+
return await self._accessor('is_bot', value)
|
|
270
|
+
|
|
271
|
+
async def version(self, value: int = object):
|
|
272
|
+
if value == object:
|
|
273
|
+
ver = await Version.first()
|
|
274
|
+
return ver.number
|
|
275
|
+
else:
|
|
276
|
+
await Version.update_or_create({"number": value})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Binance/test_binance.py
RENAMED
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Bybit/test_bybit.py
RENAMED
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Bybit/test_bybit_p2p.py
RENAMED
|
File without changes
|
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Htx/test_htx_p2p.py
RENAMED
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/tests/_todo_refact/Wallet/test_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/_todo_req/req.mjs
RENAMED
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client/Pms/Volet/_todo_req/req.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.43.dev32 → xync_client-0.0.43.dev35}/xync_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|