xync-client 0.0.145__py3-none-any.whl → 0.0.148__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.

Potentially problematic release.


This version of xync-client might be problematic. Click here for more details.

@@ -2,7 +2,6 @@ import asyncio
2
2
  import logging
3
3
  import re
4
4
  from asyncio import sleep, gather
5
- from collections import defaultdict
6
5
  from datetime import datetime, timedelta, timezone
7
6
  from difflib import SequenceMatcher
8
7
  from enum import IntEnum
@@ -18,7 +17,7 @@ from payeer_api import PayeerAPI
18
17
  from pyro_client.client.file import FileClient
19
18
  from tortoise import BaseDBAsyncClient
20
19
  from tortoise.exceptions import IntegrityError
21
- from tortoise.expressions import F, Q
20
+ from tortoise.expressions import Q
22
21
  from tortoise.functions import Count
23
22
  from tortoise.signals import post_save
24
23
  from urllib3.exceptions import ReadTimeoutError
@@ -28,10 +27,10 @@ from xync_bot import XyncBot
28
27
  from xync_schema import models
29
28
  from xync_schema.enums import OrderStatus
30
29
 
31
- from xync_schema.models import Actor, Cond, CondSim, PmCur, PairSide, Agent
30
+ from xync_schema.models import Actor, PmCur, Agent
32
31
 
33
32
  from xync_client.Abc.Agent import BaseAgentClient
34
- from xync_client.Abc.xtype import BaseOrderReq, FlatDict
33
+ from xync_client.Abc.xtype import FlatDict, BaseOrderReq
35
34
  from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus
36
35
  from xync_client.Bybit.etype.cred import CredEpyd
37
36
  from xync_client.Bybit.etype.order import (
@@ -43,6 +42,8 @@ from xync_client.Bybit.etype.order import (
43
42
  OrderFull,
44
43
  Message,
45
44
  Status,
45
+ OrderSellRequest,
46
+ TakeAdReq,
46
47
  )
47
48
  from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN
48
49
 
@@ -83,16 +84,12 @@ class AgentClient(BaseAgentClient): # Bybit client
83
84
  "actionType": "MODIFY",
84
85
  "securityRiskToken": "",
85
86
  }
86
- all_conds: dict[int, tuple[str, set[int]]] = {}
87
- cond_sims: dict[int, int] = defaultdict(set)
88
- rcond_sims: dict[int, set[int]] = defaultdict(set) # backward
89
- tree: dict = {}
90
87
 
91
88
  def __init__(self, agent: Agent, fbot: FileClient, bbot: XyncBot, **kwargs):
92
89
  super().__init__(agent, fbot, bbot, **kwargs)
93
90
  self.api = P2P(testnet=False, api_key=agent.auth["key"], api_secret=agent.auth["sec"])
94
- self.hist: dict = None
95
- self.completed_orders: list[int] = None
91
+ self.hist: dict | None = None
92
+ self.completed_orders: list[int] | None = None
96
93
 
97
94
  """ Private METHs"""
98
95
 
@@ -116,12 +113,8 @@ class AgentClient(BaseAgentClient): # Bybit client
116
113
  else:
117
114
  return logging.exception(method1)
118
115
 
119
- async def get_payment_method(self, fiat_id: int = None) -> dict:
120
- list_methods = self.creds()
121
- if fiat_id:
122
- fiat = [m for m in list_methods if m["id"] == fiat_id][0]
123
- return fiat
124
- return list_methods[1]
116
+ def get_payment_method(self, fiat_id: int) -> CredEpyd:
117
+ return self.creds()[fiat_id]
125
118
 
126
119
  def creds(self) -> dict[int, CredEpyd]:
127
120
  data = self.api.get_user_payment_types()
@@ -204,13 +197,13 @@ class AgentClient(BaseAgentClient): # Bybit client
204
197
  # 27
205
198
  async def fiat_upd(self, fiat_id: int, detail: str, name: str = None) -> dict:
206
199
  fiat = self.get_payment_method(fiat_id)
