xync-client 0.0.156.dev18__py3-none-any.whl → 0.0.162__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/Agent.py CHANGED
@@ -29,6 +29,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
29
29
  bbot: XyncBot
30
30
  fbot: FileClient
31
31
  ex_client: BaseExClient
32
+ orders: dict[int, tuple[models.Order, BaseModel]] = {} # pending
32
33
  pm_clients: dict[int, PmAgentClient] # {pm_id: PmAgentClient}
33
34
  api: HttpClient
34
35
  cred_x2e: dict[int, int] = {}
@@ -69,11 +70,13 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
69
70
  return self.cred_e2x[exid]
70
71
 
71
72
  async def start(self):
72
- if self.agent.status & 1: # щас пока юзаем статус чисто как бул актив
73
+ if self.agent.status & 1: # race
73
74
  for race in await models.Race.filter(started=True, road__ad__maker_id=self.agent.actor_id).prefetch_related(
74
75
  "road__ad__pair_side__pair__cur", "road__credexs__cred"
75
76
  ):
76
77
  create_task(self.racing(race))
78
+ if self.agent.status & 2: # listen
79
+ await self.start_listen()
77
80
 
78
81
  async def racing(self, race: models.Race):
79
82
  pair = race.road.ad.pair_side.pair
@@ -460,6 +463,16 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
460
463
  # await cred_db.banks.add(*[await PmExBank.get(exid=b) for b in banks])
461
464
  # return True
462
465
 
466
+ @abstractmethod
467
+ async def _start_listen(self): ...
468
+
469
+ @abstractmethod
470
+ async def load_pending_orders(self): ...
471
+
472
+ async def start_listen(self):
473
+ create_task(self._start_listen())
474
+ await self.load_pending_orders()
475
+
463
476
 
464
477
  def step_is_need(mad, cad) -> bool:
465
478
  # todo: пока не решен непонятный кейс, почему то конкурент по всем параметрам слабже, но в списке ранжируется выше.
xync_client/Abc/Ex.py CHANGED
@@ -431,17 +431,23 @@ class BaseExClient(HttpClient, AdLoader):
431
431
 
432
432
  async def person_name_update(self, name: str, exid: int) -> models.Person:
433
433
  if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex).prefetch_related("person"):
434
- actor.person.name = name
434
+ # мб уже есть персона с этим же name, note, tg_id
435
+ if person := await models.Person.get_or_none(
436
+ name=name, note=actor.person.note, tg_id=actor.person.tg_id, id__not=actor.person_id
437
+ ):
438
+ actor.person = person
439
+ else:
440
+ actor.person.name = name
435
441
  await actor.person.save()
436
442
  return actor.person
437
443
  # tmp dirty fix
438
444
  note = f"{self.ex.id}:{exid}"
439
- if person := await models.Person.get_or_none(note__startswith=note):
445
+ if person := await models.Person.get_or_none(note__startswith=note, name__not=name):
440
446
  person.name = name
441
447
  await person.save()
442
448
  return person
443
449
  try:
444
- return await models.Person.create(name=name, note=note)
450
+ return (await models.Person.update_or_create(name=name, note=note))[0]
445
451
  except OperationalError as e:
446
452
  raise e
447
453
  await models.Actor.create(person=person, exid=exid, ex=self.ex)
@@ -459,7 +465,7 @@ class BaseExClient(HttpClient, AdLoader):
459
465
  curex: models.CurEx = None,
460
466
  rname: str = None,
461
467
  ) -> models.Ad:
462
- self.e2x_actor()
468
+ # self.e2x_actor()
463
469
  if not maker:
464
470
  if not (maker := await models.Actor.get_or_none(exid=pad.userId, ex=self.ex)):
465
471
  person = await models.Person.create(name=rname, note=f"{self.ex.id}:{pad.userId}:{pad.nickName}")
@@ -494,6 +500,7 @@ class BaseExClient(HttpClient, AdLoader):
494
500
  ad_db, _ = await models.Ad.update_or_create(**df_unq)
495
501
  except OperationalError as e:
496
502
  raise e
503
+ await ad_db.pms.clear()
497
504
  await ad_db.pms.add(*(await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=pad.payments)))
498
505
  return ad_db
499
506
 
xync_client/Abc/Order.py CHANGED
@@ -1,27 +1,20 @@
1
1
  from abc import abstractmethod
2
2
 
3
- from asynchuobi.enums import OrderType
4
- from x_client.aiohttp import Client as HttpClient
5
- from xync_schema.models import Order, Actor
3
+ from xync_schema.models import Order
6
4
 
7
5
  from xync_client.Bybit.agent import AgentClient
8
6
 
9
7
 
10
- class BaseOrderClient(HttpClient):
8
+ class BaseOrderClient:
11
9
  order: Order
12
10
  im_maker: bool
13
11
  im_seller: bool
14
12
 
15
- @abstractmethod
16
- @property
17
- def type_map(self) -> dict[OrderType, str]: ...
18
-
19
- def __init__(self, actor: Actor, order: Order):
13
+ def __init__(self, order: Order, agent_client: AgentClient):
20
14
  self.order = order
21
- self.im_maker = order.taker_id != actor.id # or order.ad.agent_id == agent.id
22
- self.im_seller = order.ad.direction.sell and self.im_maker
23
- super().__init__(actor.ex.host_p2p)
24
- self.agent_client = AgentClient(actor)
15
+ self.im_maker = order.taker_id != agent_client.actor.id # or order.ad.agent_id == agent.id
16
+ self.im_seller = order.ad.pair_side.is_sell and self.im_maker
17
+ self.agent_client = agent_client
25
18
 
26
19
  # 2: [T] Отмена запроса на сделку
27
20
  @abstractmethod
@@ -37,7 +30,7 @@ class BaseOrderClient(HttpClient):
37
30
 
38
31
  # 5: [B] Перевод сделки в состояние "оплачено", c отправкой чека
39
32
  @abstractmethod
40
- async def mark_payed(self, receipt): ...
33
+ async def mark_payed(self, receipt: bytes = None): ...
41
34
 
42
35
  # 6: [B] Отмена сделки
43
36
  @abstractmethod
xync_client/Abc/xtype.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import Literal, ClassVar
2
2
 
3
3
  from pydantic import BaseModel, model_validator, model_serializer
4
4
  from x_model.types import BaseUpd
@@ -16,7 +16,7 @@ MapOfIdsList = dict[int | str, list[int | str]]
16
16
 
17
17
  class RemapBase(BaseModel):
18
18
  # Переопределяешь это в наследнике:
19
- _remap: dict[str, dict] = {}
19
+ _remap: ClassVar[dict[str, dict]] = {}
20
20
 
21
21
  @model_validator(mode="before")
22
22
  def _map_in(cls, data):
@@ -1,11 +1,6 @@
1
- import logging
2
- import re
3
1
  from asyncio import create_task
4
- from datetime import datetime, timedelta
5
2
  from bybit_p2p import P2P
6
3
  from pyro_client.client.file import FileClient
7
- from tortoise.exceptions import IntegrityError
8
- from tortoise.timezone import now
9
4
  from xync_bot import XyncBot
10
5
  from xync_schema.models import Agent
11
6
 
