xync-client 0.0.162__py3-none-any.whl → 0.0.172__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xync_client/Abc/Order.py CHANGED
@@ -1,8 +1,7 @@
1
1
  from abc import abstractmethod
2
2
 
3
- from xync_schema.models import Order
4
-
5
- from xync_client.Bybit.agent import AgentClient
3
+ from xync_client.Abc.Agent import BaseAgentClient
4
+ from xync_schema.models import Order, PmEx, CredEx
6
5
 
7
6
 
8
7
  class BaseOrderClient:
@@ -10,32 +9,32 @@ class BaseOrderClient:
10
9
  im_maker: bool
11
10
  im_seller: bool
12
11
 
13
- def __init__(self, order: Order, agent_client: AgentClient):
12
+ def __init__(self, order: Order, agent_client: BaseAgentClient):
14
13
  self.order = order
15
14
  self.im_maker = order.taker_id != agent_client.actor.id # or order.ad.agent_id == agent.id
16
15
  self.im_seller = order.ad.pair_side.is_sell and self.im_maker
17
16
  self.agent_client = agent_client
18
17
 
19
- # 2: [T] Отмена запроса на сделку
20
- @abstractmethod
21
- async def cancel_request(self) -> Order: ...
22
-
23
- # 3: [M] Одобрить запрос на сделку
24
- @abstractmethod
25
- async def accept_request(self) -> bool: ...
26
-
27
- # 4: [M] Отклонить запрос на сделку
28
- @abstractmethod
29
- async def reject_request(self) -> bool: ...
30
-
31
18
  # 5: [B] Перевод сделки в состояние "оплачено", c отправкой чека
19
+ async def mark_payed(self, cred_id: int = None, receipt: bytes = None):
20
+ cred_id = cred_id or self.order.cred_id
21
+ pmex = await PmEx.get(pm__pmcurs__id=self.order.cred.pmcur_id, ex=self.agent_client.ex_client.ex)
22
+ credex = await CredEx.get(cred_id=cred_id, ex=self.agent_client.ex_client.ex)
23
+ await self._mark_payed(credex.exid, pmex.exid, receipt)
24
+
32
25
  @abstractmethod
33
- async def mark_payed(self, receipt: bytes = None): ...
26
+ async def _mark_payed(self, credex_exid: int = None, pmex_exid: int | str = None, receipt: bytes = None): ...
34
27
 
35
28
  # 6: [B] Отмена сделки
36
29
  @abstractmethod
37
30
  async def cancel_order(self) -> bool: ...
38
31
 
32
+ # 6: Запрос отмены (оплаченная контрагентом продажа)
33
+ async def cancel_request(self) -> bool: ...
34
+
35
+ # 6: Одобрение запроса на отмену (оплаченная мной покупка)
36
+ async def cancel_accept(self): ...
37
+
39
38
  # 7: [S] Подтвердить получение оплаты
40
39
  @abstractmethod
41
40
  async def confirm(self) -> bool: ...
xync_client/Abc/xtype.py CHANGED
@@ -1,9 +1,14 @@
1
+ import re
1
2
  from typing import Literal, ClassVar
2
3
 
3
- from pydantic import BaseModel, model_validator, model_serializer
4
+ from pydantic import BaseModel, model_validator, model_serializer, Field
5
+ from pydantic_core.core_schema import SerializationInfo
6
+ from tortoise.expressions import Q
7
+ from tortoise.functions import Count
8
+ from x_model.func import ArrayAgg
4
9
  from x_model.types import BaseUpd
5
- from xync_schema.enums import PmType
6
- from xync_schema.models import Country, Pm, Ex, CredEx
10
+ from xync_schema.enums import PmType, Side, AdStatus, OrderStatus
11
+ from xync_schema.models import Country, Pm, Ex, CredEx, Cur
7
12
  from xync_schema.xtype import PmExBank
8
13
 
9
14
  from xync_client.pm_unifier import PmUni
@@ -27,12 +32,13 @@ class RemapBase(BaseModel):
27
32
  return data
28
33
 
29
34
  @model_serializer
30
- def _map_out(self):
31
- data = self.__dict__.copy()
32
- for field, mapping in self._remap.items():
33
- reverse = {v: k for k, v in mapping.items()}
34
- if field in data:
35
- data[field] = reverse.get(data[field], data[field])
35
+ def _map_out(self, srlz_info: SerializationInfo):
36
+ data = dict(self)
37
+ if srlz_info.by_alias:
38
+ for field, mapping in self._remap.items():
39
+ reverse = {v: k for k, v in mapping.items()}
40
+ if field in data:
41
+ data[field] = reverse.get(data[field], data[field])
36
42
  return data
37
43
 
38
44
 
@@ -65,74 +71,80 @@ class PmExIn(BaseModel):
65
71
  arbitrary_types_allowed = True
