xync-client 0.0.156.dev18__py3-none-any.whl → 0.0.164__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,19 @@ 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("ad__pair_side__pair__coin", "ad__pair_side__pair__cur")
308
280
  if myad.hex and myad.shared_at + timedelta(minutes=55) > now(): # check expired
309
281
  # check validity
310
282
  data = await self._post("/x-api/fiat/otc/item/shareItem/info", {"shareCode": myad.hex.hex()})
@@ -312,15 +284,22 @@ class AgentClient(BaseAgentClient): # Bybit client
312
284
  return myad.get_url()
313
285
  data = await self._post("/x-api/fiat/otc/item/share", {"itemId": str(myad.ad.exid)})
314
286
  if data["ret_code"] == 912300058:
315
- raise ShareException(f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} выключена")
316
- if data["ret_code"] == 912300059:
317
- raise ShareException("Торговля выключена")
318
- if data["ret_code"] != 0: # Новая ошибка
287
+ raise ShareException(f"Ad {myad.id}:{myad.ad.id}:{myad.ad.exid} agent:{myad.ad.maker.agent_id} offline")
288
+ elif data["ret_code"] == 912300059:
289
+ raise ShareException(f"Торговля мейкера {myad.ad.maker_id} выключена")
290
+ elif data["ret_code"] == 10007:
291
+ raise ShareException(f"Авторизация агента {myad.ad.maker.agent_id} слетела")
292
+ elif data["ret_code"] == 912300064:
293
+ raise ShareException(f"Ad {myad.ad.exid} maker:{myad.ad.maker_id} shared too much")
294
+ elif data["ret_code"] != 0: # Новая ошибка
319
295
  raise ShareException(data)
320
296
  url = data["result"]["shareLink"]
321
297
  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())
298
+ side = "buy" if myad.ad.pair_side.is_sell else "sell" # inverse for taker
299
+ coin, cur = myad.ad.pair_side.pair.coin.ticker, myad.ad.pair_side.pair.cur.ticker
300
+ pref = models.MyAd.WEB.format(side=side, coin=coin, cur=cur)
301
+ hx = resp.url.query["by_web_link"].replace(pref, "")
302
+ _r = await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
324
303
  await myad.refresh_from_db()
325
304
  return myad.get_url()
326
305
 
@@ -535,7 +514,7 @@ class AgentClient(BaseAgentClient): # Bybit client
535
514
  },
536
515
  )
537
516
  if res["ret_code"] == 0:
538
- return {o.id: OrderItem(**o) for o in res["result"]["items"]}
517
+ return {int(o["id"]): OrderItem(**o) for o in res["result"]["items"]}
539
518
  return res["ret_code"]
540
519
 
541
520
  def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
@@ -728,8 +707,7 @@ class AgentClient(BaseAgentClient): # Bybit client
728
707
  for oid, o in ords.items():
729
708
  if o.status != Status.completed.value or oid in self.completed_orders:
730
709
  continue
731
- fo = self.api.get_order_details(orderId=o.id)
732
- order = OrderFull.model_validate(fo["result"])
710
+ order = await self.get_order_full(o.id)
733
711
  order_db = await self.create_order_db(order)
734
712
  await sleep(1)
735
713
  dmsgs = self.api.get_chat_messages(orderId=oid, size=200)["result"]["result"][::-1]
@@ -900,27 +878,30 @@ class AgentClient(BaseAgentClient): # Bybit client
900
878
  am = 500 + i
901
879
  req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
902
880
  ord_resp: OrderResp = await self.take_ad(req)
903
- order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
881
+ order: OrderFull = await self.get_order_full(int(ord_resp.orderId))
904
882
  odb = await self.create_order_db(order)
905
883
  t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
906
884
  await t.fetch_related("order__cred__pmcur__cur")
907
885
  await self.pm_clients[366].send(t)
908
886
  ...
909
887
 
