xync-client 0.0.25.dev11__tar.gz → 0.0.25.dev20__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.
Files changed (75) hide show
  1. {xync_client-0.0.25.dev11/xync_client.egg-info → xync_client-0.0.25.dev20}/PKG-INFO +1 -1
  2. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestEx.py +1 -1
  3. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Agent.py +30 -13
  4. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/AuthTrait.py +1 -3
  5. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Base.py +3 -0
  6. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Ex.py +9 -9
  7. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/agent.py +183 -120
  8. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/auth.py +7 -3
  9. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/ex.py +60 -40
  10. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/pyd.py +66 -91
  11. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/pyro.py +0 -1
  12. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20/xync_client.egg-info}/PKG-INFO +1 -1
  13. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.env.sample +0 -0
  14. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.gitignore +0 -0
  15. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.pre-commit-config.yaml +0 -0
  16. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/README.md +0 -0
  17. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/makefile +0 -0
  18. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/pyproject.toml +0 -0
  19. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/setup.cfg +0 -0
  20. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestAgent.py +0 -0
  21. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestAsset.py +0 -0
  22. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestOrder.py +0 -0
  23. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Binance/test_binance.py +0 -0
  24. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  25. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  26. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Gate/test_gate.py +0 -0
  27. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
  28. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  29. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  30. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/__init__.py +0 -0
  31. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/_test_ex.py +0 -0
  32. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Asset.py +0 -0
  33. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/BaseTest.py +0 -0
  34. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/InAgent.py +0 -0
  35. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Order.py +0 -0
  36. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/__init__.py +0 -0
  37. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/binance_async.py +0 -0
  38. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/earn_api.py +0 -0
  39. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/ex.py +0 -0
  40. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/exceptions.py +0 -0
  41. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/sapi.py +0 -0
  42. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/web_c2c.py +0 -0
  43. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/__init__.py +0 -0
  44. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/agent.py +0 -0
  45. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/base.py +0 -0
  46. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/ex.py +0 -0
  47. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/req.mjs +0 -0
  48. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/sign.js +0 -0
  49. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/test/main.py +0 -0
  50. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/__init__.py +0 -0
  51. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/agent.py +0 -0
  52. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/ex.py +0 -0
  53. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/req.mjs +0 -0
  54. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/agent.py +0 -0
  55. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/ex.py +0 -0
  56. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/web_earn.py +0 -0
  57. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/web_p2p.py +0 -0
  58. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Gate/ex.py +0 -0
  59. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Gate/premarket.py +0 -0
  60. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/agent.py +0 -0
  61. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/earn.py +0 -0
  62. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/ex.py +0 -0
  63. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/KuCoin/pub.py +0 -0
  64. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/KuCoin/web.py +0 -0
  65. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Okx/ex.py +0 -0
  66. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/asset.py +0 -0
  67. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/inAgent.py +0 -0
  68. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/order.py +0 -0
  69. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/web.py +0 -0
  70. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/__init__.py +0 -0
  71. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/loader.py +0 -0
  72. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/SOURCES.txt +0 -0
  73. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/dependency_links.txt +0 -0
  74. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/requires.txt +0 -0
  75. {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: xync-client
3
- Version: 0.0.25.dev11
3
+ Version: 0.0.25.dev20
4
4
  Author-email: Mike Artemiev <mixartemev@gmail.com>
5
5
  Project-URL: Homepage, https://gitlab.com/XyncNet/client
6
6
  Project-URL: Repository, https://gitlab.com/XyncNet/client
@@ -18,7 +18,7 @@ class TestEx(BaseTest):
18
18
  @pytest.fixture
19
19
  async def clients(self) -> list[BaseClient]:
20
20
  exs = await Ex.filter(status__gt=ExStatus.plan)
21
- [await ex.fetch_related("contragents__agent") for ex in exs if ex.type_ == ExType.tg]
21
+ [await ex.fetch_related("actors__agent") for ex in exs if ex.type_ == ExType.tg]
22
22
  clients: list[BaseExClient] = [ex.client() for ex in exs]
23
23
  yield clients
24
24
  [await cl.close() for cl in clients]
@@ -3,20 +3,18 @@ from abc import abstractmethod
3
3
  from pydantic import BaseModel
4
4
  from tortoise.exceptions import IntegrityError
5
5
  from x_model import HTTPException, FailReason
6
- from xync_client.Abc.Ex import BaseExClient
6
+ from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Fiat, Agent, Pmex, Pmcur, Cred, PmexBank
7
+ from xync_schema.pydantic import FiatNew, FiatUpd, BaseAd, FiatPydIn, AdSalePydIn, AdBuyPydIn, CredPydIn, BaseOrder
7
8
 
9
+ from xync_client.Abc.Ex import BaseExClient
8
10
  from xync_client.Abc.Base import BaseClient
9
11
 
10
- from xync_client.Abc.AuthTrait import BaseAuthTrait
11
- from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Fiat, Agent, Pmex, Pmcur
12
- from xync_schema.pydantic import FiatNew, FiatUpd, BaseAd, FiatPydIn
13
12
 
14
-
15
- class BaseAgentClient(BaseClient, BaseAuthTrait):
13
+ class BaseAgentClient(BaseClient):
16
14
  def __init__(self, agent: Agent):
17
15
  self.agent: Agent = agent
18
- self.ex_client: BaseExClient = agent.contragent.ex.client(agent) # todo: really need?
19
- super().__init__(agent.contragent.ex) # , "host_p2p"
16
+ super().__init__(self.agent.actor.ex) # , "host_p2p"
17
+ self.ex_client: BaseExClient = self.ex.client()
20
18
 
21
19
  @abstractmethod
22
20
  async def start_listen(self) -> bool: ...
@@ -33,7 +31,7 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
33
31
 
34
32
  # 1: [T] Запрос на старт сделки
35
33
  @abstractmethod
36
- async def order_request(self, ad_id: int, amount: float) -> dict: ...
34
+ async def order_request(self, order: BaseOrder) -> dict: ...
37
35
 
38
36
  # async def start_order(self, order: Order) -> OrderOutClient:
39
37
  # return OrderOutClient(self, order)
@@ -79,7 +77,7 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
79
77
 
80
78
  # 25: Список реквизитов моих платежных методов
81
79
  @abstractmethod
82
- async def fiats(self) -> list: ... # {fiat.exid: {fiat}}
80
+ async def creds(self) -> list: ... # {fiat.exid: {fiat}}
83
81
 
84
82
  @staticmethod
85
83
  async def fiat_pyd2db(fiat_pyd: FiatNew | FiatUpd, uid: int, fid: int = None) -> tuple[Fiat, bool]:
@@ -97,14 +95,14 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
97
95
  # 26: Создание реквизита моего платежного метода
98
96
  @abstractmethod
99
97
  async def fiat_f2in(self, fiat_new: FiatNew) -> FiatPydIn:
100
- if not (pmcur := await Pmcur.get_or_none(cur_id=fiat_new.cur_id, pm_id=fiat_new.pm_id)):
98
+ if not (_pmcur := await Pmcur.get_or_none(cur_id=fiat_new.cur_id, pm_id=fiat_new.pm_id)):
101
99
  raise HTTPException(FailReason.body, f"No Pmcur with cur#{fiat_new.cur_id} and pm#{fiat_new.pm_id}", 404)
102
- # cred = await Cred.create({"exid": }, pmcur=pmcur, contragent=self.agent.contragent)
100
+ # cred = await Cred.create({"exid": }, pmcur=pmcur, actor=self.agent.actor)
103
101
  # df = {"detail": fiat_pyd.detail, "name": fiat_pyd.name, "amount": fiat_pyd.amount, "target": fiat_pyd.target}
104
102
  # unq = {"pmcur": pmcur, "user_id": uid}
105
103
 
106
104
  # async def fiat_new(self, fiat: FiatNew) -> Fiat:
107
- # contragent = await Contragent.get_or_create({"name": }, ex=self.ex_client.ex, exid=self.agent.contragent.exid)
105
+ # actor = await Actor.get_or_create({"name": }, ex=self.ex_client.ex, exid=self.agent.actor.exid)
108
106
  # FiatPydIn()
109
107
  # fiat_db: Fiat = (await self.fiat_pyd2db(fiat, self.agent.user_id))[0]
110
108
  # if not (fiatex := Fiatex.get_or_none(fiat=fiat_db, ex=self.agent.ex)):
@@ -182,3 +180,22 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
182
180
  # 39: Балансы моих монет
183
181
  @abstractmethod
184
182
  async def my_assets(self) -> dict: ...
183
+
184
+ # Сохранение объявления (с Pm/Cred-ами) в бд
185
+ async def ad_pydin2db(self, ad_pydin: AdSalePydIn | AdBuyPydIn) -> Ad:
186
+ ad_db = await self.ex_client.ad_pydin2db(ad_pydin)
187
+ await ad_db.creds.add(*getattr(ad_pydin, "creds_", []))
188
+ return ad_db
189
+
190
+ @staticmethod
191
+ async def cred_pydin2db(cred: CredPydIn) -> Cred:
192
+ df, unq = cred.args()
193
+ cred_db, _ = await Cred.update_or_create(df, **unq)
194
+ if cred.banks:
195
+ await cred_db.banks.add(*[await PmexBank.get(exid=b) for b in cred.banks])
196
+ return cred_db
197
+
198
+ @staticmethod
199
+ async def fiat_cred2db(cred: Cred) -> Fiat:
200
+ fiat, _ = await Fiat.get_or_create(cred=cred)
201
+ return fiat
@@ -6,11 +6,9 @@ from aiohttp.http_exceptions import HttpProcessingError
6
6
  from x_client.aiohttp import Client
7
7
  from xync_schema.models import Agent
8
8
 
9
- # from x_client.aiohttp import Client
10
-
11
9
 
12
10
  class BaseAuthTrait(Client):
13
- agent: Agent
11
+ agent: Agent = None
14
12
 
15
13
  @abstractmethod
16
14
  async def _get_auth_hdrs(self) -> dict[str, str]: ...
@@ -8,5 +8,8 @@ MapOfIdsList = dict[int | str, list[int | str]]
8
8
 
9
9
 
10
10
  class BaseClient(Client):
11
+ ex: Ex
12
+
11
13
  def __init__(self, ex: Ex, attr: str = "host_p2p"):
14
+ self.ex = ex
12
15
  super().__init__(getattr(ex, attr))
@@ -2,8 +2,8 @@ import logging
2
2
  import re
3
3
  from abc import abstractmethod
4
4
 
5
- from xync_schema.pydantic import AdPydIn, PmPyd, BaseAd, CurEpyd, CoinEpyd
6
- from xync_schema.models import Ex, Coin, Cur, Pm, Pmex, Curex, Pmcur, Coinex, PmexBank, Ad, Agent
5
+ from xync_schema.pydantic import BaseAdPydIn, PmPyd, BaseAd, CurEpyd, CoinEpyd
6
+ from xync_schema.models import Ex, Coin, Cur, Pm, Pmex, Curex, Pmcur, Coinex, PmexBank, Ad
7
7
 
8
8
  from xync_client.Abc.Base import BaseClient, DictOfDicts, MapOfIdsList
9
9
 
@@ -21,8 +21,7 @@ class BaseExClient(BaseClient):
21
21
  "GTB Bank (Guarantee Trust Bank)": "GTBank",
22
22
  }