@@ -13,17 +8,9 @@ from xync_client.Bybit.agent import AgentClient
13
8
  from xync_client.Bybit.ex import ExClient
14
9
 
15
10
  from xync_schema import models
16
- from xync_schema.enums import OrderStatus
17
11
 
18
12
  from xync_client.Bybit.etype.order import (
19
- StatusChange,
20
- CountDown,
21
- SellerCancelChange,
22
- Read,
23
- Receive,
24
- OrderFull,
25
13
  OrderItem,
26
- Status,
27
14
  )
28
15
 
29
16
 
@@ -47,208 +34,3 @@ class InAgentClient(AgentClient):
47
34
  for oid in po.keys() - self.orders.keys():
48
35
  fo = self.api.get_order_details(orderId=oid)
49
36
  self.orders[oid] = await self.create_order_db(fo)
50
-
51
- async def proc(self, data: dict):
52
- match data.get("topic"):
53
- case "OTC_ORDER_STATUS":
54
- match data["type"]:
55
- case "STATUS_CHANGE":
56
- upd = StatusChange.model_validate(data["data"])
57
-
58
- if order_db := self.orders.get(upd.id):
59
- order_db.status = OrderStatus[Status(data["status"]).name]
60
- await order_db.save()
61
- order = self.api.get_order_details(orderId=upd.id)
62
- order = OrderFull.model_validate(order["result"])
63
- order_db = await models.Order.get_or_none(
64
- exid=order.id, ad__exid=order.itemId
65
- ) or await self.create_order(order)
66
- match upd.status:
67
- case Status.ws_new:
68
- logging.info(f"Order {order.id} created at {order.createDate}")
69
- # сразу уменьшаем доступный остаток монеты/валюты
70
- await self.money_upd(order_db)
71
- if upd.side: # я покупатель - ждем мою оплату
72
- _dest = order.paymentTermList[0].accountNo
73
- if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
74
- return
75
- await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
76
- await self.send_payment(order_db)
77
- case Status.created:
78
- if upd.side == 0: # ждем когда покупатель оплатит
79
- if not (pmacdx := await self.get_pma_by_cdex(order)):
80
- return
81
- pma, cdx = pmacdx
82
- am, tid = await pma.check_in(
83
- float(order.amount),
84
- cdx.cred.pmcur.cur.ticker,
85
- # todo: почему в московском час.поясе?
86
- datetime.fromtimestamp(float(order.transferDate) / 1000),
87
- )
88
- if not tid:
89
- logging.info(f"Order {order.id} created at {order.createDate}, not paid yet")
90
- return
91
- try:
92
- t, is_new = await models.Transfer.update_or_create(
93
- dict(
94
- amount=int(float(order.amount) * 100),
95
- order=order_db,
96
- ),
97
- pmid=tid,
98
- )
99
- except IntegrityError as e:
100
- logging.error(tid)
101
- logging.error(order)
102
- logging.exception(e)
103
-
104
- if not is_new: # если по этому платежу уже отпущен другая продажа
105
- return
106
-
107
- # если висят незавершенные продажи с такой же суммой
108
- pos = (await self.get_orders_active(1))["result"]
109
- pos = [
110
- o
111
- for o in pos.get("items", [])
112
- if (
113
- o["amount"] == order.amount
114
- and o["id"] != upd.id
115
- and int(order.createDate) < int(o["createDate"]) + 15 * 60 * 1000
116
- # get full_order from o, and cred or pm from full_order:
117
- and self.api.get_order_details(orderId=o["id"])["result"][
118
- "paymentTermList"
119
- ][0]["accountNo"]
120
- == order.paymentTermList[0].accountNo
121
- )
122
- ]
123
- curex = await models.CurEx.get(cur__ticker=order.currencyId, ex=self.ex_client.ex)
124
- pos_db = await models.Order.filter(
125
- exid__not=order.id,
126
- cred_id=order_db.cred_id,
127
- amount=int(float(order.amount) * 10**curex.scale),
128
- status__not_in=[OrderStatus.completed, OrderStatus.canceled],
129
- created_at__gt=now() - timedelta(minutes=15),
130
- )
131
- if pos or pos_db:
132
- await self.ex_client.bot.send(
133
- f"[Duplicate amount!]"
134
- f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
135
- self.actor.person.user.username_id,
136
- )
137
- logging.warning("Duplicate amount!")
138
- return
139
-
140
- # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
141
- self.api.release_assets(orderId=upd.id)
142
- logging.info(
143
- f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
144
- )
145
- elif upd.side == 1: # я покупатель - ждем мою оплату
146
- return # logging.warning(f"Order {order.id} PAID at {now()}: {int_am}")
147
- else:
148
- ...
149
- # todo: check is always canceling
150
- # await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
151
- # logging.info(f"Order {order.id} canceled at {datetime.now()}")
152
-
153
- case Status.wait_for_seller:
154
- if order_db.status == OrderStatus.paid:
155
- return
156
- await order_db.update_from_dict(
157
- {
158
- "status": OrderStatus.paid,
159
- "payed_at": datetime.fromtimestamp(float(order.transferDate) / 1000),
160
- }
161
- ).save()
162
- logging.info(f"Order {order.id} payed at {order_db.payed_at}")
163
-
164
- case Status.appealed:
165
- # todo: appealed by WHO? щас наугад стоит by_seller
166
- await order_db.update_from_dict(
167
- {
168
- "status": OrderStatus.appealed_by_seller,
169
- "appealed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
170
- }
171
- ).save()
172
- logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
173
-
174
- case Status.canceled:
175
- await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
176
- logging.info(f"Order {order.id} canceled at {datetime.now()}")
177
- await self.money_upd(order_db)
178
-
179
- case Status.completed:
180
- await order_db.update_from_dict(
181
- {
182
- "status": OrderStatus.completed,
183
- "confirmed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
184
- }
185
- ).save()
186
- await self.money_upd(order_db)
187
-
188
- case _:
189
- logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
190
- case "COUNT_DOWN":
191
- upd = CountDown.model_validate(data["data"])
192
- case _:
193
- self.listen(data)
194
- case "OTC_USER_CHAT_MSG":
195
- match data["type"]:
196
- case "RECEIVE":
197
- upd = Receive.model_validate(data["data"])
198
- if order_db := await models.Order.get_or_none(
199
- exid=upd.orderId, ad__maker__ex=self.actor.ex
200
- ).prefetch_related("ad__pair_side__pair", "cred__pmcur__cur"):
201
- im_taker = order_db.taker_id == self.actor.id
202
- im_buyer = order_db.ad.pair_side.is_sell == im_taker
203
- if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
204
- msg, _ = await models.Msg.update_or_create(
205
- {
206
- "to_maker": upd.userId == self.actor.exid and im_taker,
207
- "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
208
- },
209
- txt=upd.message,
210
- order=order_db,
211
- )
212
- if not upd.message:
213
- ...
214
- if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
215
- if not order_db.cred.detail.startswith(dest := g.group()):
216
- order_db.cred.detail = dest
217
- await order_db.save()
218
- await self.send_payment(order_db)
219
- case "READ":
220
- upd = Read.model_validate(data["data"])
221
- # if upd.status not in (StatusWs.created, StatusWs.canceled, 10, StatusWs.completed):
222
- if upd.orderStatus in (
223
- Status.wait_for_buyer,
224
- ): # todo: тут приходит ордер.статус=10, хотя покупатель еще не нажал оплачено
225
- order = self.api.get_order_details(orderId=upd.orderId)["result"]
226
- order = OrderFull.model_validate(order)
227
-
228
- case "CLEAR":
229
- return
230
- case _:
231
- self.listen(data)
232
- case "OTC_USER_CHAT_MSG_V2":
233
- # match data["type"]:
234
- # case "RECEIVE":
235
- # upd = Receive.model_validate(data["data"])
236
- # case "READ":
237
- # upd = Read.model_validate(data["data"])
238
- # case "CLEAR":
239
- # pass
240
- # case _:
241
- # self.listen(data)
242
- return
243
- case "SELLER_CANCEL_CHANGE":
244
- upd = SellerCancelChange.model_validate(data["data"])
245
- case None:
246
- if not data.get("success"):
247
- logging.error(data, "NOT SUCCESS!")
248
- else:
249
- return # success login, subscribes, input
250
- case _:
251
- logging.warning(data, "UNKNOWN TOPIC")
252
- ...
253
- if not upd:
254
- logging.warning(data, "NOT PROCESSED UPDATE")
@@ -19,7 +19,6 @@ from aiohttp.http_exceptions import HttpProcessingError
19
19
  from bybit_p2p import P2P
