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