xync-client 0.0.155__py3-none-any.whl → 0.0.156.dev18__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.
@@ -1,27 +1,19 @@
1
- import json
2
1
  import logging
3
2
  import re
4
- import traceback
5
- from datetime import datetime, timezone, timedelta
6
- from uuid import uuid4
7
-
8
- import websockets
9
- from asyncio import run, sleep
10
- from decimal import Decimal
11
-
3
+ from asyncio import create_task
4
+ from datetime import datetime, timedelta
12
5
  from bybit_p2p import P2P
13
- from playwright.async_api import async_playwright
14
- from pydantic import ValidationError
15
6
  from pyro_client.client.file import FileClient
16
7
  from tortoise.exceptions import IntegrityError
17
8
  from tortoise.timezone import now
18
- from tortoise.transactions import in_transaction
19
9
  from xync_bot import XyncBot
10
+ from xync_schema.models import Agent
11
+
12
+ from xync_client.Bybit.agent import AgentClient
20
13
  from xync_client.Bybit.ex import ExClient
21
14
 
22
- from xync_client.Abc.PmAgent import PmAgentClient
23
15
  from xync_schema import models
24
- from xync_schema.enums import UserStatus, OrderStatus
16
+ from xync_schema.enums import OrderStatus
25
17
 
26
18
  from xync_client.Bybit.etype.order import (
27
19
  StatusChange,
@@ -30,436 +22,233 @@ from xync_client.Bybit.etype.order import (
30
22
  Read,
31
23
  Receive,
32
24
  OrderFull,
33
- StatusApi,
25
+ OrderItem,
26
+ Status,
34
27
  )
35
- from xync_client.loader import NET_TOKEN, PAY_TOKEN
36
- from xync_client.Abc.InAgent import BaseInAgentClient
37
28
 
38
29
 
39
- class InAgentClient(BaseInAgentClient):
30
+ class InAgentClient(AgentClient):
40
31
  actor: models.Actor
41
32
  agent: models.Agent
42
33
  api: P2P
43
34
  ex_client: ExClient
44
- pm_clients: dict[int, PmAgentClient]
45
-
46
- async def start_listen(self):
47
- t = await self.ott()
48
- ts = int(float(t["time_now"]) * 1000)
49
- await self.ws_prv(self.agent.auth["deviceId"], t["result"], ts)
50
-
51
- # 3N: [T] - Уведомление об одобрении запроса на сделку
52
- async def request_accepted_notify(self) -> int: ... # id
53
-
54
- async def ws_prv(self, did: str, tok: str, ts: int):
55
- u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}&timestamp={ts}"
56
- async with websockets.connect(u) as websocket:
57
- auth_msg = json.dumps({"req_id": did, "op": "login", "args": [tok]})
58
- await websocket.send(auth_msg)
59
-
60
- sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
61
- await websocket.send(sub_msg)
62
- sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"SUPER_DEAL"}']})
63
- await websocket.send(sub_msg)
64
- sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"OTC_ORDER_STATUS"}']})
65
- await websocket.send(sub_msg)
66
- sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"WEB_THREE_SELL"}']})
67
- await websocket.send(sub_msg)
68
- sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"APPEALED_CHANGE"}']})
69
- await websocket.send(sub_msg)
70
-
71
- sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order"]})
72
- await websocket.send(sub_msg)
73
- sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-eftd-complete-privilege-event"]})
74
- await websocket.send(sub_msg)
75
- sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-savings-product-event"]})
76
- await websocket.send(sub_msg)
77
- sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.deal-core.order-savings-complete-event"]})
78
- await websocket.send(sub_msg)
79
35
 
80
- sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
81
- await websocket.send(sub_msg)
82
- while resp := await websocket.recv():
83
- if data := json.loads(resp):
84
- upd, order_db = None, None
85
- logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
86
- match data.get("topic"):
87
- case "OTC_ORDER_STATUS":
88
- match data["type"]:
89
- case "STATUS_CHANGE":
36
+ orders: dict[int, models.Order] = {}
37
+
38
+ def __init__(self, agent: Agent, ex_client: ExClient, fbot: FileClient, bbot: XyncBot, **kwargs):
39
+ super().__init__(agent, ex_client, fbot, bbot, **kwargs)
40
+ create_task(self.load_pending_orders())
41
+
42
+ async def load_pending_orders(self):
43
+ po: dict[int, OrderItem] = await self.get_pending_orders()
44
+ if isinstance(po, int): # если код ошибки вместо результата
45
+ raise ValueError(po)
46
+ self.orders = {o.exid: o for o in await models.Order.filter(exid__in=po.keys())}
47
+ for oid in po.keys() - self.orders.keys():
48
+ fo = self.api.get_order_details(orderId=oid)
49
+ 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
90
91
  try:
