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/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.xtype import CurEx, CoinEx, BaseAd, BaseAdIn
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, GetAds
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, int] = {}
40
+ actor_e2x: dict[int, models.Actor] = {}
43
41
  ad_x2e: dict[int, int] = {}
44
- ad_e2x: dict[int, 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.bot = bot
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
- self.actor_x2e[actor_id] = (await models.Actor[actor_id]).exid
157
- self.actor_e2x[self.actor_x2e[actor_id]] = actor_id
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, exid: int) -> int: # actor.id
161
- if not self.actor_e2x.get(exid):
162
- self.actor_e2x[exid] = (await models.Actor.get(exid=exid, ex=self.ex)).id
163
- self.actor_x2e[self.actor_e2x[exid]] = exid
164
- return self.actor_e2x[exid]
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, exid: int) -> int: # ad.id
173
- if not self.ad_e2x.get(exid):
174
- self.ad_e2x[exid] = (await models.Ad.get(exid=exid, maker__ex=self.ex)).id
175
- self.ad_x2e[self.ad_e2x[exid]] = exid
176
- return self.ad_e2x[exid]
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: GetAds, **kwargs) -> list[BaseAd]:
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) -> BaseAdIn: ... # my_uid: for MyAd
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, CurEx] = {
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 self.bot.save_doc(byts, resp.content_type)
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, headers=self.session.headers, cookies=cookies or None, proxy=proxy.str()
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 person_name_update(self, name: str, exid: int) -> models.Person:
433
- if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex).prefetch_related("person"):
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
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 ad_load(
459
- self,
460
- pad: BaseAd,
461
- cid: int = None,
462
- ps: models.PairSide = None,
463
- maker: models.Actor = None,
464
- coinex: models.CoinEx = None,
465
- curex: models.CurEx = None,
466
- rname: str = None,
467
- ) -> models.Ad:
468
- # self.e2x_actor()
469
- if not maker:
470
- if not (maker := await models.Actor.get_or_none(exid=pad.userId, ex=self.ex)):
471
- person = await models.Person.create(name=rname, note=f"{self.ex.id}:{pad.userId}:{pad.nickName}")
472
- maker = await models.Actor.create(name=pad.nickName, person=person, exid=pad.userId, ex=self.ex)
473
- if rname:
474
- await self.person_name_update(rname, int(pad.userId))
475
- ps = ps or await models.PairSide.get_or_none(
476
- is_sell=pad.side,
477
- pair__coin__ticker=pad.tokenId,
478
- pair__cur__ticker=pad.currencyId,
479
- ).prefetch_related("pair")
480
- # if not ps or not ps.pair:
481
- # ... # THB/USDC: just for initial filling
482
- ad_upd = models.Ad.validate(pad.model_dump(by_alias=True))
483
- cur_scale = 10 ** (curex or await models.CurEx.get(cur_id=ps.pair.cur_id, ex=self.ex)).scale
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(*(await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=pad.payments)))
477
+ await ad_db.pms.add(*ad.pms)
505
478
  return ad_db
506
479
 
507
- async def cond_load( # todo: refact from Bybit Ad format to universal
508
- self,
509
- ad: BaseAd,
510
- ps: models.PairSide = None,
511
- force: bool = False,
512
- rname: str = None,
513
- coinex: models.CoinEx = None,
514
- curex: models.CurEx = None,
515
- pms_from_cond: bool = False,
516
- ) -> tuple[models.Ad, bool]:
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.id, maker__ex=self.ex).prefetch_related("cond")
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.remark)) or (cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)):
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.nickName} upd cond#{ad_db.cond_id}->{cid}")
527
- # old_cid = ad_db.cond_id # todo: solve race-condition, а пока что очищаем при каждом запуске
528
- # if not len((old_cond := await Cond.get(id=old_cid).prefetch_related('ads')).ads):
529
- # await old_cond.delete()
530
- # logging.warning(f"Cond#{old_cid} deleted!")
531
- return (ad_db or force and await self.ad_load(ad, cid, ps, coinex=coinex, curex=curex, rname=rname)), False
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.userId)})
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, True
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, False
560
-
561
- cond = await self.cond_new(cleaned, {int(ad.userId)})
562
- ad_db = await self.ad_load(ad, cond.id, ps, coinex=coinex, curex=curex, rname=rname)
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, True
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 xync_schema.models import Order
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: AgentClient):
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 mark_payed(self, receipt: bytes = None): ...
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: ...