xync-client 0.0.156.dev18__py3-none-any.whl → 0.0.162__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xync_client/Abc/Agent.py +14 -1
- xync_client/Abc/Ex.py +11 -4
- xync_client/Abc/Order.py +7 -14
- xync_client/Abc/xtype.py +2 -2
- xync_client/Bybit/InAgent.py +0 -218
- xync_client/Bybit/agent.py +247 -279
- xync_client/Bybit/etype/order.py +5 -5
- xync_client/Bybit/order.py +14 -12
- xync_client/Pms/Volet/{__init__.py → agent.py} +1 -2
- {xync_client-0.0.156.dev18.dist-info → xync_client-0.0.162.dist-info}/METADATA +1 -1
- {xync_client-0.0.156.dev18.dist-info → xync_client-0.0.162.dist-info}/RECORD +13 -13
- {xync_client-0.0.156.dev18.dist-info → xync_client-0.0.162.dist-info}/WHEEL +0 -0
- {xync_client-0.0.156.dev18.dist-info → xync_client-0.0.162.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
8
|
+
class BaseOrderClient:
|
|
11
9
|
order: Order
|
|
12
10
|
im_maker: bool
|
|
13
11
|
im_seller: bool
|
|
14
12
|
|
|
15
|
-
|
|
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.
|
|
23
|
-
|
|
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):
|
xync_client/Bybit/InAgent.py
CHANGED
|
@@ -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")
|
xync_client/Bybit/agent.py
CHANGED
|
@@ -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
|
-
|
|
81
|
-
update_ad_body = {
|
|
82
|
-
"priceType": "1",
|
|
83
|
-
"premium": "118",
|
|
84
|
-
"quantity": "0.01",
|
|
85
|
-
"minAmount": "500",
|
|
86
|
-
"maxAmount": "3500000",
|
|
87
|
-
"paymentPeriod": "30",
|
|
88
|
-
"remark": "",
|
|
89
|
-
"price": "398244.84",
|
|
90
|
-
"paymentIds": ["3162931"],
|
|
91
|
-
"tradingPreferenceSet": {
|
|
92
|
-
"isKyc": "1",
|
|
93
|
-
"hasCompleteRateDay30": "0",
|
|
94
|
-
"completeRateDay30": "",
|
|
95
|
-
"hasOrderFinishNumberDay30": "0",
|
|
96
|
-
"orderFinishNumberDay30": "0",
|
|
97
|
-
"isMobile": "0",
|
|
98
|
-
"isEmail": "0",
|
|
99
|
-
"hasUnPostAd": "0",
|
|
100
|
-
"hasRegisterTime": "0",
|
|
101
|
-
"registerTimeThreshold": "0",
|
|
102
|
-
"hasNationalLimit": "0",
|
|
103
|
-
"nationalLimit": "",
|
|
104
|
-
},
|
|
105
|
-
"actionType": "MODIFY",
|
|
106
|
-
"securityRiskToken": "",
|
|
107
|
-
}
|
|
79
|
+
orders: dict[int, tuple[models.Order, OrderFull]] = {} # pending
|
|
108
80
|
|
|
109
81
|
def __init__(
|
|
110
82
|
self,
|
|
@@ -292,19 +264,21 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
292
264
|
mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
|
|
293
265
|
exids = [pt.id for pt in ad.paymentTerms]
|
|
294
266
|
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids).prefetch_related("cred")
|
|
267
|
+
await mad_db.credexs.clear()
|
|
295
268
|
await mad_db.credexs.add(*credexs)
|
|
296
|
-
# share
|
|
297
|
-
if [cx for cx in credexs if cx.cred.ovr_pm_id == 0]:
|
|
298
|
-
try:
|
|
299
|
-
await self.ad_share(mad_db.id)
|
|
300
|
-
except ShareException as e:
|
|
301
|
-
logging.warning(e.args[0])
|
|
302
|
-
await sleep(1)
|
|
303
|
-
|
|
304
269
|
return len(ads)
|
|
305
270
|
|
|
271
|
+
async def ads_share(self, cur_id: int = None) -> int:
|
|
272
|
+
mq = models.MyAd.hot_mads_query([4]).filter(ad__maker=self.actor)
|
|
273
|
+
if cur_id:
|
|
274
|
+
mq = mq.filter(ad__pair_side__pair__cur_id=cur_id)
|
|
275
|
+
mads: list[models.MyAd] = await mq.all()
|
|
276
|
+
return len([await self.ad_share(mad.id) for mad in mads])
|
|
277
|
+
|
|
306
278
|
async def ad_share(self, maid: int):
|
|
307
|
-
myad = await models.MyAd.get(id=maid).prefetch_related(
|
|
279
|
+
myad = await models.MyAd.get(id=maid).prefetch_related(
|
|
280
|
+
"ad__pair_side__pair__coin", "ad__pair_side__pair__cur", "ad__maker"
|
|
281
|
+
)
|
|
308
282
|
if myad.hex and myad.shared_at + timedelta(minutes=55) > now(): # check expired
|
|
309
283
|
# check validity
|
|
310
284
|
data = await self._post("/x-api/fiat/otc/item/shareItem/info", {"shareCode": myad.hex.hex()})
|
|
@@ -312,15 +286,22 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
312
286
|
return myad.get_url()
|
|
313
287
|
data = await self._post("/x-api/fiat/otc/item/share", {"itemId": str(myad.ad.exid)})
|
|
314
288
|
if data["ret_code"] == 912300058:
|
|
315
|
-
raise ShareException(
|
|
289
|
+
raise ShareException(
|
|
290
|
+
f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} агента {myad.ad.maker.agent_id} выключена"
|
|
291
|
+
)
|
|
316
292
|
if data["ret_code"] == 912300059:
|
|
317
|
-
raise ShareException("Торговля выключена")
|
|
293
|
+
raise ShareException(f"Торговля агента {myad.ad.maker.agent_id} выключена")
|
|
294
|
+
if data["ret_code"] == 10007:
|
|
295
|
+
raise ShareException(f"Авторизация агента {myad.ad.maker.agent_id} слетела")
|
|
318
296
|
if data["ret_code"] != 0: # Новая ошибка
|
|
319
297
|
raise ShareException(data)
|
|
320
298
|
url = data["result"]["shareLink"]
|
|
321
299
|
resp = await self.session.get(url)
|
|
322
|
-
|
|
323
|
-
|
|
300
|
+
side = "buy" if myad.ad.pair_side.is_sell else "sell" # inverse for taker
|
|
301
|
+
coin, cur = myad.ad.pair_side.pair.coin.ticker, myad.ad.pair_side.pair.cur.ticker
|
|
302
|
+
pref = models.MyAd.WEB.format(side=side, coin=coin, cur=cur)
|
|
303
|
+
hx = resp.url.query["by_web_link"].replace(pref, "")
|
|
304
|
+
_r = await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
|
|
324
305
|
await myad.refresh_from_db()
|
|
325
306
|
return myad.get_url()
|
|
326
307
|
|
|
@@ -535,7 +516,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
535
516
|
},
|
|
536
517
|
)
|
|
537
518
|
if res["ret_code"] == 0:
|
|
538
|
-
return {o
|
|
519
|
+
return {int(o["id"]): OrderItem(**o) for o in res["result"]["items"]}
|
|
539
520
|
return res["ret_code"]
|
|
540
521
|
|
|
541
522
|
def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
|
|
@@ -728,8 +709,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
728
709
|
for oid, o in ords.items():
|
|
729
710
|
if o.status != Status.completed.value or oid in self.completed_orders:
|
|
730
711
|
continue
|
|
731
|
-
|
|
732
|
-
order = OrderFull.model_validate(fo["result"])
|
|
712
|
+
order = await self.get_order_full(o.id)
|
|
733
713
|
order_db = await self.create_order_db(order)
|
|
734
714
|
await sleep(1)
|
|
735
715
|
dmsgs = self.api.get_chat_messages(orderId=oid, size=200)["result"]["result"][::-1]
|
|
@@ -900,27 +880,30 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
900
880
|
am = 500 + i
|
|
901
881
|
req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
|
|
902
882
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
903
|
-
order: OrderFull =
|
|
883
|
+
order: OrderFull = await self.get_order_full(int(ord_resp.orderId))
|
|
904
884
|
odb = await self.create_order_db(order)
|
|
905
885
|
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
906
886
|
await t.fetch_related("order__cred__pmcur__cur")
|
|
907
887
|
await self.pm_clients[366].send(t)
|
|
908
888
|
...
|
|
909
889
|
|
|
910
|
-
|
|
890
|
+
async def load_pending_orders(self):
|
|
891
|
+
po: dict[int, OrderItem] = await self.get_pending_orders()
|
|
892
|
+
if isinstance(po, int): # если код ошибки вместо результата
|
|
893
|
+
raise ValueError(po)
|
|
894
|
+
self.orders = {
|
|
895
|
+
o.exid: (o, await self.get_order_full(o.exid)) for o in await models.Order.filter(exid__in=po.keys())
|
|
896
|
+
}
|
|
897
|
+
for oid in po.keys() - self.orders.keys():
|
|
898
|
+
await self.load_order(oid)
|
|
911
899
|
|
|
912
|
-
async def
|
|
900
|
+
async def _start_listen(self):
|
|
913
901
|
t = await self.ott()
|
|
914
902
|
ts = int(float(t["time_now"]) * 1000)
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
# 3N: [T] - Уведомление об одобрении запроса на сделку
|
|
918
|
-
async def request_accepted_notify(self) -> int: ... # id
|
|
919
|
-
|
|
920
|
-
async def ws_prv(self, did: str, tok: str, ts: int):
|
|
903
|
+
did = self.agent.auth["cookies"]["deviceId"]
|
|
921
904
|
u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}×tamp={ts}"
|
|
922
905
|
async with websockets.connect(u) as websocket:
|
|
923
|
-
auth_msg = json.dumps({"req_id": did, "op": "login", "args": [
|
|
906
|
+
auth_msg = json.dumps({"req_id": did, "op": "login", "args": [t["result"]]})
|
|
924
907
|
await websocket.send(auth_msg)
|
|
925
908
|
|
|
926
909
|
sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
|
|
@@ -947,222 +930,213 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
947
930
|
await websocket.send(sub_msg)
|
|
948
931
|
while resp := await websocket.recv():
|
|
949
932
|
if data := json.loads(resp):
|
|
950
|
-
upd, order_db = None, None
|
|
951
933
|
logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
logging.error(data, "NOT SUCCESS!")
|
|
1159
|
-
else:
|
|
1160
|
-
continue # success login, subscribes, input
|
|
1161
|
-
case _:
|
|
1162
|
-
logging.warning(data, "UNKNOWN TOPIC")
|
|
1163
|
-
...
|
|
1164
|
-
if not upd:
|
|
1165
|
-
logging.warning(data, "NOT PROCESSED UPDATE")
|
|
934
|
+
await self.proc(data)
|
|
935
|
+
|
|
936
|
+
async def proc(self, data: dict):
|
|
937
|
+
match data.get("topic"):
|
|
938
|
+
case "OTC_ORDER_STATUS":
|
|
939
|
+
match data["type"]:
|
|
940
|
+
case "STATUS_CHANGE":
|
|
941
|
+
upd = StatusChange.model_validate(data["data"])
|
|
942
|
+
order_db, order = await self.load_order(upd.id)
|
|
943
|
+
match upd.status:
|
|
944
|
+
case Status.ws_new:
|
|
945
|
+
logging.info(f"Order {upd.id} CREATED at {upd.createDate}")
|
|
946
|
+
# await self.got_new_order(order_db, order)
|
|
947
|
+
|
|
948
|
+
# # сразу уменьшаем доступный остаток монеты/валюты
|
|
949
|
+
# await self.money_upd(order_db)
|
|
950
|
+
# if upd.side: # я покупатель - ждем мою оплату
|
|
951
|
+
# _dest = order.paymentTermList[0].accountNo
|
|
952
|
+
# if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
|
|
953
|
+
# return
|
|
954
|
+
# await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
|
|
955
|
+
# await self.send_payment(order_db)
|
|
956
|
+
case Status.created:
|
|
957
|
+
if upd.side == 0: # я продавец, ждем когда покупатель оплатит
|
|
958
|
+
# check_payment() # again
|
|
959
|
+
...
|
|
960
|
+
# if not (pmacdx := await self.get_pma_by_cdex(order)):
|
|
961
|
+
# return
|
|
962
|
+
# pma, cdx = pmacdx
|
|
963
|
+
# am, tid = await pma.check_in(
|
|
964
|
+
# float(order.amount),
|
|
965
|
+
# cdx.cred.pmcur.cur.ticker,
|
|
966
|
+
# # todo: почему в московском час.поясе?
|
|
967
|
+
# datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
968
|
+
# )
|
|
969
|
+
# if not tid:
|
|
970
|
+
# logging.info(f"Order {order.id} created at {order.createDate}, not paid yet")
|
|
971
|
+
# return
|
|
972
|
+
# try:
|
|
973
|
+
# t, is_new = await models.Transfer.update_or_create(
|
|
974
|
+
# dict(
|
|
975
|
+
# amount=int(float(order.amount) * 100),
|
|
976
|
+
# order=order_db,
|
|
977
|
+
# ),
|
|
978
|
+
# pmid=tid,
|
|
979
|
+
# )
|
|
980
|
+
# except IntegrityError as e:
|
|
981
|
+
# logging.error(tid)
|
|
982
|
+
# logging.error(order)
|
|
983
|
+
# logging.exception(e)
|
|
984
|
+
#
|
|
985
|
+
# if not is_new: # если по этому платежу уже отпущен другая продажа
|
|
986
|
+
# return
|
|
987
|
+
#
|
|
988
|
+
# # если висят незавершенные продажи с такой же суммой
|
|
989
|
+
# pos = (await self.get_orders_active(1))["result"]
|
|
990
|
+
# pos = [
|
|
991
|
+
# o
|
|
992
|
+
# for o in pos.get("items", [])
|
|
993
|
+
# if (
|
|
994
|
+
# o["amount"] == order.amount
|
|
995
|
+
# and o["id"] != upd.id
|
|
996
|
+
# and int(order.createDate) < int(o["createDate"]) + 15 * 60 * 1000
|
|
997
|
+
# # get full_order from o, and cred or pm from full_order:
|
|
998
|
+
# and self.api.get_order_details(orderId=o["id"])["result"][
|
|
999
|
+
# "paymentTermList"
|
|
1000
|
+
# ][0]["accountNo"]
|
|
1001
|
+
# == order.paymentTermList[0].accountNo
|
|
1002
|
+
# )
|
|
1003
|
+
# ]
|
|
1004
|
+
# curex = await models.CurEx.get(cur__ticker=order.currencyId, ex=self.ex_client.ex)
|
|
1005
|
+
# pos_db = await models.Order.filter(
|
|
1006
|
+
# exid__not=order.id,
|
|
1007
|
+
# cred_id=order_db.cred_id,
|
|
1008
|
+
# amount=int(float(order.amount) * 10**curex.scale),
|
|
1009
|
+
# status__not_in=[OrderStatus.completed, OrderStatus.canceled],
|
|
1010
|
+
# created_at__gt=now() - timedelta(minutes=15),
|
|
1011
|
+
# )
|
|
1012
|
+
# if pos or pos_db:
|
|
1013
|
+
# await self.ex_client.bot.send(
|
|
1014
|
+
# f"[Duplicate amount!]"
|
|
1015
|
+
# f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
|
|
1016
|
+
# self.actor.person.user.username_id,
|
|
1017
|
+
# )
|
|
1018
|
+
# logging.warning("Duplicate amount!")
|
|
1019
|
+
# return
|
|
1020
|
+
#
|
|
1021
|
+
# # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
|
|
1022
|
+
# self.api.release_assets(orderId=upd.id)
|
|
1023
|
+
# logging.info(
|
|
1024
|
+
# f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
|
|
1025
|
+
# )
|
|
1026
|
+
elif upd.side == 1: # я покупатель - ждем мою оплату
|
|
1027
|
+
# pay()
|
|
1028
|
+
logging.warning(f"Order {upd.id} CREATED2 at {now()}")
|
|
1029
|
+
|
|
1030
|
+
case Status.paid:
|
|
1031
|
+
if order_db.status == OrderStatus.paid:
|
|
1032
|
+
return
|
|
1033
|
+
await order_db.update_from_dict(
|
|
1034
|
+
{
|
|
1035
|
+
"status": OrderStatus.paid,
|
|
1036
|
+
"payed_at": datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
1037
|
+
}
|
|
1038
|
+
).save()
|
|
1039
|
+
logging.info(f"Order {order.id} payed at {order_db.payed_at}")
|
|
1040
|
+
|
|
1041
|
+
case Status.appealed_by_seller: # just any appealed
|
|
1042
|
+
# todo: appealed by WHO? щас наугад стоит by_seller
|
|
1043
|
+
await order_db.update_from_dict(
|
|
1044
|
+
{
|
|
1045
|
+
"status": OrderStatus.appealed_by_seller,
|
|
1046
|
+
"appealed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
|
|
1047
|
+
}
|
|
1048
|
+
).save()
|
|
1049
|
+
logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
|
|
1050
|
+
|
|
1051
|
+
case Status.canceled:
|
|
1052
|
+
await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
|
|
1053
|
+
logging.info(f"Order {order.id} canceled at {datetime.now()}")
|
|
1054
|
+
# await self.money_upd(order_db)
|
|
1055
|
+
|
|
1056
|
+
case Status.completed:
|
|
1057
|
+
await order_db.refresh_from_db()
|
|
1058
|
+
if order_db.status != OrderStatus.completed:
|
|
1059
|
+
await order_db.update_from_dict(
|
|
1060
|
+
{
|
|
1061
|
+
"status": OrderStatus.completed,
|
|
1062
|
+
"confirmed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
|
|
1063
|
+
}
|
|
1064
|
+
).save(update_fields=["status", "confirmed_at"])
|
|
1065
|
+
# await self.money_upd(order_db)
|
|
1066
|
+
|
|
1067
|
+
case _:
|
|
1068
|
+
logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
|
|
1069
|
+
case "COUNT_DOWN":
|
|
1070
|
+
upd = CountDown.model_validate(data["data"])
|
|
1071
|
+
|
|
1072
|
+
case "OTC_USER_CHAT_MSG":
|
|
1073
|
+
match data["type"]:
|
|
1074
|
+
case "RECEIVE":
|
|
1075
|
+
upd = Receive.model_validate(data["data"])
|
|
1076
|
+
order_db, order = await self.load_order(upd.orderId)
|
|
1077
|
+
# got_msg()
|
|
1078
|
+
...
|
|
1079
|
+
# im_taker = order_db.taker_id == self.actor.id
|
|
1080
|
+
# im_buyer = order_db.ad.pair_side.is_sell == im_taker
|
|
1081
|
+
# if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
|
|
1082
|
+
# msg, _ = await models.Msg.update_or_create(
|
|
1083
|
+
# {
|
|
1084
|
+
# "to_maker": upd.userId == self.actor.exid and im_taker,
|
|
1085
|
+
# "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
|
|
1086
|
+
# },
|
|
1087
|
+
# txt=upd.message,
|
|
1088
|
+
# order=order_db,
|
|
1089
|
+
# )
|
|
1090
|
+
# if not upd.message:
|
|
1091
|
+
# ...
|
|
1092
|
+
# if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
|
|
1093
|
+
# if not order_db.cred.detail.startswith(dest := g.group()):
|
|
1094
|
+
# order_db.cred.detail = dest
|
|
1095
|
+
# await order_db.save()
|
|
1096
|
+
# await self.send_payment(order_db)
|
|
1097
|
+
case "READ":
|
|
1098
|
+
# msg_read()
|
|
1099
|
+
upd = Read.model_validate(data["data"])
|
|
1100
|
+
|
|
1101
|
+
case "CLEAR":
|
|
1102
|
+
return
|
|
1103
|
+
case "OTC_USER_CHAT_MSG_V2":
|
|
1104
|
+
# msg dup
|
|
1105
|
+
...
|
|
1106
|
+
# match data["type"]:
|
|
1107
|
+
# case "RECEIVE":
|
|
1108
|
+
# upd = Receive.model_validate(data["data"])
|
|
1109
|
+
# case "READ":
|
|
1110
|
+
# upd = Read.model_validate(data["data"])
|
|
1111
|
+
# case "CLEAR":
|
|
1112
|
+
# pass
|
|
1113
|
+
# case _:
|
|
1114
|
+
# self.listen(data)
|
|
1115
|
+
case "SELLER_CANCEL_CHANGE":
|
|
1116
|
+
upd = SellerCancelChange.model_validate(data["data"])
|
|
1117
|
+
case None:
|
|
1118
|
+
if not data.get("success"):
|
|
1119
|
+
logging.error(data, "NOT SUCCESS!")
|
|
1120
|
+
else:
|
|
1121
|
+
return # success login, subscribes, input
|
|
1122
|
+
case _:
|
|
1123
|
+
logging.warning(data, "UNKNOWN TOPIC")
|
|
1124
|
+
|
|
1125
|
+
async def get_order_full(self, oid: int) -> OrderFull:
|
|
1126
|
+
order = self.api.get_order_details(orderId=oid)
|
|
1127
|
+
return OrderFull.model_validate(order["result"])
|
|
1128
|
+
|
|
1129
|
+
async def load_order(self, oid: int) -> tuple[models.Order, OrderFull]:
|
|
1130
|
+
if not self.orders.get(oid):
|
|
1131
|
+
order = await self.get_order_full(oid)
|
|
1132
|
+
if not (
|
|
1133
|
+
order_db := await models.Order.get_or_none(exid=oid, ad__maker__ex=self.actor.ex).prefetch_related(
|
|
1134
|
+
"ad__pair_side__pair", "cred__pmcur__cur"
|
|
1135
|
+
)
|
|
1136
|
+
):
|
|
1137
|
+
order_db = await self.create_order_db(order)
|
|
1138
|
+
self.orders[oid] = order_db, order
|
|
1139
|
+
return self.orders[oid]
|
|
1166
1140
|
|
|
1167
1141
|
async def money_upd(self, odb: models.Order):
|
|
1168
1142
|
# обновляем остаток монеты
|
|
@@ -1279,11 +1253,6 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
1279
1253
|
return pma, order_db.cred.pmcur.cur.ticker
|
|
1280
1254
|
logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
|
|
1281
1255
|
|
|
1282
|
-
@staticmethod
|
|
1283
|
-
def listen(data: dict | None):
|
|
1284
|
-
# print(data)
|
|
1285
|
-
...
|
|
1286
|
-
|
|
1287
1256
|
|
|
1288
1257
|
def ms2utc(msk_ts_str: str):
|
|
1289
1258
|
return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
|
|
@@ -1334,7 +1303,7 @@ async def main():
|
|
|
1334
1303
|
cn = await init_db(TORM)
|
|
1335
1304
|
|
|
1336
1305
|
agent = (
|
|
1337
|
-
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=
|
|
1306
|
+
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=5)
|
|
1338
1307
|
.prefetch_related(
|
|
1339
1308
|
"actor__ex",
|
|
1340
1309
|
"actor__person__user__gmail",
|
|
@@ -1364,12 +1333,11 @@ async def main():
|
|
|
1364
1333
|
# await cl.ex_client.set_pairs()
|
|
1365
1334
|
# await cl.ex_client.set_pms()
|
|
1366
1335
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1336
|
+
await cl.set_creds()
|
|
1337
|
+
await cl.export_my_ads()
|
|
1369
1338
|
|
|
1370
1339
|
my_ad = await models.MyAd[5]
|
|
1371
1340
|
await cl.ad_share(my_ad.id)
|
|
1372
|
-
await cl.start_listen()
|
|
1373
1341
|
|
|
1374
1342
|
ms = await models.Agent.filter(
|
|
1375
1343
|
actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[3]
|
xync_client/Bybit/etype/order.py
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
293
|
+
orderId: int
|
|
294
294
|
message: str = None
|
|
295
295
|
msgUuid: str
|
|
296
296
|
msgUuId: str
|
xync_client/Bybit/order.py
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
from
|
|
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)
|
|
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=
|
|
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,
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
40
|
-
xync_client/Bybit/agent.py,sha256=
|
|
39
|
+
xync_client/Bybit/InAgent.py,sha256=HJzy-avXlGu7yh11WqYmx8whTGKU3HTng2dtkbLtTCQ,1234
|
|
40
|
+
xync_client/Bybit/agent.py,sha256=MyZRc5sbbuivw8N1LeFcjwba4AJytplStFldku_ESaM,66356
|
|
41
41
|
xync_client/Bybit/ex.py,sha256=W_bYXafNR3aWdx0qpr06bGHl52xGBaKKcABleveHXng,4468
|
|
42
|
-
xync_client/Bybit/order.py,sha256=
|
|
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=
|
|
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/
|
|
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.
|
|
108
|
-
xync_client-0.0.
|
|
109
|
-
xync_client-0.0.
|
|
110
|
-
xync_client-0.0.
|
|
107
|
+
xync_client-0.0.162.dist-info/METADATA,sha256=fObLDRD-fDTgXnRXbmMBiioSGGgr0WgwS12thRoLeII,1181
|
|
108
|
+
xync_client-0.0.162.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
109
|
+
xync_client-0.0.162.dist-info/top_level.txt,sha256=bmYEVIIrD3v7yFwH-X15pEfRvzhuAdfsAZ2igvNI4O8,12
|
|
110
|
+
xync_client-0.0.162.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|