91
- upd = StatusChange.model_validate(data["data"])
92
- except ValidationError as e:
93
- logging.error(e)
94
- logging.error(data["data"])
95
- order = self.api.get_order_details(orderId=upd.id)
96
- order = OrderFull.model_validate(order["result"])
97
- order_db = await models.Order.get_or_none(
98
- exid=order.id, ad__exid=order.itemId
99
- ) or await self.create_order(order)
100
- match upd.status:
101
- case StatusApi.created:
102
- logging.info(f"Order {order.id} created at {order.createDate}")
103
- # сразу уменьшаем доступный остаток монеты/валюты
104
- await self.money_upd(order_db)
105
- if upd.side: # я покупатель - ждем мою оплату
106
- _dest = order.paymentTermList[0].accountNo
107
- if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
108
- continue
109
- await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
110
- await self.send_payment(order_db)
111
- case StatusApi.wait_for_buyer:
112
- if upd.side == 0: # ждем когда покупатель оплатит
113
- if not (pmacdx := await self.get_pma_by_cdex(order)):
114
- continue
115
- pma, cdx = pmacdx
116
- am, tid = await pma.check_in(
117
- Decimal(order.amount),
118
- cdx.cred.pmcur.cur.ticker,
119
- # todo: почему в московском час.поясе?
120
- datetime.fromtimestamp(float(order.transferDate) / 1000),
121
- )
122
- if not tid:
123
- logging.info(
124
- f"Order {order.id} created at {order.createDate}, not paid yet"
125
- )
126
- continue
127
- try:
128
- t, is_new = await models.Transfer.update_or_create(
129
- dict(
130
- amount=int(float(order.amount) * 100),
131
- order=order_db,
132
- ),
133
- pmid=tid,
134
- )
135
- except IntegrityError as e:
136
- logging.error(tid)
137
- logging.error(order)
138
- logging.exception(e)
139
-
140
- if not is_new: # если по этому платежу уже отпущен другая продажа
141
- continue
142
-
143
- # если висят незавершенные продажи с такой же суммой
144
- pos = (await self.get_orders_active(1))["result"]
145
- pos = [
146
- o
147
- for o in pos.get("items", [])
148
- if (
149
- o["amount"] == order.amount
150
- and o["id"] != upd.id
151
- and int(order.createDate)
152
- < int(o["createDate"]) + 15 * 60 * 1000
153
- # get full_order from o, and cred or pm from full_order:
154
- and self.api.get_order_details(orderId=o["id"])["result"][
155
- "paymentTermList"
156
- ][0]["accountNo"]
157
- == order.paymentTermList[0].accountNo
158
- )
159
- ]
160
- curex = await models.CurEx.get(
161
- cur__ticker=order.currencyId, ex=self.ex_client.ex
162
- )
163
- pos_db = await models.Order.filter(
164
- exid__not=order.id,
165
- cred_id=order_db.cred_id,
166
- amount=int(float(order.amount) * 10**curex.scale),
167
- status__not_in=[OrderStatus.completed, OrderStatus.canceled],
168
- created_at__gt=now() - timedelta(minutes=15),
169
- )
170
- if pos or pos_db:
171
- await self.ex_client.bot.send(
172
- f"[Duplicate amount!]"
173
- f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
174
- self.actor.person.user.username_id,
175
- )
176
- logging.warning("Duplicate amount!")
177
- continue
178
-
179
- # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
180
- self.api.release_assets(orderId=upd.id)
181
- logging.info(
182
- f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
183
- )
184
- elif upd.side == 1: # я покупатель - ждем мою оплату
185
- continue # logging.warning(f"Order {order.id} PAID at {now()}: {int_am}")
186
- else:
187
- ...
188
- # todo: check is always canceling
189
- # await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
190
- # logging.info(f"Order {order.id} canceled at {datetime.now()}")
191
-
192
- case StatusApi.wait_for_seller:
193
- if order_db.status == OrderStatus.paid:
194
- continue
195
- await order_db.update_from_dict(
196
- {
197
- "status": OrderStatus.paid,
198
- "payed_at": datetime.fromtimestamp(
199
- float(order.transferDate) / 1000
200
- ),
201
- }
202
- ).save()
203
- logging.info(f"Order {order.id} payed at {order_db.payed_at}")
204
-
205
- case StatusApi.appealed:
206
- # todo: appealed by WHO? щас наугад стоит by_seller
207
- await order_db.update_from_dict(
208
- {
209
- "status": OrderStatus.appealed_by_seller,
210
- "appealed_at": datetime.fromtimestamp(
211
- float(order.updateDate) / 1000
212
- ),
213
- }
214
- ).save()
215
- logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
216
-
217
- case StatusApi.canceled:
218
- await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
219
- logging.info(f"Order {order.id} canceled at {datetime.now()}")
220
- await self.money_upd(order_db)
221
-
222
- case StatusApi.completed:
223
- await order_db.update_from_dict(
224
- {
225
- "status": OrderStatus.completed,
226
- "confirmed_at": datetime.fromtimestamp(
227
- float(order.updateDate) / 1000
228
- ),
229
- }
230
- ).save()
231
- await self.money_upd(order_db)
232
-
233
- case _:
234
- logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
235
- case "COUNT_DOWN":
236
- upd = CountDown.model_validate(data["data"])
237
- case _:
238
- self.listen(data)
239
- case "OTC_USER_CHAT_MSG":
240
- match data["type"]:
241
- case "RECEIVE":
242
- upd = Receive.model_validate(data["data"])
243
- if order_db := await models.Order.get_or_none(
244
- exid=upd.orderId, ad__maker__ex=self.actor.ex
245
- ).prefetch_related("ad__pair_side__pair", "cred__pmcur__cur"):
246
- im_taker = order_db.taker_id == self.actor.id
247
- im_buyer = order_db.ad.pair_side.is_sell == im_taker
248
- if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
249
- msg, _ = await models.Msg.update_or_create(
250
- {
251
- "to_maker": upd.userId == self.actor.exid and im_taker,
252
- "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
253
- },
254
- txt=upd.message,
92
+ t, is_new = await models.Transfer.update_or_create(
93
+ dict(
94
+ amount=int(float(order.amount) * 100),
255
95
  order=order_db,
256
- )
257
- if not upd.message:
258
- ...
259
- if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
260
- if not order_db.cred.detail.startswith(dest := g.group()):
261
- order_db.cred.detail = dest
262
- await order_db.save()
263
- await self.send_payment(order_db)
264
- case "READ":
265
- upd = Read.model_validate(data["data"])
266
- # if upd.status not in (StatusWs.created, StatusWs.canceled, 10, StatusWs.completed):
267
- if upd.orderStatus in (
268
- StatusApi.wait_for_buyer,
269
- ): # todo: тут приходит ордер.статус=10, хотя покупатель еще не нажал оплачено
270
- order = self.api.get_order_details(orderId=upd.orderId)["result"]
271
- order = OrderFull.model_validate(order)
272
-
273
- case "CLEAR":
274
- continue
275
- case _:
276
- self.listen(data)
277
- case "OTC_USER_CHAT_MSG_V2":
278
- # match data["type"]:
279
- # case "RECEIVE":
280
- # upd = Receive.model_validate(data["data"])
281
- # case "READ":
282
- # upd = Read.model_validate(data["data"])
283
- # case "CLEAR":
284
- # pass
285
- # case _:
286
- # self.listen(data)
287
- continue
288
- case "SELLER_CANCEL_CHANGE":
289
- upd = SellerCancelChange.model_validate(data["data"])
290
- case None:
291
- if not data.get("success"):
292
- logging.error(data, "NOT SUCCESS!")
293
- else:
294
- continue # success login, subscribes, input
295
- case _:
296
- logging.warning(data, "UNKNOWN TOPIC")
297
- if not upd:
298
- logging.warning(data, "NOT PROCESSED UPDATE")
299
-
300
- async def money_upd(self, odb: models.Order):
301
- # обновляем остаток монеты
302
- await odb.fetch_related("ad__pair_side__pair", "ad__my_ad__credexs__cred__fiat", "cred__pmcur", "transfer")
303
- ass = await models.Asset.get(addr__coin_id=odb.ad.pair_side.pair.coin_id, addr__actor=self.actor)
304
- # обновляем остаток валюты
305
- im_maker = odb.ad.maker_id == self.actor.id
306
- im_seller = odb.ad.pair_side.is_sell == im_maker
307
- if im_maker:
308
- if _fiats := [cx.cred.fiat for cx in odb.ad.my_ad.credexs if cx.cred.fiat]:
309
- fiat = _fiats[0]
310
- await fiat.fetch_related("cred__pmcur__pm")
311
- else:
312
- raise ValueError(odb, "No Fiat")
313
- elif im_seller: # im taker
314
- fltr = dict(cred__person_id=self.actor.person_id)
315
- fltr |= (
316
- {"cred__ovr_pm_id": odb.cred.ovr_pm_id, "cred__pmcur__cur_id": odb.cred.pmcur.cur_id}
317
- if odb.cred.ovr_pm_id
318
- else {"cred__pmcur_id": odb.cred.pmcur_id}
319
- )
320
- if not (fiat := await models.Fiat.get_or_none(**fltr).prefetch_related("cred__pmcur__pm")):
321
- raise ValueError(odb, "No Fiat")
322
- fee = round(odb.amount * (fiat.cred.pmcur.pm.fee or 0) * 0.0001)
323
- # k = int(im_seller) * 2 - 1 # im_seller: 1, im_buyer: -1
324
- if odb.status == OrderStatus.created:
325
- if im_seller:
326
- ass.free -= odb.quantity
327
- ass.freeze += odb.quantity
328
- else: # я покупатель
329
- fiat.amount -= odb.amount + fee
330
- elif odb.status == OrderStatus.completed:
331
- if im_seller:
332
- fiat.amount += odb.amount
333
- else: # я покупатель
334
- ass.free += odb.quantity
335
- elif odb.status == OrderStatus.canceled:
336
- if im_seller:
337
- ass.free += odb.quantity
338
- ass.freeze -= odb.quantity
339
- else: # я покупатель
340
- fiat.amount += odb.amount + fee
341
- else:
342
- logging.exception(odb.id, f"STATUS: {odb.status.name}")
343
- await ass.save(update_fields=["free", "freeze"])
344
- await fiat.save(update_fields=["amount"])
345
- logging.info(f"Order #{odb.id} {odb.status.name}. Fiat: {fiat.amount}, Asset: {ass.free}")
346
-
347
- async def send_payment(self, order_db: models.Order):
348
- if order_db.status != OrderStatus.created:
349
- return
350
- fmt_am = round(order_db.amount * 10**-2, 2)
351
- pma, cur = await self.get_pma_by_pmex(order_db)
352
- async with in_transaction():
353
- # отмечаем ордер на бирже "оплачен"
354
- pmex = await models.PmEx.get(pm_id=order_db.cred.pmcur.pm_id, ex=self.actor.ex)
355
- credex = await models.CredEx.get(cred=order_db.cred, ex=self.actor.ex)
356
- self.api.mark_as_paid(
357
- orderId=str(order_db.exid),
358
- paymentType=pmex.exid, # pmex.exid
359
- paymentId=str(credex.exid), # credex.exid
360
- )
361
- # проверяем не отправляли ли мы уже перевод по этому ордеру
362
- if t := await models.Transfer.get_or_none(order=order_db, amount=order_db.amount):
363
- await pma.bot.send(
364
- f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!",
365
- self.actor.person.user.username_id,
366
- )
367
- raise Exception(
368
- f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!"
369
- )
370
-
371
- # ставим в бд статус "оплачен"
372
- order_db.status = OrderStatus.paid
373
- order_db.payed_at = datetime.now(timezone.utc)
374
- await order_db.save()
375
- # создаем перевод в бд
376
- t = models.Transfer(order=order_db, amount=order_db.amount, updated_at=now())
377
- # отправляем деньги
378
- tid, img = await pma.send(t)
379
- t.pmid = tid
380
- await t.save()
381
- await self.send_receipt(str(order_db.exid), tid) # отправляем продавцу чек
382
- logging.info(f"Order {order_db.exid} PAID at {datetime.now()}: {fmt_am}!")
383
-
384
- async def send_receipt(self, oexid: str, tid: int) -> tuple[PmAgentClient | None, models.CredEx] | None:
385
- try:
386
- if res := self.api.upload_chat_file(upload_file=f"tmp/{tid}.png").get("result"):
387
- await sleep(0.5)
388
- self.api.send_chat_message(orderId=oexid, contentType="pic", message=res["url"], msgUuid=uuid4().hex)
389
- except Exception as e:
390
- logging.error(e)
391
- await sleep(0.5)
392
- self.api.send_chat_message(orderId=oexid, contentType="str", message=f"#{tid}", msgUuid=uuid4().hex)
393
-
394
- async def get_pma_by_cdex(self, order: OrderFull) -> tuple[PmAgentClient | None, models.CredEx] | None:
395
- cdxs = await models.CredEx.filter(
396
- ex=self.ex_client.ex,
397
- exid__in=[ptl.id for ptl in order.paymentTermList],
398
- cred__person=self.actor.person,
399
- ).prefetch_related("cred__pmcur__cur")
400
- pmas = [pma for cdx in cdxs if (pma := self.pm_clients.get(cdx.cred.pmcur.pm_id))]
401
- if not len(pmas):
402
- # raise ValueError(order.paymentTermList, f"No pm_agents for {order.paymentTermList[0].paymentType}")
403
- return None
404
- elif len(pmas) > 1:
405
- logging.error(order.paymentTermList, f">1 pm_agents for {cdxs[0].cred.pmcur.pm_id}")
406
- else:
407
- return pmas[0], cdxs[0]
408
-
409
- async def get_pma_by_pmex(self, order_db: models.Order) -> tuple[PmAgentClient, str]:
410
- pma = self.pm_clients.get(order_db.cred.pmcur.pm_id)
411
- if pma:
412
- return pma, order_db.cred.pmcur.cur.ticker
413
- logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
414
-
415
- @staticmethod
416
- def listen(data: dict | None):
417
- # print(data)
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")
418
252
  ...
419
-
420
-
421
- async def main():
422
- from x_model import init_db
423
- from xync_client.loader import TORM
424
-
425
- cn = await init_db(TORM, True)
426
- logging.basicConfig(level=logging.INFO)
427
-
428
- agent = (
429
- await models.Agent.filter(
430
- actor__ex_id=4,
431
- status__in=[3],
432
- auth__isnull=False,
433
- actor__person__user__status=UserStatus.ACTIVE,
434
- actor__person__user__pm_agents__pm_id=366,
435
- actor__person_id=1,
436
- )
437
- .prefetch_related("actor__ex", "actor__person__user__gmail")
438
- .first()
439
- )
440
- pm_agents = await models.PmAgent.filter(
441
- active=True,
442
- auth__isnull=False,
443
- user__status=UserStatus.ACTIVE,
444
- ).prefetch_related("pm", "user__gmail")
445
-
446
- bbot = XyncBot(PAY_TOKEN, cn)
447
-
448
- async with FileClient(NET_TOKEN) as b:
449
- b: FileClient
450
- cl = InAgentClient(agent, b, bbot)
451
- # await cl.agent_client.export_my_ads()
452
- # payeer_cl = Client(actor.person.user.username_id)
453
- for pma in pm_agents:
454
- pcl: PmAgentClient = pma.client(bbot)
455
- cl.pm_clients[pma.pm_id] = await pcl.start(await async_playwright().start(), False)
456
- try:
457
- _ = await cl.start_listen()
458
- except Exception as e:
459
- await b.send("😱Bybit InAgent CRASHED!!!😱", agent.actor.person.user.username_id)
460
- await b.send(f"```\n{''.join(traceback.format_exception(e))}\n```", agent.actor.person.user.username_id)
461
- await cl.close()
462
-
463
-
464
- if __name__ == "__main__":
465
- run(main())
253
+ if not upd:
254
+ logging.warning(data, "NOT PROCESSED UPDATE")