910
- pm_clients: dict[int, PmAgentClient]
888
+ async def load_pending_orders(self):
889
+ po: dict[int, OrderItem] = await self.get_pending_orders()
890
+ if isinstance(po, int): # если код ошибки вместо результата
891
+ raise ValueError(po)
892
+ self.orders = {
893
+ o.exid: (o, await self.get_order_full(o.exid)) for o in await models.Order.filter(exid__in=po.keys())
894
+ }
895
+ for oid in po.keys() - self.orders.keys():
896
+ await self.load_order(oid)
911
897
 
912
- async def start_listen(self):
898
+ async def _start_listen(self):
913
899
  t = await self.ott()
914
900
  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):
901
+ did = self.agent.auth["cookies"]["deviceId"]
921
902
  u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}&timestamp={ts}"
922
903
  async with websockets.connect(u) as websocket:
923
- auth_msg = json.dumps({"req_id": did, "op": "login", "args": [tok]})
904
+ auth_msg = json.dumps({"req_id": did, "op": "login", "args": [t["result"]]})
924
905
  await websocket.send(auth_msg)
925
906
 
926
907
  sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
@@ -947,222 +928,213 @@ class AgentClient(BaseAgentClient): # Bybit client
947
928
  await websocket.send(sub_msg)
948
929
  while resp := await websocket.recv():
949
930
  if data := json.loads(resp):
950
- upd, order_db = None, None
951
931
  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")