20
20
  from bybit_p2p._exceptions import FailedRequestError
21
21
  from payeer_api import PayeerAPI
22
- from pydantic import ValidationError
23
22
  from pyro_client.client.file import FileClient
24
23
  from tortoise import BaseDBAsyncClient
25
24
  from tortoise.exceptions import IntegrityError
@@ -77,34 +76,7 @@ class AgentClient(BaseAgentClient): # Bybit client
77
76
  sec_hdrs: dict[str, str]
78
77
  # rewrite token for public methods
79
78
  api: P2P
80
- last_ad_id: list[str] = []
81
- update_ad_body = {
82
- "priceType": "1",
83
- "premium": "118",
84
- "quantity": "0.01",
85
- "minAmount": "500",
86
- "maxAmount": "3500000",
87
- "paymentPeriod": "30",
88
- "remark": "",
89
- "price": "398244.84",
90
- "paymentIds": ["3162931"],
91
- "tradingPreferenceSet": {
92
- "isKyc": "1",
93
- "hasCompleteRateDay30": "0",
94
- "completeRateDay30": "",
95
- "hasOrderFinishNumberDay30": "0",
96
- "orderFinishNumberDay30": "0",
97
- "isMobile": "0",
98
- "isEmail": "0",
99
- "hasUnPostAd": "0",
100
- "hasRegisterTime": "0",
101
- "registerTimeThreshold": "0",
102
- "hasNationalLimit": "0",
103
- "nationalLimit": "",
104
- },
105
- "actionType": "MODIFY",
106
- "securityRiskToken": "",
107
- }
79
+ orders: dict[int, tuple[models.Order, OrderFull]] = {} # pending
108
80
 
109
81
  def __init__(
110
82
  self,
@@ -292,19 +264,21 @@ class AgentClient(BaseAgentClient): # Bybit client
292
264
  mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
293
265
  exids = [pt.id for pt in ad.paymentTerms]
294
266
  credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids).prefetch_related("cred")
267
+ await mad_db.credexs.clear()
295
268
  await mad_db.credexs.add(*credexs)
296
- # share
297
- if [cx for cx in credexs if cx.cred.ovr_pm_id == 0]:
298
- try:
299
- await self.ad_share(mad_db.id)
300
- except ShareException as e:
301
- logging.warning(e.args[0])
302
- await sleep(1)
303
-
304
269
  return len(ads)
305
270
 
271
+ async def ads_share(self, cur_id: int = None) -> int:
272
+ mq = models.MyAd.hot_mads_query([4]).filter(ad__maker=self.actor)
273
+ if cur_id:
274
+ mq = mq.filter(ad__pair_side__pair__cur_id=cur_id)
275
+ mads: list[models.MyAd] = await mq.all()
276
+ return len([await self.ad_share(mad.id) for mad in mads])
277
+
306
278
  async def ad_share(self, maid: int):
307
- myad = await models.MyAd.get(id=maid).prefetch_related("ad")
279
+ myad = await models.MyAd.get(id=maid).prefetch_related(
280
+ "ad__pair_side__pair__coin", "ad__pair_side__pair__cur", "ad__maker"
281
+ )
308
282
  if myad.hex and myad.shared_at + timedelta(minutes=55) > now(): # check expired
309
283
  # check validity
310
284
  data = await self._post("/x-api/fiat/otc/item/shareItem/info", {"shareCode": myad.hex.hex()})
@@ -312,15 +286,22 @@ class AgentClient(BaseAgentClient): # Bybit client
312
286
  return myad.get_url()
313
287
  data = await self._post("/x-api/fiat/otc/item/share", {"itemId": str(myad.ad.exid)})
314
288
  if data["ret_code"] == 912300058:
315
- raise ShareException(f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} выключена")
289
+ raise ShareException(
290
+ f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} агента {myad.ad.maker.agent_id} выключена"
291
+ )
316
292
  if data["ret_code"] == 912300059:
317
- raise ShareException("Торговля выключена")
293
+ raise ShareException(f"Торговля агента {myad.ad.maker.agent_id} выключена")
294
+ if data["ret_code"] == 10007:
295
+ raise ShareException(f"Авторизация агента {myad.ad.maker.agent_id} слетела")
318
296
  if data["ret_code"] != 0: # Новая ошибка
319
297
  raise ShareException(data)
320
298
  url = data["result"]["shareLink"]
321
299
  resp = await self.session.get(url)
322
- hx = resp.url.query["by_web_link"].replace(models.MyAd.WEB, "")
323
- await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
300
+ side = "buy" if myad.ad.pair_side.is_sell else "sell" # inverse for taker
301
+ coin, cur = myad.ad.pair_side.pair.coin.ticker, myad.ad.pair_side.pair.cur.ticker
302
+ pref = models.MyAd.WEB.format(side=side, coin=coin, cur=cur)
303
+ hx = resp.url.query["by_web_link"].replace(pref, "")
304
+ _r = await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
324
305
  await myad.refresh_from_db()
325
306
  return myad.get_url()
326
307
 
@@ -535,7 +516,7 @@ class AgentClient(BaseAgentClient): # Bybit client
535
516
  },
536
517
  )
537
518
  if res["ret_code"] == 0:
538
- return {o.id: OrderItem(**o) for o in res["result"]["items"]}
519
+ return {int(o["id"]): OrderItem(**o) for o in res["result"]["items"]}
539
520
  return res["ret_code"]
540
521
 
541
522
  def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
@@ -728,8 +709,7 @@ class AgentClient(BaseAgentClient): # Bybit client
728
709
  for oid, o in ords.items():
729
710
  if o.status != Status.completed.value or oid in self.completed_orders:
730
711
  continue
731
- fo = self.api.get_order_details(orderId=o.id)
732
- order = OrderFull.model_validate(fo["result"])
712
+ order = await self.get_order_full(o.id)
733
713
  order_db = await self.create_order_db(order)