23
23
 
24
- def __init__(self, ex: Ex, _: Agent = None):
25
- self.ex: Ex = ex
24
+ def __init__(self, ex: Ex):
26
25
  self.acronyms: dict[str, str] = {} # for pm norm
27
26
  super().__init__(ex) # , "host_p2p"
28
27
 
@@ -66,7 +65,7 @@ class BaseExClient(BaseClient):
66
65
 
67
66
  # Преобразрование объекта объявления из формата биржи в формат xync
68
67
  @abstractmethod
69
- async def ad_epyd2pydin(self, ad: BaseAd) -> AdPydIn: ... # my_uid: for MyAd
68
+ async def ad_epyd2pydin(self, ad: BaseAd) -> BaseAdPydIn: ... # my_uid: for MyAd
70
69
 
71
70
  def _pmnorm(self, s: str) -> str:
72
71
  def get_and_remove_acro(title: str) -> str:
@@ -196,8 +195,9 @@ class BaseExClient(BaseClient):
196
195
  await Coinex.bulk_create(coinexs, update_fields=["minimum"], on_conflict=["coin_id", "ex_id"])
197
196
 
198
197
  # Сохранение чужого объявления (с Pm-ами) в бд
199
- @staticmethod
200
- async def ad_pydin2db(ad_pydin: AdPydIn) -> Ad:
201
- ad_db, _ = await Ad.update_or_create(ad_pydin.model_dump(exclude_none=True), id=ad_pydin.id)
202
- await ad_db.pms.add(*getattr(ad_pydin, "pms_", ad_pydin.fiats_))
198
+ async def ad_pydin2db(self, ad_pydin: BaseAdPydIn) -> Ad:
199
+ df, unq = ad_pydin.args()
200
+ ad_db, _ = await Ad.update_or_create(df, **unq)
201
+ if getattr(ad_pydin, "pms_", None): # if it ListItem, not Full One # todo: remove?
202
+ await ad_db.pms.add(*ad_pydin.pms_)
203
203
  return ad_db