932
+ await self.proc(data)
933
+
934
+ async def proc(self, data: dict):
935
+ match data.get("topic"):
936
+ case "OTC_ORDER_STATUS":
937
+ match data["type"]:
938
+ case "STATUS_CHANGE":
939
+ upd = StatusChange.model_validate(data["data"])
940
+ order_db, order = await self.load_order(upd.id)
941
+ match upd.status:
942
+ case Status.ws_new:
943
+ logging.info(f"Order {upd.id} CREATED at {upd.createDate}")
944
+ # await self.got_new_order(order_db, order)
945
+
946
+ # # сразу уменьшаем доступный остаток монеты/валюты
947
+ # await self.money_upd(order_db)
948
+ # if upd.side: # я покупатель - ждем мою оплату
949
+ # _dest = order.paymentTermList[0].accountNo
950
+ # if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
951
+ # return
952
+ # await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
953
+ # await self.send_payment(order_db)
954
+ case Status.created:
955
+ if upd.side == 0: # я продавец, ждем когда покупатель оплатит
956
+ # check_payment() # again
957
+ ...
958
+ # if not (pmacdx := await self.get_pma_by_cdex(order)):
959
+ # return
960
+ # pma, cdx = pmacdx
961
+ # am, tid = await pma.check_in(
962
+ # float(order.amount),
963
+ # cdx.cred.pmcur.cur.ticker,
964
+ # # todo: почему в московском час.поясе?
965
+ # datetime.fromtimestamp(float(order.transferDate) / 1000),
966
+ # )
967
+ # if not tid:
968
+ # logging.info(f"Order {order.id} created at {order.createDate}, not paid yet")
969
+ # return
970
+ # try:
971
+ # t, is_new = await models.Transfer.update_or_create(
972
+ # dict(
973
+ # amount=int(float(order.amount) * 100),
974
+ # order=order_db,
975
+ # ),
976
+ # pmid=tid,
977
+ # )
978
+ # except IntegrityError as e:
979
+ # logging.error(tid)
980
+ # logging.error(order)
981
+ # logging.exception(e)
982
+ #
983
+ # if not is_new: # если по этому платежу уже отпущен другая продажа
984
+ # return
985
+ #
986
+ # # если висят незавершенные продажи с такой же суммой
987
+ # pos = (await self.get_orders_active(1))["result"]
988
+ # pos = [
989
+ # o
990
+ # for o in pos.get("items", [])
991
+ # if (
992
+ # o["amount"] == order.amount
993
+ # and o["id"] != upd.id
994
+ # and int(order.createDate) < int(o["createDate"]) + 15 * 60 * 1000
995
+ # # get full_order from o, and cred or pm from full_order:
996
+ # and self.api.get_order_details(orderId=o["id"])["result"][
997
+ # "paymentTermList"
998
+ # ][0]["accountNo"]
999
+ # == order.paymentTermList[0].accountNo
1000
+ # )
1001
+ # ]
1002
+ # curex = await models.CurEx.get(cur__ticker=order.currencyId, ex=self.ex_client.ex)
1003
+ # pos_db = await models.Order.filter(
1004
+ # exid__not=order.id,
1005
+ # cred_id=order_db.cred_id,
1006
+ # amount=int(float(order.amount) * 10**curex.scale),
1007
+ # status__not_in=[OrderStatus.completed, OrderStatus.canceled],
1008
+ # created_at__gt=now() - timedelta(minutes=15),
1009
+ # )
1010
+ # if pos or pos_db:
1011
+ # await self.ex_client.bot.send(
1012
+ # f"[Duplicate amount!]"
1013
+ # f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
1014
+ # self.actor.person.user.username_id,
1015
+ # )
1016
+ # logging.warning("Duplicate amount!")
1017
+ # return
1018
+ #
1019
+ # # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
1020
+ # self.api.release_assets(orderId=upd.id)
1021
+ # logging.info(
1022
+ # f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
1023
+ # )
1024
+ elif upd.side == 1: # я покупатель - ждем мою оплату
1025
+ # pay()
1026
+ logging.warning(f"Order {upd.id} CREATED2 at {now()}")
1027
+
1028
+ case Status.paid:
1029
+ if order_db.status == OrderStatus.paid:
1030
+ return
1031
+ await order_db.update_from_dict(
1032
+ {
1033
+ "status": OrderStatus.paid,
1034
+ "payed_at": datetime.fromtimestamp(float(order.transferDate) / 1000),
1035
+ }
1036
+ ).save()
1037
+ logging.info(f"Order {order.id} payed at {order_db.payed_at}")
1038
+
1039
+ case Status.appealed_by_seller: # just any appealed
1040
+ # todo: appealed by WHO? щас наугад стоит by_seller
1041
+ await order_db.update_from_dict(
1042
+ {
1043
+ "status": OrderStatus.appealed_by_seller,
1044
+ "appealed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
1045
+ }
1046
+ ).save()
1047
+ logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
1048
+
1049
+ case Status.canceled:
1050
+ await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
1051
+ logging.info(f"Order {order.id} canceled at {datetime.now()}")
1052
+ # await self.money_upd(order_db)
1053
+
1054
+ case Status.completed:
1055
+ await order_db.refresh_from_db()
1056
+ if order_db.status != OrderStatus.completed:
1057
+ await order_db.update_from_dict(
1058
+ {
1059
+ "status": OrderStatus.completed,
1060
+ "confirmed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
1061
+ }
1062
+ ).save(update_fields=["status", "confirmed_at"])
1063
+ # await self.money_upd(order_db)
1064
+
1065
+ case _:
1066
+ logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
1067
+ case "COUNT_DOWN":
1068
+ upd = CountDown.model_validate(data["data"])
1069
+
1070
+ case "OTC_USER_CHAT_MSG":
1071
+ match data["type"]:
1072
+ case "RECEIVE":
1073
+ upd = Receive.model_validate(data["data"])
1074
+ order_db, order = await self.load_order(upd.orderId)
1075
+ # got_msg()
1076
+ ...
1077
+ # im_taker = order_db.taker_id == self.actor.id
1078
+ # im_buyer = order_db.ad.pair_side.is_sell == im_taker
1079
+ # if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
1080
+ # msg, _ = await models.Msg.update_or_create(
1081
+ # {
1082
+ # "to_maker": upd.userId == self.actor.exid and im_taker,
1083
+ # "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
1084
+ # },
1085
+ # txt=upd.message,
1086
+ # order=order_db,
1087
+ # )
1088
+ # if not upd.message:
1089
+ # ...
1090
+ # if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
1091
+ # if not order_db.cred.detail.startswith(dest := g.group()):
1092
+ # order_db.cred.detail = dest
1093
+ # await order_db.save()
1094
+ # await self.send_payment(order_db)
1095
+ case "READ":
1096
+ # msg_read()
1097
+ upd = Read.model_validate(data["data"])
1098
+
1099
+ case "CLEAR":
1100
+ return
1101
+ case "OTC_USER_CHAT_MSG_V2":
1102
+ # msg dup
1103
+ ...
1104
+ # match data["type"]:
1105
+ # case "RECEIVE":
1106
+ # upd = Receive.model_validate(data["data"])
1107
+ # case "READ":
1108
+ # upd = Read.model_validate(data["data"])
1109
+ # case "CLEAR":
1110
+ # pass
1111
+ # case _:
1112
+ # self.listen(data)
1113
+ case "SELLER_CANCEL_CHANGE":
1114
+ upd = SellerCancelChange.model_validate(data["data"])
1115
+ case None:
1116
+ if not data.get("success"):
1117
+ logging.error(data, "NOT SUCCESS!")
1118
+ else:
1119
+ return # success login, subscribes, input
1120
+ case _:
1121
+ logging.warning(data, "UNKNOWN TOPIC")
1122
+
1123
+ async def get_order_full(self, oid: int) -> OrderFull:
1124
+ order = self.api.get_order_details(orderId=oid)
1125
+ return OrderFull.model_validate(order["result"])
1126
+
1127
+ async def load_order(self, oid: int) -> tuple[models.Order, OrderFull]:
1128
+ if not self.orders.get(oid):
1129
+ order = await self.get_order_full(oid)
1130
+ if not (
1131
+ order_db := await models.Order.get_or_none(exid=oid, ad__maker__ex=self.actor.ex).prefetch_related(
1132
+ "ad__pair_side__pair", "cred__pmcur__cur"
1133
+ )
1134
+ ):
1135
+ order_db = await self.create_order_db(order)
1136
+ self.orders[oid] = order_db, order
1137
+ return self.orders[oid]
1166
1138
 
