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.
- {xync_client-0.0.25.dev11/xync_client.egg-info → xync_client-0.0.25.dev20}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestEx.py +1 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Agent.py +30 -13
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/AuthTrait.py +1 -3
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Base.py +3 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Ex.py +9 -9
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/agent.py +183 -120
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/auth.py +7 -3
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/ex.py +60 -40
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/pyd.py +66 -91
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/pyro.py +0 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20/xync_client.egg-info}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.env.sample +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.gitignore +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/README.md +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/makefile +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/pyproject.toml +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/setup.cfg +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestAsset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/TestOrder.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/InAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BingX/test/main.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/BitGet/req.mjs +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Gate/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/KuCoin/pub.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client/loader.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/SOURCES.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/requires.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev20}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -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("
|
|
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
|
|
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.
|
|
19
|
-
|
|
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,
|
|
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
|
|
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 (
|
|
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,
|
|
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
|
-
#
|
|
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]: ...
|
|
@@ -2,8 +2,8 @@ import logging
|
|
|
2
2
|
import re
|
|
3
3
|
from abc import abstractmethod
|
|
4
4
|
|
|
5
|
-
from xync_schema.pydantic import
|
|
6
|
-
from xync_schema.models import Ex, Coin, Cur, Pm, Pmex, Curex, Pmcur, Coinex, PmexBank, Ad
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
ad_db, _ = await Ad.update_or_create(
|
|
202
|
-
|
|
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
|
-
|
|
10
|
-
FiatEpyd,
|
|
10
|
+
CredEpyd,
|
|
11
11
|
Attrs,
|
|
12
12
|
AttrsV2,
|
|
13
13
|
OneAdTakerMakerSale,
|
|
14
|
-
|
|
15
|
-
|
|
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,
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
) ->
|
|
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
|
|
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 [
|
|
184
|
+
return [CredEpyd(**fiat) for fiat in resp["data"]]
|
|
147
185
|
|
|
148
|
-
async def
|
|
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
|
-
|
|
158
|
-
|
|
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
|
|
167
|
-
|
|
202
|
+
if val.name == "BANKS":
|
|
203
|
+
cred_pin.banks = [b.code for b in val.value]
|
|
168
204
|
else:
|
|
169
|
-
val
|
|
170
|
-
|
|
171
|
-
return ftx
|
|
205
|
+
cred_pin.detail = val.value
|
|
206
|
+
return cred_pin
|
|
172
207
|
|
|
173
208
|
# 25: Список реквизитов моих платежных методов
|
|
174
|
-
async def
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
for
|
|
179
|
-
|
|
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
|
-
|
|
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[
|
|
251
|
-
def model(ad: dict) -> (
|
|
252
|
-
return AdMakerSale if ad["type"] == "SALE" else
|
|
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) ->
|
|
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.
|
|
266
|
-
model = OneAdTakerMakerSale if ad["type"] == "SALE" else
|
|
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) ->
|
|
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
|
|
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:
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
#
|
|
363
|
-
#
|
|
364
|
-
#
|
|
365
|
-
#
|
|
366
|
-
# await mcl.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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"]}
|