207
- fiat["realName"] = name
208
- fiat["accountNo"] = detail
209
- result = await self._post("/fiat/otc/user/payment/new_update", fiat)
200
+ fiat.realName = name
201
+ fiat.accountNo = detail
202
+ result = await self._post("/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
210
203
  srt = result["result"]["securityRiskToken"]
211
204
  await self._check_2fa(srt)
212
- fiat["securityRiskToken"] = srt
213
- result2 = await self._post("/fiat/otc/user/payment/new_update", fiat)
205
+ fiat.securityRiskToken = srt
206
+ result2 = await self._post("/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
214
207
  return result2
215
208
 
216
209
  # 28
@@ -242,10 +235,6 @@ class AgentClient(BaseAgentClient): # Bybit client
242
235
  cnx.exid, crx.exid, is_sell, [pmex.exid for pmex in pmxs or []], amount, lim, vm_filter
243
236
  )
244
237
 
245
- def online_ads(self) -> str:
246
- online = self._get("/fiat/otc/maker/work-config/get")
247
- return online["result"]["workStatus"]
248
-
249
238
  @staticmethod
250
239
  def get_rate(list_ads: list) -> float:
251
240
  ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
@@ -262,7 +251,7 @@ class AgentClient(BaseAgentClient): # Bybit client
262
251
  ads = self.my_ads(True)
263
252
  if not active:
264
253
  ads += self.my_ads(False)
265
- res = [await self.ad_create(ad, actor=self.actor) for ad in ads]
254
+ res = [await self.ex_client.ad_load(ad, maker=self.actor) for ad in ads]
266
255
  res = [await models.MyAd.update_or_create(ad=ad) for ad in res]
267
256
  return len(res)
268
257
 
@@ -273,20 +262,39 @@ class AgentClient(BaseAgentClient): # Bybit client
273
262
  security_risk_token = data["result"]["securityRiskToken"]
274
263
  return security_risk_token
275
264
 
276
- def _check_2fa(self, risk_token):
277
- # 2fa code
278
- bybit_secret = self.agent.auth["2fa"]
279
- totp = pyotp.TOTP(bybit_secret)
280
- totp_code = totp.now()
281
-
282
- res = self._post(
283
- "/user/public/risk/verify", {"risk_token": risk_token, "component_list": {"google2fa": totp_code}}
265
+ async def _check_2fa(self, risk_token) -> int:
266
+ cres = await self._post("/user/public/risk/components", {"risk_token": risk_token})
267
+ if cres["ret_msg"] != "success":
268
+ raise HTTPException("get")
269
+ cres = cres["result"]["component_list"]
270
+ res = await self._post(
271
+ "/user/public/risk/verify",
272
+ {
273
+ "risk_token": risk_token,
274
+ "component_list": {c["component_id"]: self.__get_2fa(c["component_id"]) for c in cres},
275
+ },
284
276
  )
285
277
  if res["ret_msg"] != "success":
286
- print("Wrong 2fa, wait 5 secs and retry..")
287
- sleep(5)
288
- self._check_2fa(risk_token)
289
- return res
278
+ logging.error("Wrong 2fa, wait 5 secs and retry..")
279
+ await sleep(5)
280
+ await self._check_2fa(risk_token)
281
+ return res["ret_code"]
282
+
283
+ async def __get_2fa(self, typ: Literal["google2fa", "email_verify", "payment_password_verify"], rt: str = None):
284
+ if typ == "google2fa":
285
+ bybit_secret = self.agent.auth["2fa"]
286
+ totp = pyotp.TOTP(bybit_secret)
287
+ return totp.now()
288
+ elif typ == "email_verify":
289
+ res = await self._post("/user/public/risk/send/code", {"risk_token": rt, "component_id": "email_verify"})
290
+ if res["ret_msg"] != "success":
291
+ return self.gmail.bybit_code()
292
+ elif cool_down := int(res["result"]["cool_down"]):
293
+ await sleep(cool_down)
294
+ return self.gmail.bybit_code()
295
+ elif typ == "payment_password_verify":
296
+ return self.agent.auth["pass"]
297
+ raise Exception("2fa fail")
290
298
 
291
299
  def _post_ad(self, risk_token: str):
292
300
  self.create_ad_body.update({"securityRiskToken": risk_token})
@@ -340,27 +348,56 @@ class AgentClient(BaseAgentClient): # Bybit client
340
348
  data = self.api.remove_ad(itemId=ad_id)
341
349
  return data
342
350
 
343
- async def order_request(self, br: BaseOrderReq) -> OrderResp:
344
- res0 = await self._post("/fiat/otc/item/simple", data={"item_id": str(br.ad_id)})
345
- if res0["ret_code"] == 0:
346
- res0 = res0["result"]
347
- res0 = PreOrderResp.model_validate(res0)
351
+ async def __preorder_request(self, ad_id: int) -> PreOrderResp:
352
+ res = await self._post("/fiat/otc/item/simple", data={"item_id": str(ad_id)})
353
+ if res["ret_code"] == 0:
354
+ res = res["result"]
355
+ return PreOrderResp.model_validate(res)
356
+
357
+ async def __order_request_build(self, por: PreOrderResp, br: BaseOrderReq) -> OrderRequest:
348
358
  req = OrderRequest(
349
- itemId=br.ad_id,
359
+ itemId=por.id,
350
360
  tokenId=br.coin_exid,
351
361
  currencyId=br.cur_exid,
352
- side=str(OrderRequest.Side(int(br.is_sell))),
353
- amount=str(br.fiat_amount or br.asset_amount * float(res0.price)),
354
- curPrice=res0.curPrice,
355
- quantity=str(br.asset_amount or round(br.fiat_amount / float(res0.price), br.coin_scale)),
356
- flag="amount" if br.amount_is_fiat else "quantity",
362
+ side="1" if br.is_sell else "0",
363
+ amount=str(br.fiat_amount),
364
+ curPrice=por.curPrice,
365
+ quantity=str(round(br.fiat_amount / float(por.price), br.coin_scale)),
366
+ flag="amount",
367
+ # paymentType="51",
368
+ # paymentId="20399134",
369
+ # online="0"
357
370
  )
371
+ if br.is_sell:
372
+ credex = await models.CredEx.get(
373
+ cred__person_id=self.actor.person_id,
374
+ pmcur__pm__pmexs__exid=por.payments[0],
375
+ pmcur__pm__pmexs__ex_id=self.ex_client.ex.id,
376
+ pmcur__cur_id=br.cur_exid,
377
+ )
378
+ req = OrderSellRequest(**req.model_dump(), paymentType=por.payments[0], paymentId=str(credex.exid))
379
+ return req
380
+
381
+ async def _order_request(self, bor: BaseOrderReq) -> OrderResp:
382
+ por: PreOrderResp = await self.__preorder_request(bor.ad_id)
383
+ req: OrderRequest | OrderSellRequest = await self.__order_request_build(por, bor)
358
384
  # вот непосредственно сам запрос на ордер
359
- res = await self._post("/fiat/otc/order/create", data=req.model_dump())
385
+ return await self.__order_create(req, bor)
386
+
387
+ async def __order_create(self, req: OrderRequest | OrderSellRequest, bor: BaseOrderReq) -> OrderResp:
388
+ res: dict = await self._post("/fiat/otc/order/create", data=req.model_dump())
360
389
  if res["ret_code"] == 0:
361
- return OrderResp.model_validate(res["result"])
390
+ resp = OrderResp.model_validate(res["result"])
362
391
  elif res["ret_code"] == 912120030 or res["ret_msg"] == "The price has changed, please try again later.":
363
- return await self.order_request(br)
392
+ resp = await self._order_request(bor)
393
+ if not resp.orderId and resp.needSecurityRisk:
394
+ if rc := await self._check_2fa(resp.securityRiskToken):
395
+ await self.bbot.send(self.actor.person.user.username_id, f"Bybit 2fa: {rc}")
396
+ raise Exception(f"Bybit 2fa: {rc}")
397
+ # еще раз уже с токеном
398
+ req.securityRiskToken = resp.securityRiskToken
399
+ resp = await self.__order_create(req, bor)
400
+ return resp
364
401
 
365
402
  async def cancel_order(self, order_id: str) -> bool:
366
403
  cr = CancelOrderReq(orderId=order_id)
@@ -444,12 +481,14 @@ class AgentClient(BaseAgentClient): # Bybit client
444
481
  maker_name = order.buyerRealName, order.sellerRealName
445
482
  im_maker = int(order.makerUserId == order.userId)
446
483
  taker_id = (order.userId, order.targetUserId)[im_maker]
447
- taker_person = await self.person_upsert(maker_name[::-1][ad.side], taker_id)
484
+ taker_person = await self.ex_client.person_name_update(maker_name[::-1][ad.side], taker_id)
448
485
  seller_person = (
449
- self.actor.person if order.side else await self.person_upsert(order.sellerRealName, int(order.targetUserId))
486
+ self.actor.person
487
+ if order.side
488
+ else await self.ex_client.person_name_update(order.sellerRealName, int(order.targetUserId))
450
489
  )
451
490
  taker_nick = (self.actor.name, order.targetNickName)[im_maker] # todo: check
452
- ad_db, cond_isnew = await self.cond_upsert(ad, maker_name[ad.side], force=True)
491
+ ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[ad.side])
453
492
  if not ad_db:
454
493
  ...
455
494
  ecredex: CredEpyd = order.confirmedPayTerm
@@ -546,13 +585,13 @@ class AgentClient(BaseAgentClient): # Bybit client
546
585
  to = ((odb.payed_at or odb.created_at) + timedelta(minutes=180 + 30)).isoformat(sep=" ").split("+")[0]
547
586
  tsa = [
548
587
  t
549
- for tid, t in self.hist.items()
588
+ for tid, t in (self.hist.items() if self.hist else [])
550
589
  if (ecredex.accountNo == t["to"] and t["from"] != "@merchant" and frm < t["date"] < to)
551
590
  ]
552
591
  buyer_person = (
553
592
  self.actor.person
554
593
  if not order.side
555
- else await self.person_upsert(order.buyerRealName, int(order.targetUserId))
594
+ else await self.ex_client.person_name_update(order.buyerRealName, int(order.targetUserId))
556
595
  )
557
596
  ts = [t for t in tsa if floor(fa := float(order.amount)) <= float(t["creditedAmount"]) <= round(fa)]
558
597
  if len(ts) != 1:
@@ -705,7 +744,7 @@ class AgentClient(BaseAgentClient): # Bybit client
705
744
  pmexs: list[models.PmEx] = await models.PmEx.filter(pm_id__in=pm_ids, ex=self.actor.ex).prefetch_related("pm")
706
745
  k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
707
746
  sleep_sec = 3 # 1 if set(pms) & {"volet"} and coinex.coin_id == 1 else 5
708
- creds: list[models.CredEx] = await self.get_credexs_by_pms(race.road.ad.pms, curex.cur_id)
747
+ creds: list[models.CredEx] = await self.actor.get_credexs_by(pm_ids, curex.cur_id)
709
748
  _lstat, volume = None, 0
710
749
 
711
750
  while self.actor.person.user.status > 0:
@@ -764,7 +803,7 @@ class AgentClient(BaseAgentClient): # Bybit client
764
803
  await sleep(15)
765
804
  continue
766
805
  (cur_plc,) = cur_plc # может упасть если в списке > 1 наш ad
767
- [(await self.cond_upsert(ad, ps=race.road.ad.pair_side, force=True))[0] for ad in ads[:cur_plc]]
806
+ [(await self.ex_client.cond_load(ad, race.road.ad.pair_side, True))[0] for ad in ads[:cur_plc]]
768
807
  # rivals = [
769
808
  # (await models.RaceStat.update_or_create({"place": plc, "price": ad.price, "premium": ad.premium}, ad=ad))[
770
809
  # 0
@@ -905,296 +944,87 @@ class AgentClient(BaseAgentClient): # Bybit client
905
944
  bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
906
945
  return (bc, sc), hp, vmf, zplace
907
946
 
908
- async def parse_ads(
909
- self,
910
- coinex: models.CoinEx,
911
- curex: models.CurEx,
912
- taker_side: bool,
913
- pms: list[str] = None,
914
- ceil: float = None,
915
- volume: float = 9000,
916
- min_fiat: int = None,
917
- max_fiat: int = None,
918
- ):
919
- k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
920
-
921
- if pms:
922
- creds: dict[models.PmEx, models.CredEx] = await self.get_credexs_by_norms(pms, curex.cur_id)
923
- [str(p.exid) for p in creds.values()]
924
-
925
- if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
926
- fiats = await models.Fiat.filter(
927
- cred_id__in=[cx.cred_id for cx in creds.values()], amount__not=F("target")
928
- )
929
- volume = min(volume, max(fiats, key=lambda f: f.target - f.amount).amount / ceil)
930
- else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
931
- asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
932
- volume = min(volume, asset.free)
933
- volume = str(round(volume, coinex.coin.scale))
934
- ps = await PairSide.get(
935
- is_sell=taker_side,
936
- pair__coin_id=coinex.coin_id,
937
- pair__cur_id=curex.cur_id,
938
- )
939
- while self.actor.person.user.status > 0: # todo: depends on rest asset/fiat
940
- ads: list[Ad] = await self.ads(coinex, curex, taker_side, pms and list(creds.keys()))
941
-
942
- if not ads:
943
- print(coinex.exid, curex.exid, taker_side, "no ads!")
944
- await sleep(300)
945
- continue
946
-
947
- for i, ad in enumerate(ads):
948
- if (ceil - float(ad.price)) * k < 0:
949
- break
950
- if int(ad.userId) == self.actor.exid:
951
- logging.info(f"My ad {'-' if taker_side else '+'}{coinex.exid}/{curex.exid} on place#{i}")
952
- continue
953
- ad_db, isnew = await self.cond_upsert(ad, ps=ps)
954
- if isnew:
955
- s = f"{'-' if taker_side else '+'}{ad.price}[{ad.minAmount}-{ad.maxAmount}]{coinex.exid}/{curex.exid}"
956
- print(s, end=" | ", flush=True)
957
- try:
958
- # take
959
- ...
960
- except FailedRequestError as e:
961
- if ExcCode(e.status_code) == ExcCode.RareLimit:
962
- await sleep(195)
963
- elif ExcCode(e.status_code) == ExcCode.Timestamp:
964
- await sleep(2)
965
- else:
966
- raise e
967
- except (ReadTimeoutError, ConnectionDoesNotExistError):
968
- logging.warning("Connection failed. Restarting..")
969
- await sleep(3)
970
-
971
- async def cond_upsert(
972
- self, ad: Ad, rname: str = None, ps: PairSide = None, force: bool = False
973
- ) -> tuple[models.Ad, bool]:
974
- _sim, cid = None, None
975
- ad_db = await models.Ad.get_or_none(exid=ad.id, maker__ex=self.ex_client.ex).prefetch_related("cond")
976
- # если точно такое условие уже есть в бд
977
- if not (cleaned := clean(ad.remark)) or (cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)):
978
- # и объява с таким ид уже есть, но у нее другое условие
979
- if ad_db and ad_db.cond_id != cid:
980
- # то обновляем ид ее условия
981
- ad_db.cond_id = cid
982
- await ad_db.save()
983
- logging.info(f"{ad.nickName} upd cond#{ad_db.cond_id}->{cid}")
984
- # old_cid = ad_db.cond_id # todo: solve race-condition, а пока что очищаем при каждом запуске
985
- # if not len((old_cond := await Cond.get(id=old_cid).prefetch_related('ads')).ads):
986
- # await old_cond.delete()
987
- # logging.warning(f"Cond#{old_cid} deleted!")
988
- return (ad_db or force and await self.ad_create(ad, cid, rname, ps)), False
989
- # если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
990
- if ad_db:
991
- await ad_db.fetch_related("cond__ads", "maker")
992
- if not ad_db.cond_id or (
993
- # у измененного условия этой объявы есть другие объявы?
994
- (rest_ads := set(ad_db.cond.ads) - {ad_db})
995
- and
996
- # другие объявы этого условия принадлежат другим юзерам
997
- {ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
998
- ):
999
- # создадим новое условие и присвоим его только текущей объяве
1000
- cid = await self.cond_new(cleaned, {int(ad.userId)})
1001
- ad_db.cond_id = cid
1002
- await ad_db.save()
1003
-
1004
- return ad_db, True
1005
- # а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
1006
- # обновляем условие (в тч во всех ЕГО объявах)
1007
- ad_db.cond.last_ver = ad_db.cond.raw_txt
1008
- ad_db.cond.raw_txt = cleaned
1009
- await ad_db.cond.save()
1010
- await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
1011
- # и подправим коэфициенты похожести нового текста
1012
- await self.fix_rel_sims(ad_db.cond_id, cleaned)
1013
- return ad_db, False
1014
-
1015
- cid = await self.cond_new(cleaned, {int(ad.userId)})
1016
- return await self.ad_create(ad, cid, rname, ps), True
1017
-
1018
- async def cond_new(self, txt: str, uids: set[int]) -> int:
1019
- new_cond, _ = await Cond.update_or_create(raw_txt=txt)
1020
- # и максимально похожую связь для нового условия (если есть >= 60%)
1021
- await self.cond_upd(new_cond, uids)
1022
- return new_cond.id
1023
-
1024
- async def cond_upd(self, cond: Cond, uids: set[int]):
1025
- self.all_conds[cond.id] = cond.raw_txt, uids
1026
- # и максимально похожую связь для нового условия (если есть >= 60%)
1027
- old_cid, sim = await self.cond_get_max_sim(cond.id, cond.raw_txt, uids)
1028
- await self.actual_sim(cond.id, old_cid, sim)
1029
-
1030
- def find_in_tree(self, cid: int, old_cid: int) -> bool:
1031
- if p := self.cond_sims.get(old_cid):
1032
- if p == cid:
1033
- return True
1034
- return self.find_in_tree(cid, p)
1035
- return False
1036
-
1037
- async def cond_get_max_sim(self, cid: int, txt: str, uids: set[int]) -> tuple[int | None, int | None]:
1038
- # находим все старые тексты похожие на 90% и более
1039
- if len(txt) < 15:
1040
- return None, None
1041
- sims: dict[int, int] = {}
1042
- for old_cid, (old_txt, old_uids) in self.all_conds.items():
1043
- if len(old_txt) < 15 or uids == old_uids:
1044
- continue
1045
- elif not self.can_add_sim(cid, old_cid):
1046
- continue
1047
- if sim := get_sim(txt, old_txt):
1048
- sims[old_cid] = sim
1049
- # если есть, берем самый похожий из них
1050
- if sims:
1051
- old_cid, sim = max(sims.items(), key=lambda x: x[1])
1052
- await sleep(0.3)
1053
- return old_cid, sim
1054
- return None, None
1055
-
1056
- def can_add_sim(self, cid: int, old_cid: int) -> bool:
1057
- if cid == old_cid:
1058
- return False
1059
- elif self.cond_sims.get(cid) == old_cid:
1060
- return False
1061
- elif self.find_in_tree(cid, old_cid):
1062
- return False
1063
- elif self.cond_sims.get(old_cid) == cid:
1064
- return False
1065
- elif cid in self.rcond_sims.get(old_cid, {}):
1066
- return False
1067
- elif old_cid in self.rcond_sims.get(cid, {}):
1068
- return False
1069
- return True
1070
-
1071
- async def person_upsert(self, name: str, exid: int) -> models.Person:
1072
- if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex_client.ex).prefetch_related("person"):
1073
- if not actor.person:
1074
- actor.person = await models.Person.create(name=name)
1075
- await actor.save()
1076
- return actor.person
1077
- return await models.Person.create(name=name)
1078
-
1079
- async def ad_create(
1080
- self, ad: Ad, cid: int = None, rname: str = None, ps: PairSide = None, actor: Actor = None
1081
- ) -> models.Ad:
1082
- act_df = {}
1083
- if int(ad.userId) != self.actor.exid:
1084
- act_df |= {"name": ad.nickName}
1085
- if rname:
1086
- act_df |= {"person": await self.person_upsert(rname, int(ad.userId))}
1087
- if not actor:
1088
- act_df |= {"person": await self.person_upsert(ad.nickName, int(ad.userId))}
1089
- actor, _ = await Actor.update_or_create(act_df, exid=ad.userId, ex=self.ex_client.ex)
1090
- ps = ps or await PairSide.get_or_none(
1091
- is_sell=ad.side,
1092
- pair__coin__ticker=ad.tokenId,
1093
- pair__cur__ticker=ad.currencyId,
1094
- ).prefetch_related("pair__cur", "pair__coin")
1095
- if not ps or not ps.pair:
1096
- ... # THB/USDC
1097
- ad_upd = models.Ad.validate(ad.model_dump(by_alias=True))
1098
- cur_scale = 10**ps.pair.cur.scale
1099
- coinex = await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex_client.ex)
1100
- df_unq = ad_upd.df_unq(
1101
- maker_id=actor.id,
1102
- pair_side_id=ps.id,
1103
- amount=int(float(ad.quantity) * float(ad.price) * cur_scale),
1104
- quantity=int(float(ad.quantity) * 10**coinex.scale),
1105
- min_fiat=int(float(ad.minAmount) * cur_scale),
1106
- max_fiat=ad.maxAmount and int(float(ad.maxAmount) * cur_scale),
1107
- price=int(float(ad.price) * cur_scale),
1108
- premium=int(float(ad.premium) * cur_scale),
947
+ async def take_ad(self, req: TakeAdReq):
948
+ ad: Ad = Ad.model_validate(self.api.get_ad_details(itemId=req.ad_id))
949
+ bor = BaseOrderReq(
950
+ ad_id=str(ad.id),
951
+ fiat_amount=req.amount,
952
+ is_sell=bool(ad.side),
953
+ cur_exid=ad.currencyId,
954
+ coin_exid=ad.tokenId,
955
+ coin_scale=ad.token.scale,
956
+ pm_id=req.pm_id,
1109
957
  )
1110
- ad_db, _ = await models.Ad.update_or_create(**df_unq)
1111
- await ad_db.pms.add(*(await models.Pm.filter(pmexs__ex=self.ex_client.ex, pmexs__exid__in=ad.payments)))
1112
- return ad_db
1113
-
1114
- async def fix_rel_sims(self, cid: int, new_txt: str):
1115
- for rel_sim in await CondSim.filter(cond_rel_id=cid).prefetch_related("cond"):
1116
- if sim := get_sim(new_txt, rel_sim.cond.raw_txt):
1117
- rel_sim.similarity = sim
1118
- await rel_sim.save()
1119
- else:
1120
- await rel_sim.delete()
1121
-
1122
- async def actual_cond(self):
1123
- for curr, old in await CondSim.all().values_list("cond_id", "cond_rel_id"):
1124
- self.cond_sims[curr] = old
1125
- self.rcond_sims[old] |= {curr}
1126
- for cid, (txt, uids) in self.all_conds.items():
1127
- old_cid, sim = await self.cond_get_max_sim(cid, txt, uids)
1128
- await self.actual_sim(cid, old_cid, sim)
1129
- # хз бля чо это ваще
1130
- # for ad_db in await models.Ad.filter(direction__pairex__ex=self.ex_client.ex).prefetch_related("cond", "maker"):
1131
- # ad = Ad(id=str(ad_db.exid), userId=str(ad_db.maker.exid), remark=ad_db.cond.raw_txt)
1132
- # await self.cond_upsert(ad, force=True)
1133
-
1134
- async def actual_sim(self, cid: int, old_cid: int, sim: int):
1135
- if not sim:
1136
- return
1137
- if old_sim := await CondSim.get_or_none(cond_id=cid):
1138
- if old_sim.cond_rel_id != old_cid:
1139
- if sim > old_sim.similarity:
1140
- logging.warning(f"R {cid}: {old_sim.similarity}->{sim} ({old_sim.cond_rel_id}->{old_cid})")
1141
- await old_sim.update_from_dict({"similarity": sim, "old_rel_id": old_cid}).save()
1142
- self._cond_sim_upd(cid, old_cid)
1143
- elif sim != old_sim.similarity:
1144
- logging.info(f"{cid}: {old_sim.similarity}->{sim}")
1145
- await old_sim.update_from_dict({"similarity": sim}).save()
1146
- else:
1147
- await CondSim.create(cond_id=cid, cond_rel_id=old_cid, similarity=sim)
1148
- self._cond_sim_upd(cid, old_cid)
1149
-
1150
- def _cond_sim_upd(self, cid: int, old_cid: int):
1151
- if old_old_cid := self.cond_sims.get(cid): # если старый cid уже был в дереве:
1152
- self.rcond_sims[old_old_cid].remove(cid) # удаляем из обратного
1153
- self.cond_sims[cid] = old_cid # а в прямом он автоматом переопределится, даже если и был
1154
- self.rcond_sims[old_cid] |= {cid} # ну и в обратное добавим новый
1155
-
1156
- async def get_credexs_by_pms(self, pms: list[models.Pm], cur_id: int) -> list[models.CredEx]:
1157
- return await models.CredEx.filter(
1158
- ex_id=self.actor.ex_id,
1159
- cred__pmcur__pm_id__in=[pm.id for pm in pms],
1160
- cred__person_id=self.actor.person_id,
1161
- cred__pmcur__cur_id=cur_id,
1162
- )
1163
-
1164
- def build_tree(self):
1165
- set(self.cond_sims.keys()) | set(self.cond_sims.values())
1166
- tree = defaultdict(dict)
1167
- # Группируем родителей по детям
1168
- for child, par in self.cond_sims.items():
1169
- tree[par] |= {child: {}} # todo: make from self.rcond_sim
1170
-
1171
- # Строим дерево снизу вверх
1172
- def subtree(node):
1173
- if not node:
1174
- return node
1175
- for key in node:
1176
- subnode = tree.pop(key, {})
1177
- d = subtree(subnode)
1178
- node[key] |= d # actual tree rebuilding here!
1179
- return node # todo: refact?
1180
-
1181
- # Находим корни / без родителей
1182
- roots = set(self.cond_sims.values()) - set(self.cond_sims.keys())
1183
- for root in roots:
1184
- _ = subtree(tree[root])
1185
-
1186
- self.tree = tree
958
+ resp: OrderResp = await self._order_request(bor)
959
+ return resp
960
+
961
+ # async def parse_ads(
962
+ # self,
963
+ # coinex: models.CoinEx,
964
+ # curex: models.CurEx,
965
+ # taker_side: bool,
966
+ # pms: list[str] = None,
967
+ # ceil: float = None,
968
+ # volume: float = 9000,
969
+ # min_fiat: int = None,
970
+ # max_fiat: int = None,
971
+ # ):
972
+ # k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
973
+ # if pms:
974
+ # creds: dict[models.PmEx, models.CredEx] = await self.get_credexs_by_norms(pms, curex.cur_id)
975
+ # [str(p.exid) for p in creds.values()]
976
+ #
977
+ # if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
978
+ # fiats = await models.Fiat.filter(
979
+ # cred_id__in=[cx.cred_id for cx in creds.values()], amount__not=F("target")
980
+ # )
981
+ # volume = min(volume, max(fiats, key=lambda f: f.target - f.amount).amount / ceil)
982
+ # else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
983
+ # asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
984
+ # volume = min(volume, asset.free)
985
+ # volume = str(round(volume, coinex.coin.scale))
986
+ # ps = await PairSide.get(
987
+ # is_sell=taker_side,
988
+ # pair__coin_id=coinex.coin_id,
989
+ # pair__cur_id=curex.cur_id,
990
+ # )
991
+ # while self.actor.person.user.status > 0: # todo: depends on rest asset/fiat
992
+ # ads: list[Ad] = await self.ads(coinex, curex, taker_side, pms and list(creds.keys()))
993
+ #
994
+ # if not ads:
995
+ # print(coinex.exid, curex.exid, taker_side, "no ads!")
996
+ # await sleep(300)
997
+ # continue
998
+ #
999
+ # for i, ad in enumerate(ads):
1000
+ # if (ceil - float(ad.price)) * k < 0:
1001
+ # break
1002
+ # if int(ad.userId) == self.actor.exid:
1003
+ # logging.info(f"My ad {'-' if taker_side else '+'}{coinex.exid}/{curex.exid} on place#{i}")
1004
+ # continue
1005
+ # ad_db, isnew = await self.cond_upsert(ad, ps=ps)
1006
+ # if isnew:
1007
+ # s = f"{'-' if taker_side else '+'}{ad.price}[{ad.minAmount}-{ad.maxAmount}]{coinex.exid}/{curex.exid}"
1008
+ # print(s, end=" | ", flush=True)
1009
+ # try:
1010
+ # # take
1011
+ # ...
1012
+ # except FailedRequestError as e:
1013
+ # if ExcCode(e.status_code) == ExcCode.RareLimit:
1014
+ # await sleep(195)
1015
+ # elif ExcCode(e.status_code) == ExcCode.Timestamp:
1016
+ # await sleep(2)
1017
+ # else:
1018
+ # raise e
1019
+ # except (ReadTimeoutError, ConnectionDoesNotExistError):
1020
+ # logging.warning("Connection failed. Restarting..")
1021
+ # await sleep(3)
1187
1022
 
1188
1023
 
1189
1024
  def ms2utc(msk_ts_str: str):
1190
1025
  return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
1191
1026
 
1192
1027
 
1193
- def get_sim(s1, s2) -> int:
1194
- sim = int((SequenceMatcher(None, s1, s2).ratio() - 0.6) * 10_000)
1195
- return sim if sim > 0 else 0
1196
-
1197
-
1198
1028
  def detailed_diff(str1, str2):
1199
1029
  matcher = SequenceMatcher(None, str1, str2)
1200
1030
  result = []
@@ -1212,14 +1042,6 @@ def detailed_diff(str1, str2):
1212
1042
  return "".join(result)
1213
1043
 
1214
1044
 
1215
- def clean(s) -> str:
1216
- clear = r"[^\w\s.,!?;:()\-]"
1217
- repeat = r"(.)\1{2,}"
1218
- s = re.sub(clear, "", s).lower()
1219
- s = re.sub(repeat, r"\1", s)
1220
- return s.replace("\n\n", "\n").replace(" ", " ").strip(" \n/.,!?-")
1221
-
1222
-
1223
1045
  def step_is_need(mad, cad) -> bool:
1224
1046
  # todo: пока не решен непонятный кейс, почему то конкурент по всем параметрам слабже, но в списке ранжируется выше.
1225
1047
  # текущая версия: recentExecuteRate округляется до целого, но на бэке байбита его дробная часть больше
@@ -1262,20 +1084,20 @@ async def main():
1262
1084
  else: # параметры гонки изменены
1263
1085
  ...
1264
1086
 
1265
- actor = (
1087
+ agent = (
1266
1088
  await models.Agent.filter(actor__ex_id=4, auth__isnull=False, active=True)
1267
- .prefetch_related("actor__ex", "actor__person__user")
1089
+ .prefetch_related("actor__ex", "actor__person__user__gmail")
1268
1090
  .first()
1269
1091
  )
1270
1092
  filebot = FileClient(NET_TOKEN)
1271
1093
  await filebot.start()
1272
1094
  # b.add_handler(MessageHandler(cond_start_handler, command("cond")))
1273
- cl: AgentClient = actor.client(filebot, XyncBot(PAY_TOKEN, cn))
1095
+ cl: AgentClient = agent.client(filebot, XyncBot(PAY_TOKEN, cn))
1274
1096
 
1275
1097
  # await cl.ex_client.set_pairs()
1276
1098
  # await cl.ex_client.set_pms()
1277
- # await cl.set_creds()
1278
- # await cl.export_my_ads()
1099
+ await cl.set_creds()
1100
+ await cl.export_my_ads()
1279
1101
 
1280
1102
  # создание гонок по мои активным объявам:
1281
1103
  # for ma in cl.my_ads():
@@ -1289,28 +1111,6 @@ async def main():
1289
1111
  # s, _ = await models.Synonym.update_or_create(typ=SynonymType.name, txt=name)
1290
1112
  # await s.curs.add(rub.cur)
1291
1113
 
1292
- # пока порешали рейс-кондишн, очищаем сиротские условия при каждом запуске
1293
- # [await c.delete() for c in await Cond.filter(ads__isnull=True)]
1294
- cl.all_conds = {
1295
- c.id: (c.raw_txt, {a.maker.exid for a in c.ads}) for c in await Cond.all().prefetch_related("ads__maker")
1296
- }
1297
- for curr, old in await CondSim.filter().values_list("cond_id", "cond_rel_id"):
1298
- cl.cond_sims[curr] = old
1299
- cl.rcond_sims[old] |= {curr}
1300
-
1301
- cl.build_tree()
1302
- a = set()
1303
-
1304
- def check_tree(tre):
1305
- for p, c in tre.items():
1306
- a.add(p)
1307
- check_tree(c)
1308
-
1309
- for pr, ch in cl.tree.items():
1310
- check_tree(ch)
1311
- if ct := set(cl.tree.keys()) & a:
1312
- logging.exception(f"cycle cids: {ct}")
1313
-
1314
1114
  pauth = (await models.PmAgent[1]).auth
1315
1115
  papi = PayeerAPI(pauth["email"], pauth["api_id"], pauth["api_sec"])
1316
1116
  hist: dict = papi.history(count=1000)
@@ -1323,7 +1123,7 @@ async def main():
1323
1123
  # )
1324
1124
  # await cl.get_api_orders() # 43, 1741294800000, 1749157199999)
1325
1125
 
1326
- races = await models.Race.filter(started=True).prefetch_related(
1126
+ races = await models.Race.filter(started=True, road__ad__maker_id=agent.actor_id).prefetch_related(
1327
1127
  "road__ad__pair_side__pair__cur",
1328
1128
  "road__ad__pms",
1329
1129
  )
@@ -1335,20 +1135,10 @@ async def main():
1335
1135
  # cl.get_api_orders(), # 10, 1738357200000, 1742504399999
1336
1136
  )
1337
1137
  except Exception as e:
1338
- await filebot.send("🤬Bybit agent CRASHED!!!🤬", actor.person.user.username_id)
1339
- await filebot.send(e.__repr__(), actor.person.user.username_id)
1138
+ await filebot.send("🤬Bybit agent CRASHED!!!🤬", agent.actor.person.user.username_id)
1139
+ await filebot.send(e.__repr__(), agent.actor.person.user.username_id)
1340
1140
  raise e
1341
- # bor = BaseOrderReq(
1342
- # ad_id="1861440060199632896",
1343
- # # asset_amount=40,
1344
- # fiat_amount=3000,
1345
- # amount_is_fiat=True,
1346
- # is_sell=False,
1347
- # cur_exid=rub.exid,
1348
- # coin_exid=usdt.exid,
1349
- # coin_scale=usdt.coin.scale,
1350
- # )
1351
- # res: OrderResp = await cl.order_request(bor)
1141
+
1352
1142
  # await cl.cancel_order(res.orderId)
1353
1143
  await filebot.stop()
1354
1144
  await cl.close()