xync-client 0.0.164__py3-none-any.whl → 0.0.179.dev4__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 +154 -30
- xync_client/Abc/Ex.py +113 -136
- xync_client/Abc/Order.py +20 -17
- xync_client/Abc/xtype.py +139 -65
- xync_client/Binance/ex.py +2 -2
- xync_client/BingX/agent.py +1 -1
- xync_client/BingX/ex.py +2 -2
- xync_client/BitGet/agent.py +1 -3
- xync_client/BitGet/ex.py +10 -9
- xync_client/BitPapa/ex.py +12 -9
- xync_client/Bybit/agent.py +106 -349
- xync_client/Bybit/etype/ad.py +79 -58
- xync_client/Bybit/etype/cred.py +25 -3
- xync_client/Bybit/etype/order.py +148 -92
- xync_client/Bybit/ex.py +27 -15
- xync_client/Bybit/{InAgent.py → inAgent.py} +5 -10
- xync_client/Bybit/order.py +42 -16
- xync_client/Gate/ex.py +2 -2
- xync_client/Htx/agent.py +13 -16
- xync_client/Htx/etype/ad.py +2 -4
- xync_client/Htx/etype/test.py +4 -4
- xync_client/Htx/ex.py +37 -5
- xync_client/KuCoin/ex.py +2 -2
- xync_client/Mexc/agent.py +11 -13
- xync_client/Mexc/ex.py +5 -5
- xync_client/Okx/agent.py +3 -9
- xync_client/Okx/ex.py +11 -10
- xync_client/TgWallet/agent.py +21 -21
- xync_client/TgWallet/ex.py +15 -15
- xync_client/TgWallet/pyd.py +5 -5
- xync_client/pm_unifier.py +3 -2
- {xync_client-0.0.164.dist-info → xync_client-0.0.179.dev4.dist-info}/METADATA +1 -1
- {xync_client-0.0.164.dist-info → xync_client-0.0.179.dev4.dist-info}/RECORD +35 -35
- {xync_client-0.0.164.dist-info → xync_client-0.0.179.dev4.dist-info}/WHEEL +0 -0
- {xync_client-0.0.164.dist-info → xync_client-0.0.179.dev4.dist-info}/top_level.txt +0 -0
xync_client/Abc/Ex.py
CHANGED
|
@@ -6,7 +6,6 @@ from collections import defaultdict
|
|
|
6
6
|
from difflib import SequenceMatcher
|
|
7
7
|
|
|
8
8
|
from aiohttp import ClientSession, ClientResponse
|
|
9
|
-
from google.protobuf.internal.wire_format import INT32_MAX
|
|
10
9
|
from msgspec import Struct
|
|
11
10
|
from pydantic import BaseModel
|
|
12
11
|
from pyro_client.client.file import FileClient
|
|
@@ -15,10 +14,10 @@ from x_client.aiohttp import Client as HttpClient
|
|
|
15
14
|
from xync_client.Bybit.etype.ad import AdsReq
|
|
16
15
|
from xync_schema import models
|
|
17
16
|
from xync_schema.enums import FileType
|
|
18
|
-
from xync_schema
|
|
17
|
+
from xync_schema import xtype
|
|
19
18
|
|
|
20
19
|
from xync_client.Abc.AdLoader import AdLoader
|
|
21
|
-
from xync_client.Abc.xtype import PmEx, MapOfIdsList,
|
|
20
|
+
from xync_client.Abc.xtype import PmEx, MapOfIdsList, GetAdsReq, BaseActor, BaseAd, BaseCounteragent
|
|
22
21
|
from xync_client.pm_unifier import PmUnifier, PmUni
|
|
23
22
|
|
|
24
23
|
|
|
@@ -27,41 +26,38 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
27
26
|
cur_map: dict[int, str] = {}
|
|
28
27
|
unifier_class: type = PmUnifier
|
|
29
28
|
logo_pre_url: str
|
|
30
|
-
bot: FileClient
|
|
31
29
|
ex: models.Ex
|
|
32
30
|
|
|
33
31
|
coin_x2e: dict[int, tuple[str, int]] = {}
|
|
34
32
|
coin_e2x: dict[str, int] = {}
|
|
35
33
|
cur_x2e: dict[int, tuple[str, int, int]] = {}
|
|
36
34
|
cur_e2x: dict[str, int] = {}
|
|
37
|
-
pm_x2e: dict[int, str] = {}
|
|
38
|
-
pm_e2x: dict[str, int] = {}
|
|
35
|
+
pm_x2e: dict[int, str | int] = {}
|
|
36
|
+
pm_e2x: dict[str | int, int] = {}
|
|
39
37
|
pairs_e2x: dict[int, dict[int, tuple[int, int]]] = defaultdict(defaultdict)
|
|
40
38
|
pairs_x2e: dict[int, tuple[int, int, int]] = defaultdict(defaultdict)
|
|
41
39
|
actor_x2e: dict[int, int] = {}
|
|
42
|
-
actor_e2x: dict[int,
|
|
40
|
+
actor_e2x: dict[int, models.Actor] = {}
|
|
43
41
|
ad_x2e: dict[int, int] = {}
|
|
44
|
-
ad_e2x: dict[int,
|
|
42
|
+
ad_e2x: dict[int, models.Ad] = {}
|
|
45
43
|
|
|
46
44
|
def __init__(
|
|
47
45
|
self,
|
|
48
46
|
ex: models.Ex,
|
|
49
|
-
bot: FileClient,
|
|
50
47
|
attr: str = "host_p2p",
|
|
51
48
|
headers: dict[str, str] = None,
|
|
52
49
|
cookies: dict[str, str] = None,
|
|
53
50
|
proxy: models.Proxy = None,
|
|
54
51
|
):
|
|
55
52
|
self.ex = ex
|
|
56
|
-
self.
|
|
57
|
-
super().__init__(self.host or getattr(ex, attr), headers, cookies, proxy and proxy.str())
|
|
53
|
+
super().__init__(self.host or getattr(ex, attr), headers, cookies, proxy and str(proxy))
|
|
58
54
|
|
|
59
55
|
@abstractmethod
|
|
60
56
|
def pm_type_map(self, typ: models.PmEx) -> str: ...
|
|
61
57
|
|
|
62
58
|
# 19: Список поддерживаемых валют тейкера
|
|
63
59
|
@abstractmethod
|
|
64
|
-
async def curs(self) -> dict[str, CurEx]: # {cur.ticker: cur}
|
|
60
|
+
async def curs(self) -> dict[str, xtype.CurEx]: # {cur.ticker: cur}
|
|
65
61
|
...
|
|
66
62
|
|
|
67
63
|
# 20: Список платежных методов
|
|
@@ -74,9 +70,9 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
74
70
|
async def cur_pms_map(self) -> MapOfIdsList: # {cur.exid: [pm.exid]}
|
|
75
71
|
...
|
|
76
72
|
|
|
77
|
-
# 22: Список торгуемых монет (с
|
|
73
|
+
# 22: Список торгуемых монет (с ограничением по валютам, если есть)
|
|
78
74
|
@abstractmethod
|
|
79
|
-
async def coins(self) -> dict[str, CoinEx]: # {coin.ticker: coin}
|
|
75
|
+
async def coins(self) -> dict[str, xtype.CoinEx]: # {coin.ticker: coin}
|
|
80
76
|
...
|
|
81
77
|
|
|
82
78
|
# 23: Список пар валюта/монет
|
|
@@ -118,7 +114,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
118
114
|
self.pm_e2x[self.pm_x2e[pm_id]] = pm_id
|
|
119
115
|
return self.pm_x2e[pm_id]
|
|
120
116
|
|
|
121
|
-
async def e2x_pm(self, exid: str) -> int: # pm.id
|
|
117
|
+
async def e2x_pm(self, exid: str | int) -> int: # pm.id
|
|
122
118
|
if not self.pm_e2x.get(exid):
|
|
123
119
|
self.pm_e2x[exid] = (await models.PmEx.get(exid=exid, ex=self.ex)).pm_id
|
|
124
120
|
self.pm_x2e[self.pm_e2x[exid]] = exid
|
|
@@ -153,15 +149,17 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
153
149
|
|
|
154
150
|
async def x2e_actor(self, actor_id: int) -> int: # actor.exid
|
|
155
151
|
if not self.actor_x2e.get(actor_id):
|
|
156
|
-
|
|
157
|
-
self.
|
|
152
|
+
actor = await models.Actor[actor_id]
|
|
153
|
+
self.actor_x2e[actor_id] = actor.exid
|
|
154
|
+
self.actor_e2x[self.actor_x2e[actor_id]] = actor
|
|
158
155
|
return self.actor_x2e[actor_id]
|
|
159
156
|
|
|
160
|
-
async def e2x_actor(self,
|
|
161
|
-
if not self.actor_e2x.get(exid):
|
|
162
|
-
|
|
163
|
-
self.
|
|
164
|
-
|
|
157
|
+
async def e2x_actor(self, base_actor: BaseActor) -> models.Actor:
|
|
158
|
+
if not self.actor_e2x.get(base_actor.exid):
|
|
159
|
+
actor = await self.actor_save(base_actor)
|
|
160
|
+
self.actor_e2x[base_actor.exid] = actor
|
|
161
|
+
self.actor_x2e[actor.id] = base_actor.exid
|
|
162
|
+
return self.actor_e2x[base_actor.exid]
|
|
165
163
|
|
|
166
164
|
async def x2e_ad(self, ad_id: int) -> int: # ad.exid
|
|
167
165
|
if not self.ad_x2e.get(ad_id):
|
|
@@ -169,15 +167,15 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
169
167
|
self.ad_e2x[self.ad_x2e[ad_id]] = ad_id
|
|
170
168
|
return self.ad_x2e[ad_id]
|
|
171
169
|
|
|
172
|
-
async def e2x_ad(self,
|
|
173
|
-
if not self.ad_e2x.get(
|
|
174
|
-
|
|
175
|
-
self.
|
|
176
|
-
|
|
170
|
+
async def e2x_ad(self, ad_exid: int) -> models.Ad: # todo: пока только для мейкерных, не создаются если нет в бд
|
|
171
|
+
if not self.ad_e2x.get(ad_exid):
|
|
172
|
+
ad = await models.Ad.get_or_none(exid=ad_exid, maker__ex=self.ex)
|
|
173
|
+
self.ad_e2x[ad_exid] = ad
|
|
174
|
+
self.ad_x2e[ad.id] = ad_exid
|
|
175
|
+
return self.ad_e2x[ad_exid]
|
|
177
176
|
|
|
178
177
|
# 24: Список объяв по (buy/sell, cur, coin, pm)
|
|
179
|
-
async def ads(self, xreq:
|
|
180
|
-
self.ex.etype().ad.AdsReq
|
|
178
|
+
async def ads(self, xreq: GetAdsReq, **kwargs) -> list[xtype.BaseAd]:
|
|
181
179
|
ereq = AdsReq(
|
|
182
180
|
coin_id=(await self.x2e_coin(xreq.coin_id))[0],
|
|
183
181
|
cur_id=(await self.x2e_cur(xreq.cur_id))[0],
|
|
@@ -195,11 +193,11 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
195
193
|
|
|
196
194
|
# 42: Чужая объява по id
|
|
197
195
|
@abstractmethod
|
|
198
|
-
async def ad(self, ad_id: int) -> BaseAd: ...
|
|
196
|
+
async def ad(self, ad_id: int) -> xtype.BaseAd: ...
|
|
199
197
|
|
|
200
198
|
# Преобразрование объекта объявления из формата биржи в формат xync
|
|
201
199
|
@abstractmethod
|
|
202
|
-
async def ad_epyd2pydin(self, ad: BaseAd) ->
|
|
200
|
+
async def ad_epyd2pydin(self, ad: BaseAd) -> xtype.BaseAd: ... # my_uid: for MyAd
|
|
203
201
|
|
|
204
202
|
# 99: Страны
|
|
205
203
|
async def countries(self) -> list[Struct]:
|
|
@@ -208,7 +206,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
208
206
|
# Импорт валют Cur-ов (с CurEx-ами)
|
|
209
207
|
async def set_curs(self, cookies: dict = None) -> bool:
|
|
210
208
|
# Curs
|
|
211
|
-
cur_pyds: dict[str, CurEx] = await self.curs()
|
|
209
|
+
cur_pyds: dict[str, xtype.CurEx] = await self.curs()
|
|
212
210
|
old_curs = {c.ticker: c.id for c in await models.Cur.all()}
|
|
213
211
|
curs: dict[int | str, models.Cur] = {
|
|
214
212
|
exid: (
|
|
@@ -226,7 +224,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
226
224
|
await models.CurEx.bulk_create(curexs, update_fields=["minimum", "scale"], on_conflict=["cur_id", "ex_id"])
|
|
227
225
|
|
|
228
226
|
# Импорт Pm-ов (с PmCur-, PmEx- и Pmcurex-ами) и валют (с CurEx-ами) с биржи в бд
|
|
229
|
-
async def set_pms(self, cookies: dict = None) -> bool:
|
|
227
|
+
async def set_pms(self, bot: FileClient, cookies: dict = None) -> bool:
|
|
230
228
|
if cookies:
|
|
231
229
|
self.session.cookie_jar.update_cookies(cookies)
|
|
232
230
|
curs: dict[int | str, models.Cur] = {
|
|
@@ -289,7 +287,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
289
287
|
ex=self.ex,
|
|
290
288
|
pm=pm,
|
|
291
289
|
name=pmexs_epyds[k].name,
|
|
292
|
-
logo=await self.logo_save(pmexs_epyds[k].logo, ss),
|
|
290
|
+
logo=await self.logo_save(pmexs_epyds[k].logo, bot, ss),
|
|
293
291
|
)
|
|
294
292
|
for k, pm in pms.items()
|
|
295
293
|
]
|
|
@@ -319,18 +317,18 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
319
317
|
# await Pmcurex.bulk_create(pmcurexs)
|
|
320
318
|
return True
|
|
321
319
|
|
|
322
|
-
async def logo_save(self, url: str | None, ss: ClientSession) -> models.File | None:
|
|
320
|
+
async def logo_save(self, url: str | None, bot: FileClient, ss: ClientSession) -> models.File | None:
|
|
323
321
|
if url or (file := None):
|
|
324
322
|
if not url.startswith("https:"):
|
|
325
323
|
if not url.startswith("/"):
|
|
326
324
|
url = "/" + url
|
|
327
325
|
url = "https://" + self.logo_pre_url + url
|
|
328
|
-
return await self.file_upsert(url, ss)
|
|
326
|
+
return await self.file_upsert(url, bot, ss)
|
|
329
327
|
return file
|
|
330
328
|
|
|
331
329
|
# Импорт монет (с CoinEx-ами) с биржи в бд
|
|
332
330
|
async def set_coins(self):
|
|
333
|
-
coinexs: dict[str, CoinEx] = await self.coins()
|
|
331
|
+
coinexs: dict[str, xtype.CoinEx] = await self.coins()
|
|
334
332
|
coins_db: dict[int, models.Coin] = {
|
|
335
333
|
c.exid: (
|
|
336
334
|
await models.Coin.update_or_create({"scale": c.scale or self.coin_scales[c.ticker]}, ticker=c.ticker)
|
|
@@ -352,10 +350,10 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
352
350
|
|
|
353
351
|
# Импорт пар биржи в бд
|
|
354
352
|
async def set_pairs(self):
|
|
355
|
-
curs: dict[str,
|
|
353
|
+
curs: dict[str, models.Cur] = {
|
|
356
354
|
k: (await models.Cur.get_or_create(ticker=c.ticker))[0] for k, c in (await self.curs()).items()
|
|
357
355
|
}
|
|
358
|
-
coins: dict[str, CoinEx] = {
|
|
356
|
+
coins: dict[str, xtype.CoinEx] = {
|
|
359
357
|
k: (await models.Coin.get_or_create(ticker=c.ticker))[0] for k, c in (await self.coins()).items()
|
|
360
358
|
}
|
|
361
359
|
prs: tuple[dict, dict] = await self.pairs()
|
|
@@ -377,12 +375,12 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
377
375
|
# await ad_db.pmexs.add(*getattr(ad_pydin, "pmexs_", []))
|
|
378
376
|
# return ad_db
|
|
379
377
|
|
|
380
|
-
async def file_upsert(self, url: str, ss: ClientSession = None) -> models.File:
|
|
378
|
+
async def file_upsert(self, url: str, bot: FileClient, ss: ClientSession = None) -> models.File:
|
|
381
379
|
if not (file := await models.File.get_or_none(name__startswith=url.split("?")[0])):
|
|
382
380
|
ss = ss or self.session
|
|
383
381
|
if (resp := await ss.get(url)).ok:
|
|
384
382
|
byts = await resp.read()
|
|
385
|
-
upf, ref = await
|
|
383
|
+
upf, ref = await bot.save_doc(byts, resp.content_type)
|
|
386
384
|
await sleep(0.3)
|
|
387
385
|
typ = FileType[resp.content_type.split("/")[-1]]
|
|
388
386
|
file, _ = await models.File.update_or_create({"ref": ref, "size": len(byts), "typ": typ}, name=url)
|
|
@@ -394,7 +392,10 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
394
392
|
proxy = await models.Proxy.filter(valid=True, country__short__not="US").order_by("-updated_at").first()
|
|
395
393
|
cookies = self.session.cookie_jar.filter_cookies(self.session._base_url)
|
|
396
394
|
self.session = ClientSession(
|
|
397
|
-
self.session._base_url,
|
|
395
|
+
self.session._base_url,
|
|
396
|
+
headers=self.session.headers,
|
|
397
|
+
cookies=cookies or None,
|
|
398
|
+
proxy=proxy and str(proxy),
|
|
398
399
|
)
|
|
399
400
|
return await self.METHS[resp.method](self, resp.url.path, bp)
|
|
400
401
|
return await super()._proc(resp, bp)
|
|
@@ -429,106 +430,82 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
429
430
|
if ct := set(self.tree.keys()) & a:
|
|
430
431
|
logging.exception(f"cycle cids: {ct}")
|
|
431
432
|
|
|
432
|
-
async def
|
|
433
|
-
if
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
actor.person.name = name
|
|
441
|
-
await actor.person.save()
|
|
442
|
-
return actor.person
|
|
443
|
-
# tmp dirty fix
|
|
444
|
-
note = f"{self.ex.id}:{exid}"
|
|
445
|
-
if person := await models.Person.get_or_none(note__startswith=note, name__not=name):
|
|
446
|
-
person.name = name
|
|
447
|
-
await person.save()
|
|
448
|
-
return person
|
|
449
|
-
try:
|
|
450
|
-
return (await models.Person.update_or_create(name=name, note=note))[0]
|
|
451
|
-
except OperationalError as e:
|
|
452
|
-
raise e
|
|
453
|
-
await models.Actor.create(person=person, exid=exid, ex=self.ex)
|
|
433
|
+
async def person_save(self, base_person: BaseActor) -> models.Person:
|
|
434
|
+
if not (person := await models.Person.get_or_none(note=(note := f"{self.ex.id}:{base_person.exid}"))):
|
|
435
|
+
name = base_person.name if isinstance(base_person, BaseCounteragent) else base_person.nick
|
|
436
|
+
person = await models.Person.create(name=name, note=note)
|
|
437
|
+
elif isinstance(base_person, BaseCounteragent) and not person.name:
|
|
438
|
+
# если персона не новая, но имени не было, а щас передано - обновим
|
|
439
|
+
person.name = base_person.name
|
|
440
|
+
await person.save(update_fields=["name"])
|
|
454
441
|
return person
|
|
455
|
-
# person = await models.Person.create(note=f'{actor.ex_id}:{actor.exid}:{name}') # no person for just ads with no orders
|
|
456
|
-
# raise ValueError(f"Agent #{exid} not found")
|
|
457
442
|
|
|
458
|
-
async def
|
|
459
|
-
self
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
coin_scale = 10 ** (coinex or await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex)).scale
|
|
485
|
-
amt = int(float(pad.quantity) * float(pad.price) * cur_scale)
|
|
486
|
-
mxf = pad.maxAmount and int(float(pad.maxAmount) * cur_scale)
|
|
487
|
-
df_unq = ad_upd.df_unq(
|
|
488
|
-
maker_id=maker.id,
|
|
489
|
-
pair_side_id=ps.id,
|
|
490
|
-
amount=min(amt, INT32_MAX),
|
|
491
|
-
quantity=int(float(pad.quantity) * coin_scale),
|
|
492
|
-
min_fiat=int(float(pad.minAmount) * cur_scale),
|
|
493
|
-
max_fiat=min(mxf, INT32_MAX),
|
|
494
|
-
price=int(float(pad.price) * cur_scale),
|
|
495
|
-
premium=int(float(pad.premium) * 100),
|
|
496
|
-
cond_id=cid,
|
|
497
|
-
status=self.ad_status(ad_upd.status),
|
|
443
|
+
async def actor_save(self, base_actor: BaseActor) -> models.Actor:
|
|
444
|
+
if not (actor := await models.Actor.get_or_none(exid=base_actor.exid, ex=self.ex)):
|
|
445
|
+
# если это новый актор - делаем создание персоны со стороны биржи
|
|
446
|
+
person = await self.person_save(base_actor)
|
|
447
|
+
actor = await models.Actor.create(exid=base_actor.exid, name=base_actor.nick, person=person, ex=self.ex)
|
|
448
|
+
return actor
|
|
449
|
+
|
|
450
|
+
async def ad_save(self, base_ad: BaseAd) -> models.Ad:
|
|
451
|
+
if ad_db := await models.Ad.get_or_none(exid=base_ad.exid, maker__ex=self.ex):
|
|
452
|
+
... # load new data
|
|
453
|
+
return ad_db
|
|
454
|
+
cond = await self.load_cond(base_ad.cond_txt, base_ad.maker_exid)
|
|
455
|
+
_, cur_scale, _cur_min = await self.x2e_cur(await self.e2x_cur(base_ad.curex_exid))
|
|
456
|
+
_, coin_scale = await self.x2e_coin(await self.e2x_coin(base_ad.coinex_exid))
|
|
457
|
+
badd = base_ad.model_dump()
|
|
458
|
+
badd.update(
|
|
459
|
+
amount=int((base_ad.amount or base_ad.quantity * base_ad.price) * 10**cur_scale),
|
|
460
|
+
max_fiat=int(base_ad.max_fiat * 10**cur_scale),
|
|
461
|
+
min_fiat=int(base_ad.min_fiat * 10**cur_scale),
|
|
462
|
+
premium=int(base_ad.premium * 100_00),
|
|
463
|
+
price=int(base_ad.price * 10**cur_scale),
|
|
464
|
+
quantity=int((base_ad.quantity or base_ad.amount / base_ad.price) * 10**coin_scale),
|
|
465
|
+
maker_id=(await self.e2x_actor(base_ad.maker)).id,
|
|
466
|
+
cond_id=cond and cond.id,
|
|
467
|
+
pair_side_id=await self.e2x_pair(base_ad.coinex_exid, base_ad.curex_exid, bool(base_ad.side.value)),
|
|
468
|
+
pms=await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=base_ad.pmex_exids),
|
|
498
469
|
)
|
|
470
|
+
ad = xtype.BaseAd.model_validate(badd)
|
|
471
|
+
ad_upd = models.Ad.validate(ad.model_dump(), with_pk=False)
|
|
499
472
|
try:
|
|
500
|
-
ad_db, _ = await models.Ad.update_or_create(**df_unq)
|
|
473
|
+
ad_db, _ = await models.Ad.update_or_create(**ad_upd.df_unq())
|
|
501
474
|
except OperationalError as e:
|
|
502
475
|
raise e
|
|
503
476
|
await ad_db.pms.clear()
|
|
504
|
-
await ad_db.pms.add(*
|
|
477
|
+
await ad_db.pms.add(*ad.pms)
|
|
505
478
|
return ad_db
|
|
506
479
|
|
|
507
|
-
async def
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
480
|
+
async def load_cond(self, txt: str, maker_exid: int) -> models.Cond | None:
|
|
481
|
+
# если текст пустой
|
|
482
|
+
if not (cleaned := clean(txt)):
|
|
483
|
+
return None
|
|
484
|
+
# если точно такое условие уже есть в бд
|
|
485
|
+
if cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned):
|
|
486
|
+
return await models.Cond[cid]
|
|
487
|
+
# создаем новое условие
|
|
488
|
+
return await self.cond_new(cleaned, {maker_exid})
|
|
489
|
+
|
|
490
|
+
async def old_load_cond(self, ad: BaseAd) -> models.Ad:
|
|
517
491
|
_sim, cid = None, None
|
|
518
|
-
ad_db = await models.Ad.get_or_none(exid=ad.
|
|
492
|
+
ad_db = await models.Ad.get_or_none(exid=ad.exid, maker__ex=self.ex).prefetch_related("cond")
|
|
519
493
|
# если точно такое условие уже есть в бд
|
|
520
|
-
if not (cleaned := clean(ad.
|
|
494
|
+
if not (cleaned := clean(ad.cond_txt)) or (
|
|
495
|
+
cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)
|
|
496
|
+
):
|
|
521
497
|
# и объява с таким ид уже есть, но у нее другое условие
|
|
522
498
|
if ad_db and ad_db.cond_id != cid:
|
|
499
|
+
old_cid = ad_db.cond_id
|
|
523
500
|
# то обновляем ид ее условия
|
|
524
501
|
ad_db.cond_id = cid
|
|
525
|
-
await ad_db.save()
|
|
526
|
-
logging.info(f"{ad.
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return
|
|
502
|
+
await ad_db.save(update_fields=["cond_id"])
|
|
503
|
+
logging.info(f"{ad.maker_name} upd cond#{ad_db.cond_id}->{cid}")
|
|
504
|
+
# если после переназначения объяве нового условия, со старым условием не осталось объяв, то удаляем его
|
|
505
|
+
if not len((old_cond := await models.Cond.get(id=old_cid).prefetch_related("ads")).ads):
|
|
506
|
+
await old_cond.delete()
|
|
507
|
+
logging.warning(f"Cond#{old_cid} deleted!")
|
|
508
|
+
return ad_db or await self.ad_save(ad)
|
|
532
509
|
# если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
|
|
533
510
|
if ad_db:
|
|
534
511
|
await ad_db.fetch_related("cond__ads", "maker")
|
|
@@ -540,11 +517,11 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
540
517
|
{ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
|
|
541
518
|
):
|
|
542
519
|
# создадим новое условие и присвоим его только текущей объяве
|
|
543
|
-
cond = await self.cond_new(cleaned, {int(ad.
|
|
520
|
+
cond = await self.cond_new(cleaned, {int(ad.maker_exid)})
|
|
544
521
|
ad_db.cond_id = cond.id
|
|
545
|
-
await ad_db.save()
|
|
546
|
-
ad_db.cond = cond
|
|
547
|
-
return ad_db
|
|
522
|
+
await ad_db.save(update_fields=["cond_id"])
|
|
523
|
+
ad_db.cond = cond # todo: а это зачем?
|
|
524
|
+
return ad_db
|
|
548
525
|
# а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
|
|
549
526
|
# обновляем условие (в тч во всех ЕГО объявах)
|
|
550
527
|
ad_db.cond.last_ver = ad_db.cond.raw_txt
|
|
@@ -556,12 +533,12 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
556
533
|
await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
|
|
557
534
|
# и подправим коэфициенты похожести нового текста
|
|
558
535
|
await self.fix_rel_sims(ad_db.cond_id, cleaned)
|
|
559
|
-
return ad_db
|
|
560
|
-
|
|
561
|
-
cond = await self.cond_new(cleaned, {int(ad.
|
|
562
|
-
ad_db = await self.
|
|
536
|
+
return ad_db
|
|
537
|
+
# нет ни объяв ни таких условий еще в бд, все новое
|
|
538
|
+
cond = await self.cond_new(cleaned, {int(ad.maker_exid)})
|
|
539
|
+
ad_db = await self.ad_save(ad, cond.id)
|
|
563
540
|
ad_db.cond = cond
|
|
564
|
-
return ad_db
|
|
541
|
+
return ad_db
|
|
565
542
|
|
|
566
543
|
async def cond_new(self, txt: str, uids: set[int]) -> models.Cond:
|
|
567
544
|
new_cond, _ = await models.Cond.update_or_create(raw_txt=txt)
|
xync_client/Abc/Order.py
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from xync_client.Bybit.agent import AgentClient
|
|
3
|
+
from xync_client.Abc.Agent import BaseAgentClient
|
|
4
|
+
from xync_schema.models import Order, PmEx, CredEx
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class BaseOrderClient:
|
|
9
8
|
order: Order
|
|
9
|
+
agent_client: BaseAgentClient
|
|
10
10
|
im_maker: bool
|
|
11
11
|
im_seller: bool
|
|
12
12
|
|
|
13
|
-
def __init__(self, order: Order, agent_client:
|
|
13
|
+
def __init__(self, order: Order, agent_client: BaseAgentClient):
|
|
14
14
|
self.order = order
|
|
15
15
|
self.im_maker = order.taker_id != agent_client.actor.id # or order.ad.agent_id == agent.id
|
|
16
16
|
self.im_seller = order.ad.pair_side.is_sell and self.im_maker
|
|
17
17
|
self.agent_client = agent_client
|
|
18
18
|
|
|
19
|
-
# 2: [T] Отмена запроса на сделку
|
|
20
|
-
@abstractmethod
|
|
21
|
-
async def cancel_request(self) -> Order: ...
|
|
22
|
-
|
|
23
|
-
# 3: [M] Одобрить запрос на сделку
|
|
24
|
-
@abstractmethod
|
|
25
|
-
async def accept_request(self) -> bool: ...
|
|
26
|
-
|
|
27
|
-
# 4: [M] Отклонить запрос на сделку
|
|
28
|
-
@abstractmethod
|
|
29
|
-
async def reject_request(self) -> bool: ...
|
|
30
|
-
|
|
31
19
|
# 5: [B] Перевод сделки в состояние "оплачено", c отправкой чека
|
|
20
|
+
async def mark_payed(self, cred_id: int = None, receipt: bytes = None):
|
|
21
|
+
cred_id = cred_id or self.order.cred_id
|
|
22
|
+
pmex = await PmEx.get(pm__pmcurs__id=self.order.cred.pmcur_id, ex=self.agent_client.ex_client.ex)
|
|
23
|
+
credex = await CredEx.get(cred_id=cred_id, ex=self.agent_client.ex_client.ex)
|
|
24
|
+
await self._mark_payed(credex.exid, pmex.exid, receipt)
|
|
25
|
+
|
|
32
26
|
@abstractmethod
|
|
33
|
-
async def
|
|
27
|
+
async def _mark_payed(self, credex_exid: int = None, pmex_exid: int | str = None, receipt: bytes = None): ...
|
|
34
28
|
|
|
35
29
|
# 6: [B] Отмена сделки
|
|
36
30
|
@abstractmethod
|
|
37
31
|
async def cancel_order(self) -> bool: ...
|
|
38
32
|
|
|
33
|
+
# 6: Запрос отмены (оплаченная контрагентом продажа)
|
|
34
|
+
async def cancel_request(self) -> bool: ...
|
|
35
|
+
|
|
36
|
+
# 6: Одобрение запроса на отмену (оплаченная мной покупка)
|
|
37
|
+
async def cancel_accept(self): ...
|
|
38
|
+
|
|
39
39
|
# 7: [S] Подтвердить получение оплаты
|
|
40
40
|
@abstractmethod
|
|
41
41
|
async def confirm(self) -> bool: ...
|
|
@@ -52,6 +52,9 @@ class BaseOrderClient:
|
|
|
52
52
|
@abstractmethod
|
|
53
53
|
async def cancel_appeal(self) -> bool: ...
|
|
54
54
|
|
|
55
|
+
# 15: Принять аппеляцию
|
|
56
|
+
async def appeal_accept(self): ...
|
|
57
|
+
|
|
55
58
|
# 16: Отправка сообщения юзеру в чат по ордеру с приложенным файлом
|
|
56
59
|
@abstractmethod
|
|
57
60
|
async def send_order_msg(self, msg: str, file=None) -> bool: ...
|