@@ -1,23 +1,28 @@
1
1
  import logging
2
2
  from asyncio import run
3
3
  from enum import StrEnum
4
-
4
+ from tg_auth import UserStatus
5
5
  from x_model import init_db, HTTPException, FailReason
6
+ from xync_client.TgWallet.ex import ExClient
6
7
  from xync_schema import models
7
8
 
8
9
  from xync_client.TgWallet.pyd import (
9
- Banks,
10
- FiatEpyd,
10
+ CredEpyd,
11
11
  Attrs,
12
12
  AttrsV2,
13
13
  OneAdTakerMakerSale,
14
- OneAdMakerPurchase,
15
- AdMakerPurchase,
14
+ OneAdMakerBuy,
15
+ AdMakerBuy,
16
16
  AdMakerSale,
17
17
  AdMakerNewSale,
18
- _TakerOne,
19
18
  _AdNew,
20
19
  FiatEpydIn,
20
+ _PmsTrait,
21
+ OneAdTakerBuy,
22
+ OrderEpyd,
23
+ AdMakerNewBuy,
24
+ OrderEpydIn,
25
+ AvailableVolume,
21
26
  )
22
27
  from xync_client.loader import PG_DSN
23
28
  from xync_schema.enums import AdStatus
@@ -33,20 +38,25 @@ from xync_schema.models import (
33
38
  Ad,
34
39
  Pmcur,
35
40
  Cred,
36
- PmexBank,
37
41
  Agent,
38
42
  Curex,
39
43
  Coinex,
44
+ Pm,
45
+ Order,
46
+ Actor,
40
47
  )