66
72
 
67
73
 
68
- class CredExOut(BaseModel):
69
- id: int
70
-
71
-
72
- class BaseOrderReq(BaseModel):
73
- ad_id: int | str
74
-
75
- asset_amount: float | None = None
76
- fiat_amount: float | None = None
77
-
78
- pmex_exid: str = None # int
79
-
80
- # todo: mv from base to special ex class
81
- amount_is_fiat: bool = True
82
- is_sell: bool = None
83
- cur_exid: int | str = None
84
- coin_exid: int | str = None
85
- coin_scale: int = None
74
+ class BaseActor(BaseModel):
75
+ exid: int | str
76
+ nick: str | None = None
86
77
 
87
- @model_validator(mode="after")
88
- def check_a_or_b(self):
89
- if self.amount_is_fiat and not self.fiat_amount:
90
- raise ValueError("fiat_amount is required if amount_is_fiat")
91
- if not self.amount_is_fiat and not self.asset_amount:
92
- raise ValueError("asset_amount is required if not amount_is_fiat")
93
- if not self.asset_amount and not self.fiat_amount:
94
- raise ValueError("either fiat_amount or asset_amount is required")
95
- return self
96
78
 
79
+ class BaseCounteragent(BaseActor):
80
+ name: str
97
81
 
98
- class BaseOrderPaidReq(BaseModel):
99
- ad_id: int | str
100
82
 
101
- cred_id: int | None = None
102
- pm_id: int | None = None # or pmcur_id?
83
+ class BaseCredEx(BaseModel):
84
+ detail: str
85
+ exid: int | str = Field(alias="id")
86
+ extra: str | None = None
87
+ name: str
88
+ pmex_exid: int | str
89
+ curex_exid: int | str = None # fills on outer BaseOrderFull validation hook
90
+ seller: BaseCounteragent = None # fills on outer BaseOrderFull validation hook
91
+
92
+ async def guess_cur(self, curs: list[Cur] = None):
93
+ curs = {c.ticker: c.id for c in curs or await Cur.all()}
94
+ for cur, cid in curs.items():
95
+ if re.search(re.compile(rf"\({cur}\)"), self.extra + self.detail):
96
+ return cid
97
+ lower_extras = [mb.lower() for mb in self.extra.split(" | ")]
98
+ if (
99
+ pms := await Pm.filter(Q(join_type="OR", pmexs__name__in=lower_extras, norm__in=self.extra.split(" | ")))
100
+ .group_by("pmcurs__cur_id", "pmcurs__cur__ticker")
101
+ .annotate(ccnt=Count("id"), names=ArrayAgg("norm"))
102
+ .order_by("-ccnt", "pmcurs__cur__ticker")
103
+ .values("pmcurs__cur_id", "names", "ccnt")
104
+ ):
105
+ return pms[0]["pmcurs__cur_id"]
106
+ return None
107
+
108
+
109
+ class BaseAd(BaseModel):
110
+ amount: float | None = None
111
+ auto_msg: str | None = None
112
+ cond_txt: str
113
+ created_at: int # utc(0) seconds
114
+ coinex_exid: int | str
115
+ curex_exid: int | str
116
+ exid: int | str = Field(alias="id")
117
+ maker_exid: int | str
118
+ maker_name: str
119
+ maker: BaseActor = None
120
+ max_fiat: int
121
+ min_fiat: int
122
+ # paymentPeriod: int
123
+ pmex_exids: list[int | str]
124
+ premium: float
125
+ price: float
126
+ quantity: float | None = None
127
+ # recentOrderNum: int
128
+ side: Literal[Side.BUY, Side.SALE]
129
+ status: Literal[AdStatus.active, AdStatus.defActive, AdStatus.soldOut] # 10: online; 20: offline; 30: completed
103
130
 
104
131
  @model_validator(mode="after")
105
- def check_a_or_b(self):
106
- if not self.cred_id and not self.pm_id:
107
- raise ValueError("either cred_id or pm_id is required")
132
+ def amount_or_quantity_required(self):
133
+ if not self.amount and not self.quantity:
134
+ raise ValueError("either amount or quantity is required")
135
+ self.maker = BaseActor(exid=self.maker_exid, nick=self.maker_name and self.maker_name)
108
136
  return self
109
137
 
110
138
 
111
- class BaseAdUpdate(BaseModel):
112
- id: int | str
139
+ class BaseCredexsExidsTrait:
140
+ credex_exids: list[int]
113
141
 
114
142
 