1167
1139
  async def money_upd(self, odb: models.Order):
1168
1140
  # обновляем остаток монеты
@@ -1279,11 +1251,6 @@ class AgentClient(BaseAgentClient): # Bybit client
1279
1251
  return pma, order_db.cred.pmcur.cur.ticker
1280
1252
  logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
1281
1253
 
1282
- @staticmethod
1283
- def listen(data: dict | None):
1284
- # print(data)
1285
- ...
1286
-
1287
1254
 
1288
1255
  def ms2utc(msk_ts_str: str):
1289
1256
  return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
@@ -1334,7 +1301,7 @@ async def main():
1334
1301
  cn = await init_db(TORM)
1335
1302
 
1336
1303
  agent = (
1337
- await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=2)
1304
+ await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=5)
1338
1305
  .prefetch_related(
1339
1306
  "actor__ex",
1340
1307
  "actor__person__user__gmail",
@@ -1364,12 +1331,11 @@ async def main():
1364
1331
  # await cl.ex_client.set_pairs()
1365
1332
  # await cl.ex_client.set_pms()
1366
1333
 
1367
- # await cl.set_creds()
1368
- # await cl.export_my_ads()
1334
+ await cl.set_creds()
1335
+ await cl.export_my_ads()
1369
1336
 
1370
1337
  my_ad = await models.MyAd[5]
1371
1338
  await cl.ad_share(my_ad.id)
1372
- await cl.start_listen()
1373
1339
 
1374
1340
  ms = await models.Agent.filter(
1375
1341
  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.164
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=Cy0SiY_lKId6xetYky6x-46KSbRspDXk5ZBI-7TB5Gg,66405
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.164.dist-info/METADATA,sha256=Y270ruTQR3Yxw2HmI5QQGJIaS2m3t_l3iJY81yi0vuQ,1181
108
+ xync_client-0.0.164.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ xync_client-0.0.164.dist-info/top_level.txt,sha256=bmYEVIIrD3v7yFwH-X15pEfRvzhuAdfsAZ2igvNI4O8,12
110
+ xync_client-0.0.164.dist-info/RECORD,,