41
- from xync_schema.pydantic import FiatNew, FiatPydIn
48
+ from xync_schema.pydantic import FiatNew, BaseAdPydIn, AdBuyPydIn, AdSalePydIn, CredPydIn, OrderPydIn
42
49
 
43
50
  from xync_client.Abc.Agent import BaseAgentClient
44
51
 
45
52
 
46
53
  class Exceptions(StrEnum):
54
+ ORDER_KYC = "FIAT_COUNTRY_NOT_SUPPORTED_BY_USER_KYC_COUNTRY"
47
55
  PM_KYC = "OFFER_FIAT_COUNTRY_NOT_SUPPORTED_BY_USER_KYC_COUNTRY"
48
56
  ROUND = "ROUNDING_IS_NOT_SUPPORTED"
49
57
  CUR = "CURRENCY_IS_NOT_SUPPORTED_BY_PAYMENT_METHOD"
58
+ MAX_ADS = "ACTIVE_OFFER_COUNT_LIMIT_REACHED"
59
+ INACTIVE_AD = "OFFER_ILLEGAL_STATE"
50
60
 
51
61
 
52
62
  # class Status(IntEnum):
@@ -54,6 +64,8 @@ class Exceptions(StrEnum):
54
64
 
55
65
 
56
66
  class AgentClient(BaseAgentClient, AuthClient):
67
+ ex_client: ExClient
68
+
57
69
  # 0: Получение ордеров в статусе status, по монете coin, в валюте coin, в направлении is_sell