734
714
  await sleep(1)
735
715
  dmsgs = self.api.get_chat_messages(orderId=oid, size=200)["result"]["result"][::-1]
@@ -900,27 +880,30 @@ class AgentClient(BaseAgentClient): # Bybit client
900
880
  am = 500 + i
901
881
  req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
902
882
  ord_resp: OrderResp = await self.take_ad(req)
903
- order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
883
+ order: OrderFull = await self.get_order_full(int(ord_resp.orderId))
904
884
  odb = await self.create_order_db(order)
905
885
  t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
906
886
  await t.fetch_related("order__cred__pmcur__cur")
907
887
  await self.pm_clients[366].send(t)
908
888
  ...
909
889
 
910
- pm_clients: dict[int, PmAgentClient]
890
+ async def load_pending_orders(self):
891
+ po: dict[int, OrderItem] = await self.get_pending_orders()
892
+ if isinstance(po, int): # если код ошибки вместо результата
893
+ raise ValueError(po)
894
+ self.orders = {
895
+ o.exid: (o, await self.get_order_full(o.exid)) for o in await models.Order.filter(exid__in=po.keys())
896
+ }
897
+ for oid in po.keys() - self.orders.keys():
898
+ await self.load_order(oid)
911
899
 
912
- async def start_listen(self):
900
+ async def _start_listen(self):
913
901
  t = await self.ott()
914
902
  ts = int(float(t["time_now"]) * 1000)
915
- await self.ws_prv(self.agent.auth["cookies"]["deviceId"], t["result"], ts)
916
-
917
- # 3N: [T] - Уведомление об одобрении запроса на сделку
918
- async def request_accepted_notify(self) -> int: ... # id
919
-
920
- async def ws_prv(self, did: str, tok: str, ts: int):
903
+ did = self.agent.auth["cookies"]["deviceId"]
921
904
  u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}&timestamp={ts}"
922
905
  async with websockets.connect(u) as websocket:
923
- auth_msg = json.dumps({"req_id": did, "op": "login", "args": [tok]})
906
+ auth_msg = json.dumps({"req_id": did, "op": "login", "args": [t["result"]]})
924
907
  await websocket.send(auth_msg)
925
908
 
926
909
  sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
@@ -947,222 +930,213 @@ class AgentClient(BaseAgentClient): # Bybit client
947
930
  await websocket.send(sub_msg)
948
931
  while resp := await websocket.recv():
949
932
  if data := json.loads(resp):
950
- upd, order_db = None, None
951
933
  logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
