xync-client 0.0.145__tar.gz → 0.0.148__tar.gz
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.
- {xync_client-0.0.145/xync_client.egg-info → xync_client-0.0.148}/PKG-INFO +4 -1
- {xync_client-0.0.145 → xync_client-0.0.148}/pyproject.toml +3 -0
- xync_client-0.0.148/xync_client/Abc/AdLoader.py +285 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Agent.py +2 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Ex.py +11 -9
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/PmAgent.py +1 -1
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/xtype.py +3 -1
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/InAgent.py +16 -11
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/agent.py +181 -391
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/etype/ad.py +1 -1
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/etype/cred.py +1 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/etype/order.py +34 -16
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Gmail/__init__.py +16 -3
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Payeer/__init__.py +7 -5
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Volet/__init__.py +1 -1
- {xync_client-0.0.145 → xync_client-0.0.148/xync_client.egg-info}/PKG-INFO +4 -1
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client.egg-info/SOURCES.txt +1 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client.egg-info/requires.txt +3 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/.env.sample +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/.gitignore +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/README.md +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/makefile +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/setup.cfg +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/TestAgent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/TestAsset.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/TestEx.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/TestOrder.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Auth.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Exception.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/HasAbotUid.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/InAgent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BitGet/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/BitPapa/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/order.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Bybit/ws.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Gate/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Gate/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/etype/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/etype/cred.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/etype/test.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/KuCoin/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/KuCoin/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/KuCoin/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Mexc/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Mexc/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Mexc/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Okx/etype/ad.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Okx/etype/pm.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/.gitignore +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Alfa/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Alfa/state.json +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/MTS/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Ozon/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Payeer/.gitignore +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Payeer/api.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Payeer/login.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Sber/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Sber/utils.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Tinkoff/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Volet/api.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Volet/pl.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Xync/__main__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Xync/ed.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/Pms/Yandex/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/agent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/auth.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/ex.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/pyd.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/pyro.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/__init__.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/details.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/loader.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client/pm_unifier.py +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.145 → xync_client-0.0.148}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xync-client
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.148
|
|
4
4
|
Author-email: Mike Artemiev <mixartemev@gmail.com>
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/XyncNet/client
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/XyncNet/client
|
|
@@ -8,6 +8,9 @@ Requires-Python: >=3.11
|
|
|
8
8
|
Requires-Dist: asynchuobi
|
|
9
9
|
Requires-Dist: bs4
|
|
10
10
|
Requires-Dist: bybit-p2p
|
|
11
|
+
Requires-Dist: google-api-python-client
|
|
12
|
+
Requires-Dist: google-auth-httplib2
|
|
13
|
+
Requires-Dist: google-auth-oauthlib
|
|
11
14
|
Requires-Dist: requests-toolbelt
|
|
12
15
|
Requires-Dist: msgspec
|
|
13
16
|
Requires-Dist: python-binance
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from asyncio import sleep
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from difflib import SequenceMatcher
|
|
6
|
+
|
|
7
|
+
from xync_schema import models
|
|
8
|
+
from xync_schema.xtype import BaseAd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AdLoader:
|
|
12
|
+
ex: models.Ex
|
|
13
|
+
all_conds: dict[int, tuple[str, set[int]]] = {}
|
|
14
|
+
cond_sims: dict[int, int] = defaultdict(set)
|
|
15
|
+
rcond_sims: dict[int, set[int]] = defaultdict(set) # backward
|
|
16
|
+
tree: dict = {}
|
|
17
|
+
|
|
18
|
+
async def old_conds_load(self):
|
|
19
|
+
# пока не порешали рейс-кондишн, очищаем сиротские условия при каждом запуске
|
|
20
|
+
# [await c.delete() for c in await Cond.filter(ads__isnull=True)]
|
|
21
|
+
self.all_conds = {
|
|
22
|
+
c.id: (c.raw_txt, {a.maker.exid for a in c.ads})
|
|
23
|
+
for c in await models.Cond.all().prefetch_related("ads__maker")
|
|
24
|
+
}
|
|
25
|
+
for curr, old in await models.CondSim.filter().values_list("cond_id", "cond_rel_id"):
|
|
26
|
+
self.cond_sims[curr] = old
|
|
27
|
+
self.rcond_sims[old] |= {curr}
|
|
28
|
+
|
|
29
|
+
self.build_tree()
|
|
30
|
+
a = set()
|
|
31
|
+
|
|
32
|
+
def check_tree(tre):
|
|
33
|
+
for p, c in tre.items():
|
|
34
|
+
a.add(p)
|
|
35
|
+
check_tree(c)
|
|
36
|
+
|
|
37
|
+
for pr, ch in self.tree.items():
|
|
38
|
+
check_tree(ch)
|
|
39
|
+
if ct := set(self.tree.keys()) & a:
|
|
40
|
+
logging.exception(f"cycle cids: {ct}")
|
|
41
|
+
|
|
42
|
+
async def person_name_update(self, name: str, exid: int) -> models.Person:
|
|
43
|
+
if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex).prefetch_related("person"):
|
|
44
|
+
actor.person.name = name
|
|
45
|
+
await actor.person.save()
|
|
46
|
+
return actor.person
|
|
47
|
+
# return await models.Person.create(note=f'{actor.ex_id}:{actor.exid}:{name}') # no person for just ads with no orders
|
|
48
|
+
raise ValueError(f"Agent #{exid} not found")
|
|
49
|
+
|
|
50
|
+
async def ad_load(
|
|
51
|
+
self,
|
|
52
|
+
pad: BaseAd,
|
|
53
|
+
cid: int = None,
|
|
54
|
+
ps: models.PairSide = None,
|
|
55
|
+
maker: models.Actor = None,
|
|
56
|
+
coinex: models.CoinEx = None,
|
|
57
|
+
curex: models.CurEx = None,
|
|
58
|
+
rname: str = None,
|
|
59
|
+
pms_from_cond: bool = False,
|
|
60
|
+
) -> models.Ad:
|
|
61
|
+
if not maker:
|
|
62
|
+
if not (maker := await models.Actor.get_or_none(exid=pad.userId, ex=self.ex)):
|
|
63
|
+
person = await models.Person.create(name=rname, note=f"{self.ex.id}:{pad.userId}:{pad.nickName}")
|
|
64
|
+
maker = await models.Actor.create(name=pad.nickName, person=person, exid=pad.userId, ex=self.ex)
|
|
65
|
+
if rname:
|
|
66
|
+
await self.person_name_update(rname, int(pad.userId))
|
|
67
|
+
ps = ps or await models.PairSide.get_or_none(
|
|
68
|
+
is_sell=pad.side,
|
|
69
|
+
pair__coin__ticker=pad.tokenId,
|
|
70
|
+
pair__cur__ticker=pad.currencyId,
|
|
71
|
+
).prefetch_related("pair")
|
|
72
|
+
# if not ps or not ps.pair:
|
|
73
|
+
# ... # THB/USDC: just for initial filling
|
|
74
|
+
ad_upd = models.Ad.validate(pad.model_dump(by_alias=True))
|
|
75
|
+
cur_scale = 10 ** (curex or await models.CurEx.get(coin_id=ps.pair.cur_id, ex=self.ex)).scale
|
|
76
|
+
coin_scale = 10 ** (coinex or await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex)).scale
|
|
77
|
+
df_unq = ad_upd.df_unq(
|
|
78
|
+
maker_id=maker.id,
|
|
79
|
+
pair_side_id=ps.id,
|
|
80
|
+
amount=int(float(pad.quantity) * float(pad.price) * cur_scale),
|
|
81
|
+
quantity=int(float(pad.quantity) * coin_scale),
|
|
82
|
+
min_fiat=int(float(pad.minAmount) * cur_scale),
|
|
83
|
+
max_fiat=pad.maxAmount and int(float(pad.maxAmount) * cur_scale),
|
|
84
|
+
price=int(float(pad.price) * cur_scale),
|
|
85
|
+
premium=int(float(pad.premium) * 100),
|
|
86
|
+
cond_id=cid,
|
|
87
|
+
)
|
|
88
|
+
ad_db, _ = await models.Ad.update_or_create(**df_unq)
|
|
89
|
+
if not pms_from_cond:
|
|
90
|
+
await ad_db.pms.add(*(await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=pad.payments)))
|
|
91
|
+
return ad_db
|
|
92
|
+
|
|
93
|
+
async def cond_load( # todo: refact from Bybit Ad format to universal
|
|
94
|
+
self,
|
|
95
|
+
ad: BaseAd,
|
|
96
|
+
ps: models.PairSide = None,
|
|
97
|
+
force: bool = False,
|
|
98
|
+
rname: str = None,
|
|
99
|
+
coinex: models.CoinEx = None,
|
|
100
|
+
curex: models.CurEx = None,
|
|
101
|
+
pms_from_cond: bool = False,
|
|
102
|
+
) -> tuple[models.Ad, bool]:
|
|
103
|
+
_sim, cid = None, None
|
|
104
|
+
ad_db = await models.Ad.get_or_none(exid=ad.id, maker__ex=self.ex).prefetch_related("cond")
|
|
105
|
+
# если точно такое условие уже есть в бд
|
|
106
|
+
if not (cleaned := clean(ad.remark)) or (cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)):
|
|
107
|
+
# и объява с таким ид уже есть, но у нее другое условие
|
|
108
|
+
if ad_db and ad_db.cond_id != cid:
|
|
109
|
+
# то обновляем ид ее условия
|
|
110
|
+
ad_db.cond_id = cid
|
|
111
|
+
await ad_db.save()
|
|
112
|
+
logging.info(f"{ad.nickName} upd cond#{ad_db.cond_id}->{cid}")
|
|
113
|
+
# old_cid = ad_db.cond_id # todo: solve race-condition, а пока что очищаем при каждом запуске
|
|
114
|
+
# if not len((old_cond := await Cond.get(id=old_cid).prefetch_related('ads')).ads):
|
|
115
|
+
# await old_cond.delete()
|
|
116
|
+
# logging.warning(f"Cond#{old_cid} deleted!")
|
|
117
|
+
return (
|
|
118
|
+
ad_db
|
|
119
|
+
or force
|
|
120
|
+
and await self.ad_load(
|
|
121
|
+
ad, cid, ps, coinex=coinex, curex=curex, rname=rname, pms_from_cond=pms_from_cond
|
|
122
|
+
)
|
|
123
|
+
), False
|
|
124
|
+
# если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
|
|
125
|
+
if ad_db:
|
|
126
|
+
await ad_db.fetch_related("cond__ads", "maker")
|
|
127
|
+
if not ad_db.cond_id or (
|
|
128
|
+
# у измененного условия этой объявы есть другие объявы?
|
|
129
|
+
(rest_ads := set(ad_db.cond.ads) - {ad_db})
|
|
130
|
+
and
|
|
131
|
+
# другие объявы этого условия принадлежат другим юзерам
|
|
132
|
+
{ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
|
|
133
|
+
):
|
|
134
|
+
# создадим новое условие и присвоим его только текущей объяве
|
|
135
|
+
cid = await self.cond_new(cleaned, {int(ad.userId)})
|
|
136
|
+
ad_db.cond_id = cid
|
|
137
|
+
await ad_db.save()
|
|
138
|
+
|
|
139
|
+
return ad_db, True
|
|
140
|
+
# а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
|
|
141
|
+
# обновляем условие (в тч во всех ЕГО объявах)
|
|
142
|
+
ad_db.cond.last_ver = ad_db.cond.raw_txt
|
|
143
|
+
ad_db.cond.raw_txt = cleaned
|
|
144
|
+
await ad_db.cond.save()
|
|
145
|
+
await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
|
|
146
|
+
# и подправим коэфициенты похожести нового текста
|
|
147
|
+
await self.fix_rel_sims(ad_db.cond_id, cleaned)
|
|
148
|
+
return ad_db, False
|
|
149
|
+
|
|
150
|
+
cid = await self.cond_new(cleaned, {int(ad.userId)})
|
|
151
|
+
return await self.ad_load(
|
|
152
|
+
ad, cid, ps, coinex=coinex, curex=curex, rname=rname, pms_from_cond=pms_from_cond
|
|
153
|
+
), True
|
|
154
|
+
|
|
155
|
+
async def cond_new(self, txt: str, uids: set[int]) -> int:
|
|
156
|
+
new_cond, _ = await models.Cond.update_or_create(raw_txt=txt)
|
|
157
|
+
# и максимально похожую связь для нового условия (если есть >= 60%)
|
|
158
|
+
await self.cond_upd(new_cond, uids)
|
|
159
|
+
return new_cond.id
|
|
160
|
+
|
|
161
|
+
async def cond_upd(self, cond: models.Cond, uids: set[int]):
|
|
162
|
+
self.all_conds[cond.id] = cond.raw_txt, uids
|
|
163
|
+
# и максимально похожую связь для нового условия (если есть >= 60%)
|
|
164
|
+
old_cid, sim = await self.cond_get_max_sim(cond.id, cond.raw_txt, uids)
|
|
165
|
+
await self.actual_sim(cond.id, old_cid, sim)
|
|
166
|
+
|
|
167
|
+
def find_in_tree(self, cid: int, old_cid: int) -> bool:
|
|
168
|
+
if p := self.cond_sims.get(old_cid):
|
|
169
|
+
if p == cid:
|
|
170
|
+
return True
|
|
171
|
+
return self.find_in_tree(cid, p)
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
async def cond_get_max_sim(self, cid: int, txt: str, uids: set[int]) -> tuple[int | None, int | None]:
|
|
175
|
+
# находим все старые тексты похожие на 90% и более
|
|
176
|
+
if len(txt) < 15:
|
|
177
|
+
return None, None
|
|
178
|
+
sims: dict[int, int] = {}
|
|
179
|
+
for old_cid, (old_txt, old_uids) in self.all_conds.items():
|
|
180
|
+
if len(old_txt) < 15 or uids == old_uids:
|
|
181
|
+
continue
|
|
182
|
+
elif not self.can_add_sim(cid, old_cid):
|
|
183
|
+
continue
|
|
184
|
+
if sim := get_sim(txt, old_txt):
|
|
185
|
+
sims[old_cid] = sim
|
|
186
|
+
# если есть, берем самый похожий из них
|
|
187
|
+
if sims:
|
|
188
|
+
old_cid, sim = max(sims.items(), key=lambda x: x[1])
|
|
189
|
+
await sleep(0.3)
|
|
190
|
+
return old_cid, sim
|
|
191
|
+
return None, None
|
|
192
|
+
|
|
193
|
+
def can_add_sim(self, cid: int, old_cid: int) -> bool:
|
|
194
|
+
if cid == old_cid:
|
|
195
|
+
return False
|
|
196
|
+
elif self.cond_sims.get(cid) == old_cid:
|
|
197
|
+
return False
|
|
198
|
+
elif self.find_in_tree(cid, old_cid):
|
|
199
|
+
return False
|
|
200
|
+
elif self.cond_sims.get(old_cid) == cid:
|
|
201
|
+
return False
|
|
202
|
+
elif cid in self.rcond_sims.get(old_cid, {}):
|
|
203
|
+
return False
|
|
204
|
+
elif old_cid in self.rcond_sims.get(cid, {}):
|
|
205
|
+
return False
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
async def fix_rel_sims(self, cid: int, new_txt: str):
|
|
209
|
+
for rel_sim in await models.CondSim.filter(cond_rel_id=cid).prefetch_related("cond"):
|
|
210
|
+
if sim := get_sim(new_txt, rel_sim.cond.raw_txt):
|
|
211
|
+
rel_sim.similarity = sim
|
|
212
|
+
await rel_sim.save()
|
|
213
|
+
else:
|
|
214
|
+
await rel_sim.delete()
|
|
215
|
+
|
|
216
|
+
async def actual_cond(self):
|
|
217
|
+
for curr, old in await models.CondSim.all().values_list("cond_id", "cond_rel_id"):
|
|
218
|
+
self.cond_sims[curr] = old
|
|
219
|
+
self.rcond_sims[old] |= {curr}
|
|
220
|
+
for cid, (txt, uids) in self.all_conds.items():
|
|
221
|
+
old_cid, sim = await self.cond_get_max_sim(cid, txt, uids)
|
|
222
|
+
await self.actual_sim(cid, old_cid, sim)
|
|
223
|
+
# хз бля чо это ваще
|
|
224
|
+
# for ad_db in await models.Ad.filter(direction__pairex__ex=self.ex).prefetch_related("cond", "maker"):
|
|
225
|
+
# ad = Ad(id=str(ad_db.exid), userId=str(ad_db.maker.exid), remark=ad_db.cond.raw_txt)
|
|
226
|
+
# await self.cond_upsert(ad, force=True)
|
|
227
|
+
|
|
228
|
+
async def actual_sim(self, cid: int, old_cid: int, sim: int):
|
|
229
|
+
if not sim:
|
|
230
|
+
return
|
|
231
|
+
if old_sim := await models.CondSim.get_or_none(cond_id=cid):
|
|
232
|
+
if old_sim.cond_rel_id != old_cid:
|
|
233
|
+
if sim > old_sim.similarity:
|
|
234
|
+
logging.warning(f"R {cid}: {old_sim.similarity}->{sim} ({old_sim.cond_rel_id}->{old_cid})")
|
|
235
|
+
await old_sim.update_from_dict({"similarity": sim, "old_rel_id": old_cid}).save()
|
|
236
|
+
self._cond_sim_upd(cid, old_cid)
|
|
237
|
+
elif sim != old_sim.similarity:
|
|
238
|
+
logging.info(f"{cid}: {old_sim.similarity}->{sim}")
|
|
239
|
+
await old_sim.update_from_dict({"similarity": sim}).save()
|
|
240
|
+
else:
|
|
241
|
+
await models.CondSim.create(cond_id=cid, cond_rel_id=old_cid, similarity=sim)
|
|
242
|
+
self._cond_sim_upd(cid, old_cid)
|
|
243
|
+
|
|
244
|
+
def _cond_sim_upd(self, cid: int, old_cid: int):
|
|
245
|
+
if old_old_cid := self.cond_sims.get(cid): # если старый cid уже был в дереве:
|
|
246
|
+
self.rcond_sims[old_old_cid].remove(cid) # удаляем из обратного
|
|
247
|
+
self.cond_sims[cid] = old_cid # а в прямом он автоматом переопределится, даже если и был
|
|
248
|
+
self.rcond_sims[old_cid] |= {cid} # ну и в обратное добавим новый
|
|
249
|
+
|
|
250
|
+
def build_tree(self):
|
|
251
|
+
set(self.cond_sims.keys()) | set(self.cond_sims.values())
|
|
252
|
+
tree = defaultdict(dict)
|
|
253
|
+
# Группируем родителей по детям
|
|
254
|
+
for child, par in self.cond_sims.items():
|
|
255
|
+
tree[par] |= {child: {}} # todo: make from self.rcond_sim
|
|
256
|
+
|
|
257
|
+
# Строим дерево снизу вверх
|
|
258
|
+
def subtree(node):
|
|
259
|
+
if not node:
|
|
260
|
+
return node
|
|
261
|
+
for key in node:
|
|
262
|
+
subnode = tree.pop(key, {})
|
|
263
|
+
d = subtree(subnode)
|
|
264
|
+
node[key] |= d # actual tree rebuilding here!
|
|
265
|
+
return node # todo: refact?
|
|
266
|
+
|
|
267
|
+
# Находим корни / без родителей
|
|
268
|
+
roots = set(self.cond_sims.values()) - set(self.cond_sims.keys())
|
|
269
|
+
for root in roots:
|
|
270
|
+
_ = subtree(tree[root])
|
|
271
|
+
|
|
272
|
+
self.tree = tree
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def get_sim(s1, s2) -> int:
|
|
276
|
+
sim = int((SequenceMatcher(None, s1, s2).ratio() - 0.6) * 10_000)
|
|
277
|
+
return sim if sim > 0 else 0
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def clean(s) -> str:
|
|
281
|
+
clear = r"[^\w\s.,!?;:()\-]"
|
|
282
|
+
repeat = r"(.)\1{2,}"
|
|
283
|
+
s = re.sub(clear, "", s).lower()
|
|
284
|
+
s = re.sub(repeat, r"\1", s)
|
|
285
|
+
return s.replace("\n\n", "\n").replace(" ", " ").strip(" \n/.,!?-")
|
|
@@ -10,6 +10,7 @@ from xync_schema.xtype import BaseAd
|
|
|
10
10
|
|
|
11
11
|
from xync_client.Abc.Ex import BaseExClient
|
|
12
12
|
from xync_client.Abc.xtype import CredExOut, BaseOrderReq, BaseAdUpdate
|
|
13
|
+
from xync_client.Gmail import GmClient
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class BaseAgentClient(HttpClient):
|
|
@@ -28,6 +29,7 @@ class BaseAgentClient(HttpClient):
|
|
|
28
29
|
self.fbot = fbot
|
|
29
30
|
self.agent: Agent = agent
|
|
30
31
|
self.actor: Actor = agent.actor
|
|
32
|
+
self.gmail = GmClient(agent.actor.person.user)
|
|
31
33
|
super().__init__(self.actor.ex.host_p2p, headers, cookies)
|
|
32
34
|
self.ex_client: BaseExClient = self.actor.ex.client(fbot)
|
|
33
35
|
|
|
@@ -11,11 +11,12 @@ from xync_schema import models
|
|
|
11
11
|
from xync_schema.enums import FileType
|
|
12
12
|
from xync_schema.xtype import CurEx, CoinEx, BaseAd, BaseAdIn
|
|
13
13
|
|
|
14
|
+
from xync_client.Abc.AdLoader import AdLoader
|
|
14
15
|
from xync_client.Abc.xtype import PmEx, MapOfIdsList
|
|
15
16
|
from xync_client.pm_unifier import PmUnifier, PmUni
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
class BaseExClient(HttpClient):
|
|
19
|
+
class BaseExClient(HttpClient, AdLoader):
|
|
19
20
|
cur_map: dict[int, str] = {}
|
|
20
21
|
unifier_class: type = PmUnifier
|
|
21
22
|
logo_pre_url: str
|
|
@@ -72,6 +73,7 @@ class BaseExClient(HttpClient):
|
|
|
72
73
|
pm_exids: list[str | int] = None,
|
|
73
74
|
amount: int = None,
|
|
74
75
|
lim: int = None,
|
|
76
|
+
vm_filter: bool = False,
|
|
75
77
|
) -> list[BaseAd]: # {ad.id: ad}
|
|
76
78
|
...
|
|
77
79
|
|
|
@@ -250,14 +252,14 @@ class BaseExClient(HttpClient):
|
|
|
250
252
|
return True
|
|
251
253
|
|
|
252
254
|
# Сохранение чужого объявления (с Pm-ами) в бд
|
|
253
|
-
async def ad_pydin2db(self, ad_pydin: BaseAdIn) -> models.Ad:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
255
|
+
# async def ad_pydin2db(self, ad_pydin: BaseAdIn) -> models.Ad:
|
|
256
|
+
# dct = ad_pydin.model_dump()
|
|
257
|
+
# dct["exid"] = dct.pop("id")
|
|
258
|
+
# ad_in = models.Ad.validate(dct)
|
|
259
|
+
# ad_db, _ = await models.Ad.update_or_create(**ad_in.df_unq())
|
|
260
|
+
# await ad_db.credexs.add(*getattr(ad_pydin, "credexs_", []))
|
|
261
|
+
# await ad_db.pmexs.add(*getattr(ad_pydin, "pmexs_", []))
|
|
262
|
+
# return ad_db
|
|
261
263
|
|
|
262
264
|
async def file_upsert(self, url: str, ss: ClientSession = None) -> models.File:
|
|
263
265
|
if not (file := await models.File.get_or_none(name__startswith=url.split("?")[0])):
|
|
@@ -32,7 +32,7 @@ class PmAgentClient(HasAbotUid, metaclass=ABCMeta):
|
|
|
32
32
|
ubot: FileClient | UserClient = None
|
|
33
33
|
page: Page
|
|
34
34
|
pages: type(StrEnum) = Pages
|
|
35
|
-
last_active: datetime =
|
|
35
|
+
last_active: datetime = now()
|
|
36
36
|
with_userbot: bool = False
|
|
37
37
|
_is_started: bool = False
|
|
38
38
|
|
|
@@ -53,7 +53,9 @@ class BaseOrderReq(BaseModel):
|
|
|
53
53
|
asset_amount: float | None = None
|
|
54
54
|
fiat_amount: float | None = None
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
pm_id: int = (None,)
|
|
57
|
+
|
|
58
|
+
# todo: mv from base to special ex class
|
|
57
59
|
amount_is_fiat: bool = True
|
|
58
60
|
is_sell: bool = None
|
|
59
61
|
cur_exid: int | str = None
|
|
@@ -77,7 +77,7 @@ class InAgentClient(BaseInAgentClient):
|
|
|
77
77
|
while resp := await websocket.recv():
|
|
78
78
|
if data := json.loads(resp):
|
|
79
79
|
upd, order_db = None, None
|
|
80
|
-
logging.info(f" {
|
|
80
|
+
logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
|
|
81
81
|
match data.get("topic"):
|
|
82
82
|
case "OTC_ORDER_STATUS":
|
|
83
83
|
match data["type"]:
|
|
@@ -108,10 +108,11 @@ class InAgentClient(BaseInAgentClient):
|
|
|
108
108
|
if not (pmacdx := await self.get_pma_by_cdex(order)):
|
|
109
109
|
continue
|
|
110
110
|
pma, cdx = pmacdx
|
|
111
|
-
am, tid = pma.check_in(
|
|
111
|
+
am, tid = await pma.check_in(
|
|
112
112
|
Decimal(order.amount),
|
|
113
113
|
cdx.cred.pmcur.cur.ticker,
|
|
114
|
-
|
|
114
|
+
# todo: почему в московском час.поясе?
|
|
115
|
+
datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
115
116
|
)
|
|
116
117
|
if not tid:
|
|
117
118
|
logging.info(
|
|
@@ -142,9 +143,13 @@ class InAgentClient(BaseInAgentClient):
|
|
|
142
143
|
if (
|
|
143
144
|
o["amount"] == order.amount
|
|
144
145
|
and o["id"] != upd.id
|
|
145
|
-
and int(order.createDate)
|
|
146
|
-
|
|
147
|
-
#
|
|
146
|
+
and int(order.createDate)
|
|
147
|
+
< int(o["createDate"]) + 15 * 60 * 1000
|
|
148
|
+
# get full_order from o, and cred or pm from full_order:
|
|
149
|
+
and self.agent_client.api.get_order_details(orderId=o["id"])[
|
|
150
|
+
"result"
|
|
151
|
+
]["paymentTermList"][0]["accountNo"]
|
|
152
|
+
== order.paymentTermList[0].accountNo
|
|
148
153
|
)
|
|
149
154
|
]
|
|
150
155
|
curex = await models.CurEx.get(
|
|
@@ -155,7 +160,7 @@ class InAgentClient(BaseInAgentClient):
|
|
|
155
160
|
cred_id=order_db.cred_id,
|
|
156
161
|
amount=int(float(order.amount) * 10**curex.scale),
|
|
157
162
|
status__not_in=[OrderStatus.completed, OrderStatus.canceled],
|
|
158
|
-
created_at__gt=now() - timedelta(
|
|
163
|
+
created_at__gt=now() - timedelta(minutes=15),
|
|
159
164
|
)
|
|
160
165
|
if pos or pos_db:
|
|
161
166
|
await self.agent_client.ex_client.bot.send(
|
|
@@ -169,10 +174,10 @@ class InAgentClient(BaseInAgentClient):
|
|
|
169
174
|
# !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
|
|
170
175
|
self.agent_client.api.release_assets(orderId=upd.id)
|
|
171
176
|
logging.info(
|
|
172
|
-
f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {
|
|
177
|
+
f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
|
|
173
178
|
)
|
|
174
179
|
elif upd.side == 1: # я покупатель - ждем мою оплату
|
|
175
|
-
continue # logging.warning(f"Order {order.id} PAID at {
|
|
180
|
+
continue # logging.warning(f"Order {order.id} PAID at {now()}: {int_am}")
|
|
176
181
|
else:
|
|
177
182
|
...
|
|
178
183
|
# todo: check is always canceling
|
|
@@ -416,7 +421,7 @@ async def main():
|
|
|
416
421
|
actor__person__user__status=UserStatus.ACTIVE,
|
|
417
422
|
actor__person__user__pm_agents__isnull=False,
|
|
418
423
|
)
|
|
419
|
-
.prefetch_related("actor__ex", "
|
|
424
|
+
.prefetch_related("actor__ex", "actor__person__user__gmail")
|
|
420
425
|
.first()
|
|
421
426
|
)
|
|
422
427
|
pm_agents = await models.PmAgent.filter(
|
|
@@ -434,7 +439,7 @@ async def main():
|
|
|
434
439
|
# payeer_cl = Client(actor.person.user.username_id)
|
|
435
440
|
for pma in pm_agents:
|
|
436
441
|
pcl: PmAgentClient = pma.client(bbot)
|
|
437
|
-
cl.pmacs[pma.pm_id] = await pcl.start(await async_playwright().start(),
|
|
442
|
+
cl.pmacs[pma.pm_id] = await pcl.start(await async_playwright().start(), False)
|
|
438
443
|
try:
|
|
439
444
|
_ = await cl.start_listen()
|
|
440
445
|
except Exception as e:
|