58
70
  async def orders(
59
71
  self, status: OrderStatus = OrderStatus.created, coin: Coin = None, cur: Cur = None, is_sell: bool = None
@@ -69,48 +81,74 @@ class AgentClient(BaseAgentClient, AuthClient):
69
81
  orders = await self._post("/p2p/public-api/v2/offer/order/get", {"orderId": oid})
70
82
  return orders["data"]
71
83
 
72
- # 1: [T] Запрос на старт сделки
73
- async def order_request(self, ad_id: int, amount: float = None) -> dict | bool:
74
- await self.agent.fetch_related("ex", "ex__agents")
75
- ad: _TakerOne = await self.ex_client.ad(ad_id=ad_id)
76
- fiats: list[FiatEpyd] = await self.fiats()
77
- fiats_pms: dict[str, int] = {fiat.paymentMethod.code: fiat.id for fiat in fiats}
78
- pmfs: dict[str, int]
79
- if ad.is_sell:
80
- pmfs = {pd.paymentMethod.code: pd.id for pd in ad.paymentDetails if pd.paymentMethod.code in fiats_pms}
81
- fiatex = (
82
- await Cred.filter(
83
- fiat__user_id=self.agent.user_id,
84
- ex=self.ex_client.ex,
85
- fiat__pmcur__pm__pmexs__exid__in=pmfs.keys(),
86
- )
87
- .order_by("-fiat__amount")
88
- .first()
89
- .prefetch_related("fiat__pmcur")
84
+ async def order_ad2epydin(self, ad: Ad, amount: float, cred_id: int = None) -> OrderEpydIn:
85
+ if not cred_id: # i am taker
86
+ iam_maker = self.agent.actor_id == ad.maker_id
87
+ iam_seller = ad.direction.sell == iam_maker
88
+ cred_filter = (
89
+ {"actor__agent__user_id": self.agent.user_id}
90
+ if iam_seller
91
+ else { # its a buy ad, i am taker
92
+ "actor": ad.maker
93
+ }
90
94
  )
91
- pmex = await Pmex.get(ex=self.ex_client.ex, pm_id=fiatex.fiat.pmcur.pm_id)
92
- # else:
93
- # pmfs = {pm.code: fid for pm in ad.paymentMethods if (fid := fiats_pms.get(pm.code))}
94
- # f = await Cred.filter(
95
- # fiat__user_id=self.agent.user_id, ex_id=self.ex_client.ex_id, fiat__pmcur__pm__pmexs__exid__in=pmfs.keys()
96
- # ).order_by("-fiat__amount").first()
97
- # p = max(pmfs, key=lambda x: fiats_pms)
98
- req_body = {
99
- "offerId": ad_id,
100
- "paymentDetailsId": pmfs[pmex.exid], # fiats_pms[pid],
101
- "amount": {"currencyCode": ad.orderAmountLimits.currencyCode, "amount": amount},
102
- "type": ad.type,
103
- }
104
- request = await self._post("/p2p/public-api/v2/offer/order/create-by-amount", req_body)
95
+ await Cred.filter(
96
+ **cred_filter,
97
+ pmcur__pms__in=ad.pms,
98
+ # todo: ordering and filtering by fiat.amount-target
99
+ ).first() if iam_seller else 0
100
+ await ad.fetch_related("direction__pairex__pair__cur")
101
+ return OrderEpydIn(
102
+ offerId=ad.exid,
103
+ amount=AvailableVolume(currencyCode=ad.direction.pairex.pair.cur.ticker, amount=str(int(amount))),
104
+ type="SALE" if ad.direction.sell else "PURCHASE",
105
+ paymentDetailsId=cred_id,
106
+ )
107
+
108
+ # 1: [T] Запрос на старт сделки
109
+ async def order_request(self, req: OrderEpydIn) -> OrderEpyd | dict:
110
+ request = await self._post("/p2p/public-api/v2/offer/order/create-by-amount", req.model_dump(exclude_none=True))
105
111
  if r := request.get("data"):
106
- return await self._post("/p2p/public-api/v2/offer/order/confirm", {"orderId": r["id"], "type": ad.type})
112
+ res = await self._post("/p2p/public-api/v2/offer/order/confirm", {"orderId": r["id"], "type": req.type})
113
+ if res.get("status") == "SUCCESS":
114
+ return OrderEpyd(**r)
107
115
  logging.error(request)
108
116
  return request
109
117
 
118
+ async def order_epyd2pydin(self, order: OrderEpyd) -> OrderPydIn:
119
+ ad = await Ad.get(exid=order.offerId, direction__pairex__ex=self.ex)
120
+ cred = await Cred.get(exid=order.paymentDetails.id, actor__ex=self.ex)
121
+ iam_maker = self.agent.actor == ad.maker
122
+ taker = (
123
+ (await Actor.get(exid=(order.seller if order.is_sell == iam_maker else order.buyer).userId, ex=self.ex))
124
+ if iam_maker
125
+ else self.agent.actor
126
+ )
127
+ return OrderPydIn(
128
+ exid=order.id,
129
+ amount=order.amount.amount,
130
+ maker_topic=None,
131
+ taker_topic=None,
132
+ status=OrderStatus.created,
133
+ created_at=order.createDateTime,
134
+ payed_at=None,
135
+ confirmed_at=None,
136
+ appealed_at=None,
137
+ ad=ad,
138
+ cred=cred,
139
+ taker=taker,
140
+ )
141
+
142
+ @staticmethod
143
+ async def order_pydin2db(order: OrderPydIn) -> Order:
144
+ df, unq = order.args()
145
+ order_db, _ = await Order.update_or_create(df, **unq)
146
+ return order_db
147
+
110
148
  # # # FIATS # # #
111
149
  def fiat_args2ex_pyd(
112
150
  self, exid: int | str, cur: str, detail: str, name: str, fid: int, typ: str, extra=None
113
- ) -> FiatEpyd:
151
+ ) -> CredEpyd:
114
152
  fiat = self.fiat_pyd(
115
153
  paymentMethodCode=exid,
116
154
  currencyCode=cur,
@@ -141,11 +179,11 @@ class AgentClient(BaseAgentClient, AuthClient):
141
179
  }
142
180
 
143
181
  # 25: Список реквизитов моих платежных методов
144
- async def fiats(self) -> list[FiatEpyd]:
182
+ async def creds(self) -> list[CredEpyd]:
145
183
  resp = await self._post("/p2p/public-api/v3/payment-details/get/by-user-id")
146
- return [FiatEpyd(**fiat) for fiat in resp["data"]]
184
+ return [CredEpyd(**fiat) for fiat in resp["data"]]
147
185
 
148
- async def _fiat_epyd2pydin(self, fiat: FiatEpyd) -> FiatPydIn:
186
+ async def cred_epyd2pydin(self, fiat: CredEpyd) -> CredPydIn:
149
187
  if not (pmex := await Pmex.get_or_none(exid=fiat.paymentMethod.code, ex=self.ex_client.ex)):
150
188
  raise HTTPException(
151
189
  FailReason.body, f"No Pmex {fiat.paymentMethod.code} on ex#{self.ex_client.ex.name}", 404
@@ -154,36 +192,26 @@ class AgentClient(BaseAgentClient, AuthClient):
154
192
  raise HTTPException(
155
193
  FailReason.body, f"No Pmcur with cur#{fiat.currency} and pm#{fiat.paymentMethod.code}", 404
156
194
  )
157
- ftx = FiatPydIn(
158
- id=fiat.id,
159
- detail="",
160
- name=fiat.name,
161
- amount=0,
162
- user_id=self.agent.user_id,
195
+ cred_pin = CredPydIn(
196
+ exid=fiat.id,
163
197
  pmcur=pmcur,
198
+ actor=self.agent.actor,
199
+ name=fiat.name,
164
200
  )
165
201
  for val in fiat.attributes.values:
166
- if val.name != "BANKS":
167
- ftx.detail = val.value
202
+ if val.name == "BANKS":
203
+ cred_pin.banks = [b.code for b in val.value]
168
204
  else:
169
- val: Banks
170
- ftx.banks = [b.code for b in val.value]
171
- return ftx
205
+ cred_pin.detail = val.value
206
+ return cred_pin
172
207
 
173
208
  # 25: Список реквизитов моих платежных методов
174
- async def set_fiatexs(self) -> list[Cred]:
175
- fiat_epyds = await self.fiats()
176
- fiat_xpyds: list[FiatPydIn] = [await self._fiat_epyd2pydin(f) for f in fiat_epyds]
177
- fxs: list[Cred] = []
178
- for fx in fiat_xpyds:
179
- df, unq = fx.args()
180
- cred_db, _ = await Cred.update_or_create(
181
- {"name": fx.cred.name, "detail": fx.cred.detail}, exid=fx.id, ex=self.ex_client.ex
182
- )
183
- fiat_db, _ = await Fiat.update_or_create(df, **unq)
184
- banks = [await PmexBank.get(exid=bn) for bn in fx.banks]
185
- await cred_db.banks.add(*banks)
186
- return fxs
209
+ async def set_fiats(self) -> list[Fiat]:
210
+ creds_epyd: list[CredEpyd] = await self.creds()
211
+ creds_pyd_in: list[CredPydIn] = [await self.cred_epyd2pydin(f) for f in creds_epyd]
212
+ creds_db = [await self.cred_pydin2db(crd) for crd in creds_pyd_in]
213
+ fiats_db = [await self.fiat_cred2db(cd) for cd in creds_db]
214
+ return fiats_db
187
215
 
188
216
  # 26: Создание реквизита моего платежного метода
189
217
  async def fiat_new(self, fiat: FiatNew) -> Fiat:
@@ -201,7 +229,7 @@ class AgentClient(BaseAgentClient, AuthClient):
201
229
  attributes={"values": vals},
202
230
  )
203
231
  add_fiat = await self._post("/p2p/public-api/v3/payment-details/create", fiat_in.model_dump())
204
- FiatEpyd(**add_fiat["data"])
232
+ CredEpyd(**add_fiat["data"])
205
233
  cred, _ = await Cred.update_or_create(
206
234
  {"name": fiat.name, "detail": fiat.detail}, ex=self.ex_client.ex, exid=add_fiat["data"]["id"]
207
235
  )
@@ -246,10 +274,22 @@ class AgentClient(BaseAgentClient, AuthClient):
246
274
  del_fiat = await self._post("/p2p/public-api/v3/payment-details/delete", {"id": fiat_id})
247
275
  return del_fiat
248
276
 
277
+ async def ad_epyd2pydin(self, ad_: OneAdTakerMakerSale | OneAdMakerBuy | OneAdTakerBuy) -> AdBuyPydIn | AdSalePydIn:
278
+ ad_in: BaseAdPydIn = await self.ex_client.ad_common_epyd2pydin(ad_)
279
+ ad_in.maker = self.agent.actor
280
+ if isinstance(ad_, _PmsTrait):
281
+ return AdBuyPydIn(
282
+ **ad_in.model_dump(),
283
+ pms_=await Pm.filter(pmexs__ex=self.ex_client.ex, pmexs__exid__in=[p.code for p in ad_.paymentMethods]),
284
+ )
285
+ creds_pin: list[CredPydIn] = [await self.cred_epyd2pydin(c) for c in ad_.paymentDetails]
286
+ creds_ = [(await Cred.update_or_create((a := cp.args())[0], **a[1]))[0] for cp in creds_pin]
287
+ return AdSalePydIn(**ad_in.model_dump(), creds_=creds_)
288
+
249
289
  # 29: Список моих объявлений
250
- async def my_ads(self, status: AdStatus = None) -> list[AdMakerPurchase | AdMakerSale]:
251
- def model(ad: dict) -> (AdMakerPurchase | AdMakerSale).__class__:
252
- return AdMakerSale if ad["type"] == "SALE" else AdMakerPurchase
290
+ async def my_ads(self, status: AdStatus = None) -> list[AdMakerBuy | AdMakerSale]:
291
+ def model(ad: dict) -> (AdMakerBuy | AdMakerSale).__class__:
292
+ return AdMakerSale if ad["type"] == "SALE" else AdMakerBuy
253
293
 
254
294
  mapping = {AdStatus.defActive: "INACTIVE", AdStatus.active: "ACTIVE"}
255
295
  ads = await self._post(
@@ -259,18 +299,46 @@ class AgentClient(BaseAgentClient, AuthClient):
259
299
  return [model(ad)(**ad) for ad in ads["data"] if not status or (status and ad["status"] == mapping[status])]
260
300
 
261
301
  # 43: Моя объява по id
262
- async def my_ad(self, ad_id: int) -> OneAdMakerPurchase | OneAdTakerMakerSale:
302
+ async def my_ad(self, ad_id: int) -> OneAdMakerBuy | OneAdTakerMakerSale:
263
303
  ad = await self._post("/p2p/public-api/v2/offer/get-user-own", {"offerId": ad_id})
264
304
  ad: dict = ad["data"]
265
- assert ad["user"]["userId"] == self.agent.contragent.exid, "Not your ad"
266
- model = OneAdTakerMakerSale if ad["type"] == "SALE" else OneAdMakerPurchase
305
+ assert ad["user"]["userId"] == self.agent.actor.exid, "Not your ad"
306
+ model = OneAdTakerMakerSale if ad["type"] == "SALE" else OneAdMakerBuy
267
307
  return model(**ad)
268
308
 
309
+ async def ad_f2ein(
310
+ self, coin: Coin, cur: Cur, is_sell: bool, vol: int = None, cur_min: int = None
311
+ ) -> AdMakerNewSale | AdMakerNewBuy:
312
+ coinex = await Coinex.get(coin=coin, ex=self.ex)
313
+ curex = await Curex.get(ex=self.ex, cur=cur)
314
+ creds = await Cred.filter(actor__agent__user_id=self.agent.user_id, pmcur__cur=cur).limit(5)
315
+ # todo: ordering and filtering by fiat.amount-target
316
+ ad_ein = _AdNew(
317
+ type="SALE" if is_sell else "PURCHASE",
318
+ initVolume=AvailableVolume(currencyCode=coinex.exid, amount=str(vol or coinex.minimum)),
319
+ orderRoundingRequired=curex.rounding_scale is not None,
320
+ price={
321
+ "type": "FLOATING",
322
+ "baseCurrencyCode": coinex.exid,
323
+ "quoteCurrencyCode": curex.exid,
324
+ "value": "120" if is_sell else "80",
325
+ },
326
+ orderAmountLimits={"min": str(cur_min or curex.minimum)},
327
+ paymentConfirmTimeout="PT15M",
328
+ comment="tst",
329
+ )
330
+ if ad_ein.type == "SALE":
331
+ ad_ein = AdMakerNewSale(**ad_ein.model_dump(exclude_none=True), paymentDetailsIds=[c.exid for c in creds])
332
+ else:
333
+ pmexs = await Pmex.filter(ex=self.agent.actor.ex, pm__pmcurs__id__in=[c.pmcur_id for c in creds])
334
+ ad_ein = AdMakerNewBuy(**ad_ein.model_dump(exclude_none=True), paymentMethodCodes=[p.exid for p in pmexs])
335
+ return ad_ein
336
+
269
337
  # 30: Создание объявления
270
- async def ad_new(self, ad: _AdNew) -> OneAdMakerPurchase | OneAdTakerMakerSale:
338
+ async def ad_new(self, ad: _AdNew) -> OneAdMakerBuy | OneAdTakerMakerSale:
271
339
  create = await self._post("/p2p/public-api/v2/offer/create", ad.model_dump())
272
340
  if res := create.get("data"):
273
- return OneAdTakerMakerSale(**res) if res["type"] == "SALE" else OneAdMakerPurchase(**res)
341
+ return OneAdTakerMakerSale(**res) if res["type"] == "SALE" else OneAdMakerBuy(**res)
274
342
  raise Exception(create)
275
343
 
276
344
  # 31: Редактирование объявления
@@ -312,7 +380,7 @@ class AgentClient(BaseAgentClient, AuthClient):
312
380
 
313
381
  # 33: Вкл/выкл объявления
314
382
  async def ad_switch(self, ad_id: int, active: bool) -> bool:
315
- ad: OneAdMakerPurchase | OneAdTakerMakerSale = await self.my_ad(ad_id)
383
+ ad: OneAdMakerBuy | OneAdTakerMakerSale = await self.my_ad(ad_id)
316
384
  pre = "" if active else "de"
317
385
  switch = await self._post(f"/p2p/public-api/v2/offer/{pre}activate", {"type": ad.type, "offerId": ad_id})
318
386
  return switch["status"] == "SUCCESS"
@@ -353,50 +421,45 @@ class AgentClient(BaseAgentClient, AuthClient):
353
421
 
354
422
  async def main():
355
423
  await init_db(PG_DSN, models, True)
356
- agents: tuple[Agent, Agent] = (
357
- await Agent.filter(ex_id=34, auth__isnull=False).order_by("user_id").limit(2).prefetch_related("ex")
424
+ maker: Agent
425
+ taker: Agent
426
+ maker, taker = (
427
+ await Agent.filter(actor__ex_id=34, auth__isnull=False, user__status__gte=UserStatus.MEMBER)
428
+ .order_by("user_id")
429
+ .limit(2)
430
+ .prefetch_related("actor__ex")
358
431
  )
359
- maker, taker = agents
360
432
  mcl: AgentClient = maker.client()
361
433
  tcl: AgentClient = taker.client()
362
- # msale_ad: list[AdEpyd] = await mcl.ex_client.ad(1941011)
363
- # mbuy_ad: list[AdEpyd] = await mcl.ex_client.ad(1813220)
364
- # fiatexs = await tcl.set_fiatexs()
365
- # fiatexs = await mcl.set_fiatexs()
366
- # await mcl.my_ads()
367
- coinex = await Coinex.get(coin__ticker="USDT")
368
- taker_fiats = await Fiat.filter(user_id=taker.user_id)
369
- maker_fiats = await Fiat.filter(user_id=maker.user_id)
370
- common_mfs: set[Fiat] = {mf for mf in maker_fiats if mf.pmcur_id in {tf.pmcur_id for tf in taker_fiats}}
371
- mfs: list[Fiat] = sorted(common_mfs, key=lambda x: x.amount, reverse=True)[:5]
372
- curex = await Curex.get(ex=maker.ex, cur_id=(await mfs[0].pmcur).cur_id)
373
- ad_ein = AdMakerNewSale(
374
- type="SALE",
375
- initVolume={"currencyCode": coinex.exid, "amount": f"{coinex.minimum}"},
376
- orderRoundingRequired=curex.rounding_scale is not None,
377
- price={
378
- "type": "FLOATING",
379
- "baseCurrencyCode": coinex.exid,
380
- "quoteCurrencyCode": curex.exid,
381
- "value": "120",
382
- },
383
- orderAmountLimits={"min": str(curex.minimum)},
384
- paymentConfirmTimeout="PT15M",
385
- comment="test1",
386
- )
387
- if ad_ein.type == "SALE":
388
- mfiatexids = [(await Cred.get(ex=maker.ex, fiat=mf)).exid for mf in mfs]
389
- ad_ein.paymentDetailsIds = mfiatexids[:1]
390
- else:
391
- pmexsids = [(await Pmex.get(ex=maker.ex, pm__pmcurs__id=mf.pmcur_id)).exid for mf in mfs]
392
- ad_ein.paymentMethodCodes = pmexsids[:1]
393
-
394
- mad = await mcl.ad_new(ad_ein)
395
- await mcl.ad_switch(mad.id, True)
396
- await tcl.order_request(mad.id, float(mad.orderAmountLimits.min))
397
- ad_in = await mcl.ex_client.ad_epyd2pydin(mad)
398
- ad_db = await Ad.create(**ad_in.model_dump(exclude_none=True))
399
- print(ad_db)
434
+ # fiats = await tcl.set_fiats()
435
+ # fiats = await mcl.set_fiats()
436
+ # my_ads = await mcl.my_ads()
437
+ # my_ads_in = [await mcl.ad_epyd2pydin(ma) for ma in my_ads]
438
+ # _my_ads_db = [await mcl.ad_pydin2db(ma) for ma in my_ads_in]
439
+
440
+ coin = await Coin.get(ticker="USDT")
441
+ cur = await Cur.get(ticker="RUB")
442
+ ad_sell_ein = await tcl.ad_f2ein(coin, cur, True)
443
+ ad_buy_ein = await mcl.ad_f2ein(coin, cur, False)
444
+ sad = await tcl.ad_new(ad_sell_ein)
445
+ bad = await mcl.ad_new(ad_buy_ein)
446
+ await tcl.ad_switch(sad.id, True)
447
+ await mcl.ad_switch(bad.id, True)
448
+ sad_in = await tcl.ad_epyd2pydin(sad)
449
+ sad_db = await Ad.create(**sad_in.model_dump(exclude_none=True))
450
+ bad_in = await mcl.ad_epyd2pydin(bad)
451
+ await Ad.create(**bad_in.model_dump(exclude_none=True))
452
+
453
+ order_epin: OrderEpydIn = await tcl.order_ad2epydin(sad_db, float(sad.orderAmountLimits.min))
454
+ new_order: OrderEpyd = await tcl.order_request(order_epin)
455
+ order_pin: OrderPydIn = await tcl.order_epyd2pydin(new_order)
456
+ _order_db = await tcl.order_pydin2db(order_pin)
457
+
458
+ # order_epin: OrderEpydIn = await tcl.order_ad2epydin(ad_db, float(mad.orderAmountLimits.min), cred_ids[0])
459
+ # new_order: OrderEpyd = await tcl.order_request(order_epin)
460
+ # order_pin: OrderPydIn = await tcl.order_epyd2pydin(new_order)
461
+ # _order_db = await tcl.order_pydin2db(order_pin)
462
+
400
463
  await tcl.close(), await mcl.close()
401
464
 
402
465
 
@@ -1,15 +1,19 @@
1
1
  from x_client.http import Client as HttpClient
2
+ from xync_schema.models import Agent
2
3
 
3
4
  from xync_client.Abc.AuthTrait import BaseAuthTrait
5
+ from xync_client.Abc.Base import BaseClient
4
6
  from xync_client.TgWallet.pyro import PyroClient
5
7
 
6
8
 
7
- class AuthClient(BaseAuthTrait):
9
+ class AuthClient(BaseAuthTrait, BaseClient):
8
10
  async def _get_auth_hdrs(self) -> dict[str, str]:
11
+ if not self.agent:
12
+ self.agent = await Agent.filter(actor__ex=self.ex, auth__isnull=False).prefetch_related("actor").first()
9
13
  pyro = PyroClient(self.agent)
10
14
  init_data = await pyro.get_init_data()
11
15
  tokens = HttpClient("walletbot.me")._post("/api/v1/users/auth/", init_data)
12
- self.agent.exid = tokens["user_id"]
13
- await self.agent.save()
16
+ self.agent.actor.exid = tokens["user_id"]
17
+ await self.agent.actor.save()
14
18
  pref = "" if self.__class__.__name__ == "AssetClient" else "Bearer "
15
19
  return {"Wallet-Authorization": tokens["jwt"], "Authorization": pref + tokens["value"]}