952
- match data.get("topic"):
953
- case "OTC_ORDER_STATUS":
954
- match data["type"]:
955
- case "STATUS_CHANGE":
956
- try:
957
- upd = StatusChange.model_validate(data["data"])
958
- except ValidationError as e:
959
- logging.error(e)
960
- logging.error(data["data"])
961
- order = self.api.get_order_details(orderId=upd.id)
962
- order = OrderFull.model_validate(order["result"])
963
- order_db = await models.Order.get_or_none(
964
- exid=order.id, ad__exid=order.itemId
965
- ) or await self.create_order_db(order)
966
- match upd.status:
967
- case Status.created:
968
- logging.info(f"Order {order.id} created at {order.createDate}")
969
- # сразу уменьшаем доступный остаток монеты/валюты
970
- await self.money_upd(order_db)
971
- if upd.side: # я покупатель - ждем мою оплату
972
- _dest = order.paymentTermList[0].accountNo
973
- if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
974
- continue
975
- await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
976
- await self.send_payment(order_db)
977
- case Status.wait_for_buyer:
978
- if upd.side == 0: # ждем когда покупатель оплатит
979
- if not (pmacdx := await self.get_pma_by_cdex(order)):
980
- continue
981
- pma, cdx = pmacdx
982
- am, tid = await pma.check_in(
983
- float(order.amount),
984
- cdx.cred.pmcur.cur.ticker,
985
- # todo: почему в московском час.поясе?
986
- datetime.fromtimestamp(float(order.transferDate) / 1000),
987
- )
988
- if not tid:
989
- logging.info(
990
- f"Order {order.id} created at {order.createDate}, not paid yet"
991
- )
992
- continue
993
- try:
994
- t, is_new = await models.Transfer.update_or_create(
995
- dict(
996
- amount=int(float(order.amount) * 100),
997
- order=order_db,
998
- ),
999
- pmid=tid,
1000
- )
1001
- except IntegrityError as e:
1002
- logging.error(tid)
1003
- logging.error(order)
1004
- logging.exception(e)
1005
-
1006
- if not is_new: # если по этому платежу уже отпущен другая продажа
1007
- continue
1008
-
1009
- # если висят незавершенные продажи с такой же суммой
1010
- pos = (await self.get_pending_orders(1))["result"]
1011
- pos = [
1012
- o
1013
- for o in pos.get("items", [])
1014
- if (
1015
- o["amount"] == order.amount
1016
- and o["id"] != upd.id
1017
- and int(order.createDate)
1018
- < int(o["createDate"]) + 15 * 60 * 1000
1019
- # get full_order from o, and cred or pm from full_order:
1020
- and self.api.get_order_details(orderId=o["id"])["result"][
1021
- "paymentTermList"
1022
- ][0]["accountNo"]
1023
- == order.paymentTermList[0].accountNo
1024
- )
1025
- ]
1026
- curex = await models.CurEx.get(
1027
- cur__ticker=order.currencyId, ex=self.ex_client.ex
1028
- )
1029
- pos_db = await models.Order.filter(
1030
- exid__not=order.id,
1031
- cred_id=order_db.cred_id,
1032
- amount=int(float(order.amount) * 10**curex.scale),
1033
- status__not_in=[OrderStatus.completed, OrderStatus.canceled],
1034
- created_at__gt=now() - timedelta(minutes=15),
1035
- )
1036
- if pos or pos_db:
1037
- await self.ex_client.bot.send(
1038
- f"[Duplicate amount!]"
1039
- f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
1040
- self.actor.person.user.username_id,
1041
- )
1042
- logging.warning("Duplicate amount!")
1043
- continue
1044
-
1045
- # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
1046
- self.api.release_assets(orderId=upd.id)
1047
- logging.info(
1048
- f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
1049
- )
1050
- elif upd.side == 1: # я покупатель - ждем мою оплату
1051
- continue # logging.warning(f"Order {order.id} PAID at {now()}: {int_am}")
1052
- else:
1053
- ...
1054
- # todo: check is always canceling
1055
- # await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
1056
- # logging.info(f"Order {order.id} canceled at {datetime.now()}")
1057
-
1058
- case Status.wait_for_seller:
1059
- if order_db.status == OrderStatus.paid:
1060
- continue
1061
- await order_db.update_from_dict(
1062
- {
1063
- "status": OrderStatus.paid,
1064
- "payed_at": datetime.fromtimestamp(
1065
- float(order.transferDate) / 1000
1066
- ),
1067
- }
1068
- ).save()
1069
- logging.info(f"Order {order.id} payed at {order_db.payed_at}")
1070
-
1071
- case Status.appealed:
1072
- # todo: appealed by WHO? щас наугад стоит by_seller
1073
- await order_db.update_from_dict(
1074
- {
1075
- "status": OrderStatus.appealed_by_seller,
1076
- "appealed_at": datetime.fromtimestamp(
1077
- float(order.updateDate) / 1000
1078
- ),
1079
- }
1080
- ).save()
1081
- logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
1082
-
1083
- case Status.canceled:
1084
- await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
1085
- logging.info(f"Order {order.id} canceled at {datetime.now()}")
1086
- await self.money_upd(order_db)
1087
-
1088
- case Status.completed:
1089
- await order_db.update_from_dict(
1090
- {
1091
- "status": OrderStatus.completed,
1092
- "confirmed_at": datetime.fromtimestamp(
1093
- float(order.updateDate) / 1000
1094
- ),
1095
- }
1096
- ).save()
1097
- await self.money_upd(order_db)
1098
-
1099
- case _:
1100
- logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
1101
- case "COUNT_DOWN":
1102
- upd = CountDown.model_validate(data["data"])
1103
- case _:
1104
- self.listen(data)
1105
- case "OTC_USER_CHAT_MSG":
1106
- match data["type"]:
1107
- case "RECEIVE":
1108
- upd = Receive.model_validate(data["data"])
1109
- if order_db := await models.Order.get_or_none(
1110
- exid=upd.orderId, ad__maker__ex=self.actor.ex
1111
- ).prefetch_related("ad__pair_side__pair", "cred__pmcur__cur"):
1112
- im_taker = order_db.taker_id == self.actor.id
1113
- im_buyer = order_db.ad.pair_side.is_sell == im_taker
1114
- if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
1115
- msg, _ = await models.Msg.update_or_create(
1116
- {
1117
- "to_maker": upd.userId == self.actor.exid and im_taker,
1118
- "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
1119
- },
1120
- txt=upd.message,
1121
- order=order_db,
1122
- )
1123
- if not upd.message:
1124
- ...
1125
- if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
1126
- if not order_db.cred.detail.startswith(dest := g.group()):
1127
- order_db.cred.detail = dest
1128
- await order_db.save()
1129
- await self.send_payment(order_db)
1130
- case "READ":
1131
- upd = Read.model_validate(data["data"])
1132
- # if upd.status not in (StatusWs.created, StatusWs.canceled, 10, StatusWs.completed):
1133
- if upd.orderStatus in (
1134
- Status.wait_for_buyer,
1135
- ): # todo: тут приходит ордер.статус=10, хотя покупатель еще не нажал оплачено
1136
- order = self.api.get_order_details(orderId=upd.orderId)["result"]
1137
- order = OrderFull.model_validate(order)
1138
-
1139
- case "CLEAR":
1140
- continue
1141
- case _:
1142
- self.listen(data)
1143
- case "OTC_USER_CHAT_MSG_V2":
1144
- # match data["type"]:
1145
- # case "RECEIVE":
1146
- # upd = Receive.model_validate(data["data"])
1147
- # case "READ":
1148
- # upd = Read.model_validate(data["data"])
1149
- # case "CLEAR":
1150
- # pass
1151
- # case _:
1152
- # self.listen(data)
1153
- continue
1154
- case "SELLER_CANCEL_CHANGE":
1155
- upd = SellerCancelChange.model_validate(data["data"])
1156
- case None:
1157
- if not data.get("success"):
1158
- logging.error(data, "NOT SUCCESS!")
1159
- else:
1160
- continue # success login, subscribes, input
1161
- case _:
1162
- logging.warning(data, "UNKNOWN TOPIC")
1163
- ...
1164
- if not upd:
1165
- logging.warning(data, "NOT PROCESSED UPDATE")
934
+ await self.proc(data)
935
+
936
+ async def proc(self, data: dict):
937
+ match data.get("topic"):
938
+ case "OTC_ORDER_STATUS":
939
+ match data["type"]:
940
+ case "STATUS_CHANGE":
941
+ upd = StatusChange.model_validate(data["data"])
942
+ order_db, order = await self.load_order(upd.id)
943
+ match upd.status:
944
+ case Status.ws_new:
945
+ logging.info(f"Order {upd.id} CREATED at {upd.createDate}")
946
+ # await self.got_new_order(order_db, order)
947
+
948
+ # # сразу уменьшаем доступный остаток монеты/валюты
949
+ # await self.money_upd(order_db)
950
+ # if upd.side: # я покупатель - ждем мою оплату
951
+ # _dest = order.paymentTermList[0].accountNo
952
+ # if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
953
+ # return
954
+ # await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
955
+ # await self.send_payment(order_db)
956
+ case Status.created:
957
+ if upd.side == 0: # я продавец, ждем когда покупатель оплатит
958
+ # check_payment() # again
959
+ ...
960
+ # if not (pmacdx := await self.get_pma_by_cdex(order)):
961
+ # return
962
+ # pma, cdx = pmacdx
963
+ # am, tid = await pma.check_in(
964
+ # float(order.amount),
965
+ # cdx.cred.pmcur.cur.ticker,
966
+ # # todo: почему в московском час.поясе?
967
+ # datetime.fromtimestamp(float(order.transferDate) / 1000),
968
+ # )
969
+ # if not tid:
970
+ # logging.info(f"Order {order.id} created at {order.createDate}, not paid yet")
971
+ # return
972
+ # try:
973
+ # t, is_new = await models.Transfer.update_or_create(
974
+ # dict(
975
+ # amount=int(float(order.amount) * 100),
976
+ # order=order_db,
977
+ # ),
978
+ # pmid=tid,
979
+ # )
980
+ # except IntegrityError as e:
981
+ # logging.error(tid)
982
+ # logging.error(order)
983
+ # logging.exception(e)
984
+ #
985
+ # if not is_new: # если по этому платежу уже отпущен другая продажа
986
+ # return
987
+ #
988
+ # # если висят незавершенные продажи с такой же суммой
989
+ # pos = (await self.get_orders_active(1))["result"]
990
+ # pos = [
991
+ # o
992
+ # for o in pos.get("items", [])
993
+ # if (
994
+ # o["amount"] == order.amount
995
+ # and o["id"] != upd.id
996
+ # and int(order.createDate) < int(o["createDate"]) + 15 * 60 * 1000
997
+ # # get full_order from o, and cred or pm from full_order:
998
+ # and self.api.get_order_details(orderId=o["id"])["result"][
999
+ # "paymentTermList"
1000
+ # ][0]["accountNo"]
1001
+ # == order.paymentTermList[0].accountNo
1002
+ # )
1003
+ # ]
1004
+ # curex = await models.CurEx.get(cur__ticker=order.currencyId, ex=self.ex_client.ex)
1005
+ # pos_db = await models.Order.filter(
1006
+ # exid__not=order.id,
1007
+ # cred_id=order_db.cred_id,
1008
+ # amount=int(float(order.amount) * 10**curex.scale),
1009
+ # status__not_in=[OrderStatus.completed, OrderStatus.canceled],
1010
+ # created_at__gt=now() - timedelta(minutes=15),
1011
+ # )
1012
+ # if pos or pos_db:
1013
+ # await self.ex_client.bot.send(
1014
+ # f"[Duplicate amount!]"
1015
+ # f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
1016
+ # self.actor.person.user.username_id,
1017
+ # )
1018
+ # logging.warning("Duplicate amount!")
1019
+ # return
1020
+ #
1021
+ # # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
1022
+ # self.api.release_assets(orderId=upd.id)
1023
+ # logging.info(
1024
+ # f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
1025
+ # )
1026
+ elif upd.side == 1: # я покупатель - ждем мою оплату
1027
+ # pay()
1028
+ logging.warning(f"Order {upd.id} CREATED2 at {now()}")
1029
+
1030
+ case Status.paid:
1031
+ if order_db.status == OrderStatus.paid:
1032
+ return
1033
+ await order_db.update_from_dict(
1034
+ {
1035
+ "status": OrderStatus.paid,
1036
+ "payed_at": datetime.fromtimestamp(float(order.transferDate) / 1000),
1037
+ }
1038
+ ).save()
1039
+ logging.info(f"Order {order.id} payed at {order_db.payed_at}")
1040
+
1041
+ case Status.appealed_by_seller: # just any appealed
1042
+ # todo: appealed by WHO? щас наугад стоит by_seller
1043
+ await order_db.update_from_dict(
1044
+ {
1045
+ "status": OrderStatus.appealed_by_seller,
1046
+ "appealed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
1047
+ }
1048
+ ).save()
1049
+ logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
1050
+
1051
+ case Status.canceled:
1052
+ await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
1053
+ logging.info(f"Order {order.id} canceled at {datetime.now()}")
1054
+ # await self.money_upd(order_db)
1055
+
1056
+ case Status.completed:
1057
+ await order_db.refresh_from_db()
1058
+ if order_db.status != OrderStatus.completed:
1059
+ await order_db.update_from_dict(
1060
+ {
1061
+ "status": OrderStatus.completed,
1062
+ "confirmed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
1063
+ }
1064
+ ).save(update_fields=["status", "confirmed_at"])
1065
+ # await self.money_upd(order_db)
1066
+
1067
+ case _:
1068
+ logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
1069
+ case "COUNT_DOWN":
1070
+ upd = CountDown.model_validate(data["data"])
1071
+
1072
+ case "OTC_USER_CHAT_MSG":
1073
+ match data["type"]:
1074
+ case "RECEIVE":
1075
+ upd = Receive.model_validate(data["data"])
1076
+ order_db, order = await self.load_order(upd.orderId)
1077
+ # got_msg()
1078
+ ...
1079
+ # im_taker = order_db.taker_id == self.actor.id
1080
+ # im_buyer = order_db.ad.pair_side.is_sell == im_taker
1081
+ # if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
1082
+ # msg, _ = await models.Msg.update_or_create(
1083
+ # {
1084
+ # "to_maker": upd.userId == self.actor.exid and im_taker,
1085
+ # "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
1086
+ # },
1087
+ # txt=upd.message,
1088
+ # order=order_db,
1089
+ # )
1090
+ # if not upd.message:
1091
+ # ...
1092
+ # if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
1093
+ # if not order_db.cred.detail.startswith(dest := g.group()):
1094
+ # order_db.cred.detail = dest
1095
+ # await order_db.save()
1096
+ # await self.send_payment(order_db)
1097
+ case "READ":
1098
+ # msg_read()
1099
+ upd = Read.model_validate(data["data"])
1100
+
1101
+ case "CLEAR":
1102
+ return
1103
+ case "OTC_USER_CHAT_MSG_V2":
1104
+ # msg dup
1105
+ ...
1106
+ # match data["type"]:
1107
+ # case "RECEIVE":
1108
+ # upd = Receive.model_validate(data["data"])
1109
+ # case "READ":
1110
+ # upd = Read.model_validate(data["data"])
1111
+ # case "CLEAR":
1112
+ # pass
1113
+ # case _:
1114
+ # self.listen(data)
1115
+ case "SELLER_CANCEL_CHANGE":
1116
+ upd = SellerCancelChange.model_validate(data["data"])
1117
+ case None:
1118
+ if not data.get("success"):
1119
+ logging.error(data, "NOT SUCCESS!")
1120
+ else:
1121
+ return # success login, subscribes, input
1122
+ case _:
1123
+ logging.warning(data, "UNKNOWN TOPIC")
1124
+
1125
+ async def get_order_full(self, oid: int) -> OrderFull:
1126
+ order = self.api.get_order_details(orderId=oid)
1127
+ return OrderFull.model_validate(order["result"])
1128
+
1129
+ async def load_order(self, oid: int) -> tuple[models.Order, OrderFull]:
1130
+ if not self.orders.get(oid):
1131
+ order = await self.get_order_full(oid)
1132
+ if not (
1133
+ order_db := await models.Order.get_or_none(exid=oid, ad__maker__ex=self.actor.ex).prefetch_related(
1134
+ "ad__pair_side__pair", "cred__pmcur__cur"
1135
+ )
1136
+ ):
1137
+ order_db = await self.create_order_db(order)
1138
+ self.orders[oid] = order_db, order
1139
+ return self.orders[oid]
1166
1140
 