115
- class BaseCad(BaseAdUpdate):
116
- id: str
117
- createDate: str
118
- currencyId: str
119
- maxAmount: str
120
- minAmount: str
121
- nickName: str
122
- paymentPeriod: int
123
- payments: list[str]
124
- premium: str
125
- price: str
126
- priceType: Literal[0, 1] # 0 - fix rate, 1 - floating
127
- quantity: str
128
- recentOrderNum: int
129
- remark: str
130
- side: Literal[0, 1] # 0 - покупка, 1 - продажа
131
- tokenId: str
132
- userId: str
143
+ class BaseCredexsTrait:
144
+ credex_exids: list[BaseCredEx]
133
145
 
134
146
 
135
- class GetAds(BaseModel):
147
+ class GetAdsReq(BaseModel):
136
148
  coin_id: int | str
137
149
  cur_id: int | str
138
150
  is_sell: bool
@@ -148,7 +160,7 @@ class GetAds(BaseModel):
148
160
  kwargs: dict = {}
149
161
 
150
162
 
151
- class AdUpd(BaseAdUpdate, GetAds):
163
+ class AdUpdReq(BaseAd, GetAdsReq):
152
164
  price: float
153
165
  pm_ids: list[int | str]
154
166
  amount: float
@@ -160,3 +172,65 @@ class AdUpd(BaseAdUpdate, GetAds):
160
172
 
161
173
  class Config:
162
174
  arbitrary_types_allowed = True
175
+
176
+
177
+ class BaseOrder(BaseModel):
178
+ ad__pair_side__is_sell: bool # int: 0 покупка, 1 продажа (именно для меня - апи агента, и пох мейкер я или тейкер)
179
+ created_at: int
180
+ exid: int = Field(alias="id")
181
+ my_exid: int | None = None # апи агент юзер
182
+ status: OrderStatus
183
+
184
+
185
+ class BaseOrderItem(BaseOrder):
186
+ amount: float | None = None
187
+ pmex_exid: int | str = None # int
188
+ quantity: float | None = None
189
+ ctr_exid: int | None = None # контрагент
190
+ ctr_nick: str | None = None
191
+ taker_exid: int | None = None # в байбите нету
192
+ curex_exid: int | str = None
193
+ coinex_exid: int | str = None
194
+ seller_name: str
195
+ buyer_name: str
196
+
197
+ @model_validator(mode="after")
198
+ def amount_or_quantity_required(self):
199
+ if not self.amount and not self.quantity:
200
+ raise ValueError("either amount or quantity is required")
201
+ return self
202
+
203
+
204
+ class BaseOrderFull(BaseOrderItem):
205
+ ad_id: int | str
206
+ ad__maker_exid: int | None = None
207
+ credex: BaseCredEx
208
+ my_nick: str = Field(alias="nickName")
209
+ taker: BaseCounteragent = None # fills on validation hook
210
+
211
+
212
+ class BaseOrderReq(BaseModel):
213
+ ad_id: int | str
214
+
215
+ quantity: float | None = None
216
+ amount: float | None = None
217
+
218
+ pmex_exid: str = None # int
219
+
220
+ is_sell: bool = None
221
+ cur_exid: int | str = None
222
+ coin_exid: int | str = None
223
+ coin_scale: int = None
224
+
225
+
226
+ class BaseOrderPaidReq(BaseModel):
227
+ ad_id: int | str
228
+
229
+ cred_id: int | None = None
230
+ pm_id: int | None = None # or pmcur_id?
231
+
232
+ @model_validator(mode="after")
233
+ def check_a_or_b(self):
234
+ if not self.cred_id and not self.pm_id:
235
+ raise ValueError("either cred_id or pm_id is required")
236
+ return self
@@ -90,7 +90,7 @@ class AgentClient(BaseAgentClient):
90
90
  pass
91
91
 
92
92
  # 29: Список моих объявлений
93
- async def my_ads(self):
93
+ async def get_my_ads(self):
94
94
  pass
95
95
 
96
96
  # 30: Создание объявления
@@ -2,13 +2,11 @@ import json
2
2
  import subprocess
3
3
  from asyncio import run
4
4
  from x_model import init_db
5
- from xync_schema import models
6
5
  from xync_schema.enums import AdStatus, PmType, OrderStatus
7
6
  from xync_schema.models import User, Pm, Coin, Cur, Ad, Fiat, Order, Agent
8
7
  from xync_schema.pydantic import FiatNew
9
8
 
10
9
  from xync_client.Abc.Agent import BaseAgentClient
11
- from xync_client.loader import PG_DSN
12
10
 
13
11
 
14
12
  class Client(BaseAgentClient):
@@ -50,7 +48,7 @@ class Client(BaseAgentClient):
50
48
  async def fiat_del(self, fiat_id: int) -> bool:
51
49
  pass
52
50
 
53
- async def my_ads(self) -> list[Ad]:
51
+ async def get_my_ads(self) -> list[Ad]:
54
52
  pass
55
53
 
56
54
  async def ad_new(