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.
- xync_client/Abc/AdLoader.py +0 -294
- xync_client/Abc/Agent.py +313 -51
- xync_client/Abc/Ex.py +414 -12
- xync_client/Abc/xtype.py +34 -2
- xync_client/Bybit/InAgent.py +225 -436
- xync_client/Bybit/agent.py +529 -397
- xync_client/Bybit/etype/__init__.py +0 -0
- xync_client/Bybit/etype/ad.py +47 -34
- xync_client/Bybit/etype/order.py +33 -48
- xync_client/Bybit/ex.py +20 -46
- xync_client/Htx/agent.py +82 -40
- xync_client/Htx/etype/ad.py +22 -5
- xync_client/Htx/etype/order.py +194 -0
- xync_client/Htx/ex.py +16 -16
- xync_client/Mexc/agent.py +196 -13
- xync_client/Mexc/api.py +955 -336
- xync_client/Mexc/etype/ad.py +52 -1
- xync_client/Mexc/etype/order.py +131 -416
- xync_client/Mexc/ex.py +29 -19
- xync_client/Okx/1.py +14 -0
- xync_client/Okx/agent.py +39 -0
- xync_client/Okx/ex.py +8 -8
- xync_client/Pms/Payeer/agent.py +396 -0
- xync_client/Pms/Payeer/login.py +1 -63
- xync_client/Pms/Payeer/trade.py +58 -0
- xync_client/loader.py +1 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/METADATA +2 -1
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/RECORD +30 -26
- xync_client/Pms/Payeer/__init__.py +0 -262
- xync_client/Pms/Payeer/api.py +0 -25
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/WHEEL +0 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/top_level.txt +0 -0
xync_client/Bybit/InAgent.py
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import logging
|
|
3
2
|
import re
|
|
4
|
-
import
|
|
5
|
-
from datetime import datetime,
|
|
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
|
|
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
|
-
|
|
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(
|
|
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}×tamp={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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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")
|