1167
1141
  async def money_upd(self, odb: models.Order):
1168
1142
  # обновляем остаток монеты
@@ -1279,11 +1253,6 @@ class AgentClient(BaseAgentClient): # Bybit client
1279
1253
  return pma, order_db.cred.pmcur.cur.ticker
1280
1254
  logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
1281
1255
 
1282
- @staticmethod
1283
- def listen(data: dict | None):
1284
- # print(data)
1285
- ...
1286
-
1287
1256
 
1288
1257
  def ms2utc(msk_ts_str: str):
1289
1258
  return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
@@ -1334,7 +1303,7 @@ async def main():
1334
1303
  cn = await init_db(TORM)
1335
1304
 
1336
1305
  agent = (
1337
- await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=2)
1306
+ await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=5)
1338
1307
  .prefetch_related(
1339
1308
  "actor__ex",
1340
1309
  "actor__person__user__gmail",
@@ -1364,12 +1333,11 @@ async def main():
1364
1333
  # await cl.ex_client.set_pairs()
1365
1334
  # await cl.ex_client.set_pms()
1366
1335
 
1367
- # await cl.set_creds()
1368
- # await cl.export_my_ads()
1336
+ await cl.set_creds()
1337
+ await cl.export_my_ads()
1369
1338
 
1370
1339
  my_ad = await models.MyAd[5]
1371
1340
  await cl.ad_share(my_ad.id)
1372
- await cl.start_listen()
1373
1341
 
1374
1342
  ms = await models.Agent.filter(
1375
1343
  actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[3]
@@ -1,5 +1,5 @@
1
1
  from enum import IntEnum
2
- from typing import Literal
2
+ from typing import Literal, ClassVar
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
@@ -20,9 +20,9 @@ class Status(IntEnum):
20
20
  created = 10 # waiting for buyer to pay
21
21
  paid = 20 # waiting for seller to release
22
22
  appealed_by_seller = 30 # appealing
23
- appealed_by_buyer = 30 # the same appealing
23
+ # appealed_by_buyer = 30 # the same appealing
24
24
  canceled = 40 # order cancelled
25
- complete = 50 # order finished
25
+ completed = 50 # order finished
26
26
  # a = 60 # paying (only when paying online)
27
27
  # a = 70 # pay fail (only when paying online)
28
28
  # a = 80 # exception cancelled (the coin convert to other coin only hotswap)
@@ -137,7 +137,7 @@ class PaymentTerm(CredPaymentTerm):
137
137
 
138
138
 
139
139
  class _BaseOrder(RemapBase):
140
- _remap = {"status": {}}
140
+ _remap: ClassVar[dict[str, dict]] = {"status": {}}
141
141
 
142
142
  id: int
143
143
  userId: int
@@ -290,7 +290,7 @@ class CountDown(_BaseChange):
290
290
 
291
291
  class _BaseMsg(BaseModel):
292
292
  userId: int
293
- orderId: str
293
+ orderId: int
294
294
  message: str = None
295
295
  msgUuid: str
296
296
  msgUuId: str
@@ -1,26 +1,28 @@
1
- from xync_schema.models import Order
1
+ from xync_client.Bybit.etype.cred import PaymentTerm
2
+ from xync_schema.models import CredEx, PmEx
2
3
 
3
4
  from xync_client.Abc.Order import BaseOrderClient
4
5
 
5
6
 
6
7
  class OrderClient(BaseOrderClient):
7
- # 2: Отмена своего запроса на сделку
8
- async def cancel_request(self) -> Order: ...
9
-
10
- # 3: Одобрить запрос на сделку
11
- async def accept_request(self) -> bool: ...
12
-
13
- # 4: Отклонить чужой запрос на сделку
14
- async def reject_request(self) -> bool: ...
15
-
16
8
  # 5: Перевод сделки в состояние "оплачено", c отправкой чека
17
- async def mark_payed(self, receipt): ...
9
+ async def mark_payed(self, payterm: PaymentTerm = None, receipt: bytes = None):
10
+ if payterm:
11
+ pt = str(payterm.paymentType)
12
+ pid = payterm.id
13
+ else:
14
+ pmx = await PmEx.get(pm__pmcurs__id=self.order.cred.pmcur_id, ex=self.agent_client.ex_client.ex)
15
+ pt = pmx.exid
16
+ cdx = await CredEx.get(cred_id=self.order.cred_id, ex=self.agent_client.ex_client.ex)
17
+ pid = str(cdx.exid)
18
+ self.agent_client.api.mark_as_paid(orderId=str(self.order.exid), paymentType=pt, paymentId=pid)
18
19
 
19
20
  # 6: Отмена одобренной сделки
20
21
  async def cancel_order(self) -> bool: ...
21
22
 
22
23
  # 7: Подтвердить получение оплаты
23
- async def confirm(self) -> bool: ...
24
+ async def confirm(self):
25
+ self.agent_client.api.release_assets(orderId=str(self.order.exid))
24
26
 
25
27
  # 9, 10: Подать аппеляцию cо скриншотом/видео/файлом
26
28
  async def start_appeal(self, file) -> bool: ...
@@ -10,7 +10,6 @@ from PGram import Bot
10
10
  from playwright.async_api import async_playwright, Page, Locator, Position, Playwright, Browser # , FloatRect
11
11
  from pyotp import TOTP
12
12
 
13
- # noinspection PyProtectedMember
14
13
  from playwright._impl._errors import TimeoutError
15
14
  from pyro_client.client.user import UserClient
16
15
  from pyrogram.handlers import MessageHandler
@@ -268,7 +267,7 @@ async def _test():
268
267
  playwright: Playwright = await async_playwright().start()
269
268
 
270
269
  try:
271
- o = await models.Order.create(ad_id=7, exid=1, amount=900, cred_id=522, taker_id=419)
270
+ o = await models.Order.create(ad_id=7, exid=1, amount=900, cred_id=522, taker_id=1794)
272
271
  await o.fetch_related("cred__pmcur__cur", "ad")
273
272
  pma = await models.PmAgent.get(
274
273
  active=True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.156.dev18
3
+ Version: 0.0.162
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
@@ -3,17 +3,17 @@ xync_client/details.py,sha256=21itVPCgAtaYRR1H9J9oYudj95gafcFjExUN6QL17OI,1330
3
3
  xync_client/loader.py,sha256=DA6lHUxGidULjpHyKGS1POc5r11rlOThQRdueOgr04A,617
4
4
  xync_client/pm_unifier.py,sha256=DYkFYXzmhVEh8WJvI---3Geas34mZ83cT1O-88D68YQ,6568
5
5
  xync_client/Abc/AdLoader.py,sha256=PkVl6CwRvXkL7dd9HA124hBgXVMOQiXac73rYKL0PIk,67
6
- xync_client/Abc/Agent.py,sha256=GMHYZlosHotO6XdQHJ_rPnmHAB5jrkRXwNLxeiQMUY0,22971
6
+ xync_client/Abc/Agent.py,sha256=IN3CsgXdzQ9L0dtkConnVLnqVLhLYfLQ19bwJDCl7_c,23296
7
7
  xync_client/Abc/Asset.py,sha256=hlgyFaU9byr2N2r8Heh-_ICx49SKuKxfRTUA4yQWmEw,454
8
8
  xync_client/Abc/Auth.py,sha256=OPQXN7_XYQZP9431ylFksd6JDusbKG8N_1g6CXTZ6yY,1495
9
9
  xync_client/Abc/BaseTest.py,sha256=vaAs5Z4HYV7k_C3zQz6JKO75s2hXtVbBI3-0Srkzv5Q,2388
10
- xync_client/Abc/Ex.py,sha256=TtbOseV7b4qKfjdWOf4zPGNpra3hFF-CuR2oOyCdzlQ,32359
10
+ xync_client/Abc/Ex.py,sha256=TaRbQpl9hQmTzYxGkweYaYPuYMmFQ3UwRM77AeRhBYU,32743
11
11
  xync_client/Abc/Exception.py,sha256=Sts7RpP370NBdjaH_cyXDdHtjge8zXNUGWCrKw49Zyk,482
12
12
  xync_client/Abc/HasAbotUid.py,sha256=LsTHHjMHBauCwJoqgDa9Lx4R6xsDOHfsN4jM539Bpqg,279
13
13
  xync_client/Abc/InAgent.py,sha256=8BnZ7VzWLIJY93xnNTqlpY3JdarQkkpRRjDDEALaVAA,303
14
- xync_client/Abc/Order.py,sha256=7-FGIJu5z9aYi0A_eJV4F-cp_6Mz_izNpefexDQZvHw,2428
14
+ xync_client/Abc/Order.py,sha256=TJhCvE9FZ_29KIKvDPO1sJxTj2tYxIQar5yFgUaP22Q,2226
15
15
  xync_client/Abc/PmAgent.py,sha256=Xl-0-KMwcTp_7qIt7NV7-aD22j955tFYFqcHHbmGMTQ,4193
16
- xync_client/Abc/xtype.py,sha256=mABfxNxUjkKkv65YgEbOq5lciYpIZaERImgloui83tA,4010
16
+ xync_client/Abc/xtype.py,sha256=rp-to40BqNgT76zj5jyIdzLeRRHwpa6-fdD3OKJUXT4,4030
17
17
  xync_client/Binance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  xync_client/Binance/binance_async.py,sha256=LP2DZaHwkfsp_4Tjvetb-1ntjQtJfODF0OgZpoPx4KU,2688
19
19
  xync_client/Binance/earn_api.py,sha256=hvk0MaVTLszIglQXVhbUjGcaHfbF9Ul7FFXmy94U2so,4411
@@ -36,17 +36,17 @@ xync_client/BitGet/agent.py,sha256=YVs3bDY0OcEJGU7m2A8chzO6PFhWDnQQrA-E6MVkBBg,3
36
36
  xync_client/BitGet/ex.py,sha256=uEvvXuLaJv8o8BFi0bMA3XyBuTfVDWagAjLOHZl-xlE,3765
37
37
  xync_client/BitGet/etype/ad.py,sha256=fysSW47wGYjSOPUqY864z857AJz4gjN-nOkI1Jxd27U,1838
38
38
  xync_client/BitPapa/ex.py,sha256=U-RRB_RSOtErfRgxOZYWegZ_td_uZO37YKo3Jxchf_w,912
39
- xync_client/Bybit/InAgent.py,sha256=jmqqglT_YTkhg_EDxvrOl1Q5VUcz-nudtr7LYhplyYY,14100
40
- xync_client/Bybit/agent.py,sha256=ycQcVkf82oiT6PmRDCkaoot8E8mCLhc9dAFVPgASMd8,69654
39
+ xync_client/Bybit/InAgent.py,sha256=HJzy-avXlGu7yh11WqYmx8whTGKU3HTng2dtkbLtTCQ,1234
40
+ xync_client/Bybit/agent.py,sha256=MyZRc5sbbuivw8N1LeFcjwba4AJytplStFldku_ESaM,66356
41
41
  xync_client/Bybit/ex.py,sha256=W_bYXafNR3aWdx0qpr06bGHl52xGBaKKcABleveHXng,4468
42
- xync_client/Bybit/order.py,sha256=H4UIb8hxFGnw1hZuSbr0yZ4qeaCOIZOMc6jEst0ycBs,1713
42
+ xync_client/Bybit/order.py,sha256=qDE6WDlpbYDdVJeIG33bIOPuMfgBBhbaVOR8LJa8Bms,2006
43
43
  xync_client/Bybit/web_earn.py,sha256=qjqS10xlFc8r40IhDdPZ0LxA2dFEGbvBGXdsrUUJCMo,3019
44
44
  xync_client/Bybit/web_p2p.py,sha256=sAXzK03t6jwDnz4rrvP2IzI0KxfKa7C_5GuzH1HwLuA,11768
45
45
  xync_client/Bybit/ws.py,sha256=OQjZHo_MiAH1dlOs3c-aUZBKyqToNTmH560udh6RYDE,1431
46
46
  xync_client/Bybit/etype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  xync_client/Bybit/etype/ad.py,sha256=qGCVc9eqc7lRRk0Hh3gD5i3HY0TXX-JdNY91VDoNg1I,5830
48
48
  xync_client/Bybit/etype/cred.py,sha256=CH6xqHh1214bLhHy_K9eVBb1SJVhWBfGb0l3LtBf3fU,1701
49
- xync_client/Bybit/etype/order.py,sha256=GbaBXBGAOdFC748qqsPAjEGdTy63IzUCb7f4tb9LddY,7868
49
+ xync_client/Bybit/etype/order.py,sha256=FAQ8ILI1E_Ow25KF8a9hinbI6kXY-t0aC0Xnrp4069o,7908
50
50
  xync_client/Gate/ex.py,sha256=QbhB3u7TWnvVGD-AknB2nay6KZjEXQ-1JT9UacX4sWI,3735
51
51
  xync_client/Gate/premarket.py,sha256=IW-CgkmNJePJR2j_NRfULNKTePMX35XlhldqdiO76zY,2138
52
52
  xync_client/Gate/etype/ad.py,sha256=-EwtFcOWWvtE6UjaOdsuXWDTCVjAIRK0kSEsqPP4Yls,1296
@@ -87,7 +87,7 @@ xync_client/Pms/Payeer/trade.py,sha256=Z_PSrXsFsecPam2sjuzImiwTXX-9cKGuJTbYOwLnL
87
87
  xync_client/Pms/Sber/__init__.py,sha256=dxQfd9ZPhFTc_C4xrwaxrV6p0SijDCLNzBeUv3oQG38,4926
88
88
  xync_client/Pms/Sber/utils.py,sha256=gIeJspwvoBbOBt-fjxwW4WDHPoL2Evs8LVufsjrFOfo,1870
89
89
  xync_client/Pms/Tinkoff/__init__.py,sha256=ZyLvBEUn-vh-85oPUUDS586AHgvx3c-mkQE3yBQtbw8,5580
90
- xync_client/Pms/Volet/__init__.py,sha256=mYJEmZwFoNU2B2erv7n8k6RA0PsXU12K7hpyrdEocrk,12088
90
+ xync_client/Pms/Volet/agent.py,sha256=K9GhwTHPQUK3TgobOZd_EYVp0GV-pGq_7MdcTUJJkPM,12056
91
91
  xync_client/Pms/Volet/api.py,sha256=6_dH2rzmyyvha3PeoiZdSltiAzKDWn8roSUJOAErX4M,3673
92
92
  xync_client/Pms/Volet/pl.py,sha256=l7lvUrpjFoObXPHaseOIAcSbkNqJdpy6OLDutxYJH3U,2451
93
93
  xync_client/Pms/Volet/_todo_req/req.mjs,sha256=ut3Jw37rL5lY7SskjZ9f1l0VE33tuP-PZEYUTcJMc2I,817
@@ -104,7 +104,7 @@ xync_client/TgWallet/order.py,sha256=BOmBx5WWfJv0-_-A8DcR-Xd8utqO_VTmSqSegm0cteQ
104
104
  xync_client/TgWallet/pyd.py,sha256=Ys3E8b3RLuyQ26frWT0F0BorkNxVpxnd18tY4Gp9dik,5636
105
105
  xync_client/TgWallet/pyro.py,sha256=2K7QWdo48k4MbbgQt90gdz_HiPck69Njm4xaMjIVgoo,1440
106
106
  xync_client/TgWallet/web.py,sha256=kDcv9SKKQPe91mw1qJBpbuyKYCAmZdfdHJylHumLBVU,1608
107
- xync_client-0.0.156.dev18.dist-info/METADATA,sha256=9bvLxkPjU71Y-6Ur0-1dbXTfTDqFnucO3LLzMJrqLAs,1187
108
- xync_client-0.0.156.dev18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
- xync_client-0.0.156.dev18.dist-info/top_level.txt,sha256=bmYEVIIrD3v7yFwH-X15pEfRvzhuAdfsAZ2igvNI4O8,12
110
- xync_client-0.0.156.dev18.dist-info/RECORD,,
107
+ xync_client-0.0.162.dist-info/METADATA,sha256=fObLDRD-fDTgXnRXbmMBiioSGGgr0WgwS12thRoLeII,1181
108
+ xync_client-0.0.162.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ xync_client-0.0.162.dist-info/top_level.txt,sha256=bmYEVIIrD3v7yFwH-X15pEfRvzhuAdfsAZ2igvNI4O8,12
110
+ xync_client-0.0.162.dist-info/RECORD,,