xync-client 0.0.25.dev42__tar.gz → 0.0.25.dev43__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.
- {xync_client-0.0.25.dev42/xync_client.egg-info → xync_client-0.0.25.dev43}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/TestEx.py +9 -9
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/Ex.py +12 -171
- xync_client-0.0.25.dev43/xync_client/Abc/types.py +1 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Gate/ex.py +22 -21
- xync_client-0.0.25.dev43/xync_client/pm_unifier.py +161 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43/xync_client.egg-info}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client.egg-info/SOURCES.txt +2 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/.env.sample +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/.gitignore +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/README.md +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/makefile +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/pyproject.toml +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/setup.cfg +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/TestAgent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/TestAsset.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/TestOrder.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/Agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/AuthTrait.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/Base.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/InAgent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/pyd.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BingX/test/main.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/BitGet/req.mjs +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Bybit/agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/etype/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/etype/ad.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/etype/cred.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/etype/pm.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/KuCoin/pub.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/Okx/pyd.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/agent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/auth.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/ex.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/pyd.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/pyro.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/__init__.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/loader.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client/pyro.py +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client.egg-info/requires.txt +0 -0
- {xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
-
from xync_schema.types import
|
|
4
|
+
from xync_schema.types import BaseAd, CurEx, CoinEx, PmOut
|
|
5
5
|
|
|
6
6
|
from xync_client.Abc.BaseTest import BaseTest
|
|
7
7
|
from xync_schema.enums import ExStatus, ExType, ExAction
|
|
@@ -13,8 +13,8 @@ from xync_client.Abc.Ex import BaseExClient
|
|
|
13
13
|
|
|
14
14
|
@pytest.mark.asyncio(loop_scope="session")
|
|
15
15
|
class TestEx(BaseTest):
|
|
16
|
-
coins: dict[int, dict[str,
|
|
17
|
-
curs: dict[int, dict[str,
|
|
16
|
+
coins: dict[int, dict[str, CoinEx]] = {}
|
|
17
|
+
curs: dict[int, dict[str, CurEx]] = {}
|
|
18
18
|
|
|
19
19
|
@pytest.fixture
|
|
20
20
|
async def clients(self) -> list[BaseClient]:
|
|
@@ -27,8 +27,8 @@ class TestEx(BaseTest):
|
|
|
27
27
|
# 19
|
|
28
28
|
async def test_curs(self, clients: list[BaseExClient]):
|
|
29
29
|
for client in clients:
|
|
30
|
-
curs: dict[str,
|
|
31
|
-
ok = self.is_dict_of_objects(curs,
|
|
30
|
+
curs: dict[str, CurEx] = await client.curs()
|
|
31
|
+
ok = self.is_dict_of_objects(curs, CurEx)
|
|
32
32
|
t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.curs)
|
|
33
33
|
assert t.ok, "No curs"
|
|
34
34
|
self.curs[client.ex.id] = curs
|
|
@@ -37,8 +37,8 @@ class TestEx(BaseTest):
|
|
|
37
37
|
# 20
|
|
38
38
|
async def test_pms(self, clients: list[BaseExClient]):
|
|
39
39
|
for client in clients:
|
|
40
|
-
pms: dict[int | str,
|
|
41
|
-
ok = self.is_dict_of_objects(pms,
|
|
40
|
+
pms: dict[int | str, PmOut] = await client.pms()
|
|
41
|
+
ok = self.is_dict_of_objects(pms, PmOut)
|
|
42
42
|
t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.pms)
|
|
43
43
|
assert t.ok, "No pms"
|
|
44
44
|
logging.info(f"{client.ex.name}: {ExAction.pms.name} - ok")
|
|
@@ -55,8 +55,8 @@ class TestEx(BaseTest):
|
|
|
55
55
|
# 22
|
|
56
56
|
async def test_coins(self, clients: list[BaseExClient]):
|
|
57
57
|
for client in clients:
|
|
58
|
-
coins: dict[str,
|
|
59
|
-
ok = self.is_dict_of_objects(coins,
|
|
58
|
+
coins: dict[str, CoinEx] = await client.coins()
|
|
59
|
+
ok = self.is_dict_of_objects(coins, CoinEx)
|
|
60
60
|
t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.coins)
|
|
61
61
|
assert t.ok, "No coins"
|
|
62
62
|
self.coins[client.ex.id] = coins
|
|
@@ -1,173 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import re
|
|
3
2
|
from abc import abstractmethod
|
|
4
3
|
|
|
5
4
|
import msgspec
|
|
6
5
|
from msgspec import Struct
|
|
7
6
|
from tortoise.exceptions import MultipleObjectsReturned, IntegrityError
|
|
8
|
-
from x_model.types import Upd
|
|
9
|
-
from xync_schema import types
|
|
10
7
|
from xync_schema import models
|
|
8
|
+
from xync_schema.types import CurEx, PmOut, CoinEx, BaseAd, BaseAdIn
|
|
11
9
|
|
|
12
10
|
from xync_client.Abc.Base import BaseClient, MapOfIdsList
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class PmUni(Upd):
|
|
16
|
-
_unq: list[str] = ["norm", "country"]
|
|
17
|
-
|
|
18
|
-
norm: str
|
|
19
|
-
acronym: str = None
|
|
20
|
-
country: str = None
|
|
21
|
-
alias: str = None
|
|
22
|
-
extra: str = None
|
|
23
|
-
bank: bool = None
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class PmUnifier:
|
|
27
|
-
pms: dict[str, PmUni] = {} # {origin: normalized}
|
|
28
|
-
|
|
29
|
-
pm_map: dict[str, str] = {
|
|
30
|
-
"Юmoney": "YooMoney",
|
|
31
|
-
# "Local Bank (R-Green)": "Sberbank",
|
|
32
|
-
# "Local Bank (S-Green)": "Sberbank",
|
|
33
|
-
# "Local Card (Red)": "Alfa-Bank",
|
|
34
|
-
# "Local Card (Yellow)": "Tinkoff",
|
|
35
|
-
# "Local Card M-redTS": "MTS-bank",
|
|
36
|
-
# "Local Card-Green": "Sberbank",
|
|
37
|
-
# "Local Card-Yellow": "Tinkoff",
|
|
38
|
-
# "GTB Bank (Guarantee Trust Bank)": "GTBank",
|
|
39
|
-
}
|
|
40
|
-
re_bank = [
|
|
41
|
-
r"^bank (?!of )|bank$",
|
|
42
|
-
r" banka$",
|
|
43
|
-
r" bankas$",
|
|
44
|
-
r" bankası$",
|
|
45
|
-
r" banca$",
|
|
46
|
-
r"^banco(?! de | del )| banco$",
|
|
47
|
-
]
|
|
48
|
-
re_extra = [
|
|
49
|
-
r"\(card\)$|\bpay$|\bmoney$|\bwallet$|\bcash$",
|
|
50
|
-
r"b\.s\.c\.|k\.s\.c|s\.a$| sv$| gt$",
|
|
51
|
-
]
|
|
52
|
-
re_glut = [
|
|
53
|
-
r"\.io$|\.com$",
|
|
54
|
-
r"l'|d’|d'",
|
|
55
|
-
]
|
|
56
|
-
i18n_map = {
|
|
57
|
-
# " ": " ",
|
|
58
|
-
"nationale": "national",
|
|
59
|
-
"а": "a",
|
|
60
|
-
"á": "a",
|
|
61
|
-
"â": "a",
|
|
62
|
-
"о": "o",
|
|
63
|
-
"ó": "o",
|
|
64
|
-
"ō": "o",
|
|
65
|
-
"ú": "u",
|
|
66
|
-
"ü": "u",
|
|
67
|
-
"ų": "u",
|
|
68
|
-
"с": "c",
|
|
69
|
-
"č": "c",
|
|
70
|
-
"ç": "c",
|
|
71
|
-
"é": "e",
|
|
72
|
-
"è": "e",
|
|
73
|
-
"ş": "s",
|
|
74
|
-
"š": "s",
|
|
75
|
-
"ř": "r",
|
|
76
|
-
"í": "i",
|
|
77
|
-
}
|
|
78
|
-
rms = " -:`'’′"
|
|
79
|
-
|
|
80
|
-
def __init__(self, countries: list[str]):
|
|
81
|
-
self.cts = countries
|
|
82
|
-
|
|
83
|
-
def countries(self, name: str):
|
|
84
|
-
for ct in self.cts:
|
|
85
|
-
# Если имя кончается на "Название_страны" в скобках
|
|
86
|
-
if (
|
|
87
|
-
ct
|
|
88
|
-
and self.pms[name].norm.endswith((ct + ")", ct))
|
|
89
|
-
and not self.pms[name].norm.endswith((" of " + ct, " of the " + ct, " and " + ct, " de " + ct))
|
|
90
|
-
):
|
|
91
|
-
self.pms[name].norm = self.pms[name].norm.replace(ct, "")
|
|
92
|
-
self.pms[name].country = ct
|
|
93
|
-
self.clear(name)
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
def extra(self, name: str):
|
|
97
|
-
for r in self.re_extra:
|
|
98
|
-
if match := re.search(r, self.pms[name].norm):
|
|
99
|
-
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
100
|
-
self.pms[name].extra = match.group()
|
|
101
|
-
self.clear(name)
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
def alias(self, name: str):
|
|
105
|
-
if match := re.search(r"\(.+\)$", self.pms[name].norm):
|
|
106
|
-
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
107
|
-
self.pms[name].alias = match.group()[1:-1]
|
|
108
|
-
self.clear(name)
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
def bank(self, name: str):
|
|
112
|
-
for r in self.re_bank:
|
|
113
|
-
if match := re.search(r, self.pms[name].norm):
|
|
114
|
-
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
115
|
-
self.pms[name].bank = True
|
|
116
|
-
self.clear(name)
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
def acro(self, name: str):
|
|
120
|
-
acr = "".join(
|
|
121
|
-
wrd[0]
|
|
122
|
-
for wrd in self.pms[name].norm.split(" ")
|
|
123
|
-
if not wrd.isupper()
|
|
124
|
-
and not wrd.startswith(("(", "the"))
|
|
125
|
-
and len(wrd) > 2
|
|
126
|
-
or (len(wrd) > 1 and wrd.istitle())
|
|
127
|
-
).upper()
|
|
128
|
-
if len(acr) >= 2 and (
|
|
129
|
-
f"({acr})" in self.pms[name].norm
|
|
130
|
-
or self.pms[name].norm.startswith((acr + " ", acr + ":"))
|
|
131
|
-
or self.pms[name].norm.endswith(" " + acr)
|
|
132
|
-
):
|
|
133
|
-
self.pms[name].norm = self.pms[name].norm.replace(acr, "", 1).replace("()", "", 1).strip()
|
|
134
|
-
self.pms[name].acronym = acr
|
|
135
|
-
self.clear(name)
|
|
136
|
-
|
|
137
|
-
def slim(self, name: str):
|
|
138
|
-
for rm in self.re_glut:
|
|
139
|
-
self.pms[name].norm = re.sub(rm, "", self.pms[name].norm)
|
|
140
|
-
|
|
141
|
-
def i18n(self, name: str):
|
|
142
|
-
for src, trgt in self.i18n_map.items():
|
|
143
|
-
self.pms[name].norm = self.pms[name].norm.replace(src, trgt)
|
|
144
|
-
|
|
145
|
-
def clear(self, name: str):
|
|
146
|
-
self.pms[name].norm = self.pms[name].norm.replace("()", "").replace(" ", " ").strip(" -:")
|
|
147
|
-
|
|
148
|
-
def __call__(self, s: str) -> PmUni:
|
|
149
|
-
# если в словаре замен есть текущее назвние - меняем, иначе берем строку до запятой
|
|
150
|
-
self.pms[s] = PmUni(norm=self.pm_map.get(s, s.split(",")[0]))
|
|
151
|
-
# вырезаем мусорные добавки
|
|
152
|
-
self.slim(s)
|
|
153
|
-
# заменяем локальные символы на англ:
|
|
154
|
-
self.i18n(s)
|
|
155
|
-
# находим и вырезаем аббревиатуру, если есть
|
|
156
|
-
self.acro(s)
|
|
157
|
-
# уменьшаем все буквы
|
|
158
|
-
self.pms[s].norm = self.pms[s].norm.lower()
|
|
159
|
-
# находим и вырезаем страны, если есть
|
|
160
|
-
self.countries(s)
|
|
161
|
-
self.bank(s)
|
|
162
|
-
self.extra(s)
|
|
163
|
-
self.alias(s)
|
|
164
|
-
self.bank(s)
|
|
165
|
-
self.countries(s)
|
|
166
|
-
self.extra(s)
|
|
167
|
-
# вырезаем каждый символ rms
|
|
168
|
-
[self.pms[s].norm.replace(rm, "") for rm in self.rms]
|
|
169
|
-
|
|
170
|
-
return self.pms[s]
|
|
11
|
+
from xync_client.pm_unifier import PmUnifier, PmUni
|
|
171
12
|
|
|
172
13
|
|
|
173
14
|
class BaseExClient(BaseClient):
|
|
@@ -176,12 +17,12 @@ class BaseExClient(BaseClient):
|
|
|
176
17
|
|
|
177
18
|
# 19: Список поддерживаемых валют тейкера
|
|
178
19
|
@abstractmethod
|
|
179
|
-
async def curs(self) -> dict[str,
|
|
20
|
+
async def curs(self) -> dict[str, CurEx]: # {cur.ticker: cur}
|
|
180
21
|
...
|
|
181
22
|
|
|
182
23
|
# 20: Список платежных методов
|
|
183
24
|
@abstractmethod
|
|
184
|
-
async def pms(self, cur: models.Cur = None) -> dict[int | str,
|
|
25
|
+
async def pms(self, cur: models.Cur = None) -> dict[int | str, PmOut]: # {pm.exid: pm}
|
|
185
26
|
...
|
|
186
27
|
|
|
187
28
|
# 21: Список платежных методов по каждой валюте
|
|
@@ -191,7 +32,7 @@ class BaseExClient(BaseClient):
|
|
|
191
32
|
|
|
192
33
|
# 22: Список торгуемых монет (с ограничениям по валютам, если есть)
|
|
193
34
|
@abstractmethod
|
|
194
|
-
async def coins(self) -> dict[str,
|
|
35
|
+
async def coins(self) -> dict[str, CoinEx]: # {coin.ticker: coin}
|
|
195
36
|
...
|
|
196
37
|
|
|
197
38
|
# 23: Список пар валюта/монет
|
|
@@ -202,16 +43,16 @@ class BaseExClient(BaseClient):
|
|
|
202
43
|
@abstractmethod
|
|
203
44
|
async def ads(
|
|
204
45
|
self, coin_exid: str, cur_exid: str, is_sell: bool, pm_exids: list[str | int] = None, amount: int = None
|
|
205
|
-
) -> list[
|
|
46
|
+
) -> list[BaseAd]: # {ad.id: ad}
|
|
206
47
|
...
|
|
207
48
|
|
|
208
49
|
# 42: Чужая объява по id
|
|
209
50
|
@abstractmethod
|
|
210
|
-
async def ad(self, ad_id: int) ->
|
|
51
|
+
async def ad(self, ad_id: int) -> BaseAd: ...
|
|
211
52
|
|
|
212
53
|
# Преобразрование объекта объявления из формата биржи в формат xync
|
|
213
54
|
@abstractmethod
|
|
214
|
-
async def ad_epyd2pydin(self, ad:
|
|
55
|
+
async def ad_epyd2pydin(self, ad: BaseAd) -> BaseAdIn: ... # my_uid: for MyAd
|
|
215
56
|
|
|
216
57
|
# 99: Страны
|
|
217
58
|
async def countries(self) -> list[Struct]:
|
|
@@ -220,7 +61,7 @@ class BaseExClient(BaseClient):
|
|
|
220
61
|
# Импорт Pm-ов (с Pmcur-, Pmex- и Pmcurex-ами) и валют (с Curex-ами) с биржи в бд
|
|
221
62
|
async def set_pmcurexs(self):
|
|
222
63
|
# Curs
|
|
223
|
-
cur_pyds: dict[str,
|
|
64
|
+
cur_pyds: dict[str, CurEx] = await self.curs()
|
|
224
65
|
curs: dict[int | str, models.Cur] = {
|
|
225
66
|
cur_pyd.exid: (await models.Cur.update_or_create({"rate": cur_pyd.rate}, ticker=tkr))[0]
|
|
226
67
|
for tkr, cur_pyd in cur_pyds.items()
|
|
@@ -274,7 +115,7 @@ class BaseExClient(BaseClient):
|
|
|
274
115
|
# todo: curexcountry
|
|
275
116
|
|
|
276
117
|
# Pms
|
|
277
|
-
pms_epyds: dict[int | str,
|
|
118
|
+
pms_epyds: dict[int | str, PmOut] = {
|
|
278
119
|
k: v for k, v in sorted((await self.pms()).items(), key=lambda x: x[1].name)
|
|
279
120
|
} # sort by name
|
|
280
121
|
pms: dict[int | str, models.Pm] = dict({})
|
|
@@ -343,7 +184,7 @@ class BaseExClient(BaseClient):
|
|
|
343
184
|
|
|
344
185
|
# Импорт монет (с Coinex-ами) с биржи в бд
|
|
345
186
|
async def set_coinexs(self):
|
|
346
|
-
coins: dict[str,
|
|
187
|
+
coins: dict[str, CoinEx] = await self.coins()
|
|
347
188
|
coins_db: dict[int, models.Coin] = {
|
|
348
189
|
c.exid: (await models.Coin.update_or_create(ticker=c.ticker))[0] for c in coins.values()
|
|
349
190
|
}
|
|
@@ -353,7 +194,7 @@ class BaseExClient(BaseClient):
|
|
|
353
194
|
await models.Coinex.bulk_create(coinexs, update_fields=["minimum"], on_conflict=["coin_id", "ex_id"])
|
|
354
195
|
|
|
355
196
|
# Сохранение чужого объявления (с Pm-ами) в бд
|
|
356
|
-
async def ad_pydin2db(self, ad_pydin:
|
|
197
|
+
async def ad_pydin2db(self, ad_pydin: BaseAdIn) -> models.Ad:
|
|
357
198
|
df, unq = ad_pydin.args()
|
|
358
199
|
ad_db, _ = await models.Ad.update_or_create(df, **unq)
|
|
359
200
|
if getattr(ad_pydin, "pms_", None): # if it ListItem, not Full One # todo: remove?
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import subprocess
|
|
2
3
|
from asyncio import run, sleep
|
|
3
|
-
from json import
|
|
4
|
+
from json import loads
|
|
4
5
|
|
|
6
|
+
import requests
|
|
5
7
|
from bs4 import BeautifulSoup, Script
|
|
6
8
|
from x_model import init_db
|
|
7
9
|
from xync_schema import models
|
|
@@ -13,31 +15,30 @@ from xync_client.loader import PG_DSN
|
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class ExClient(BaseExClient):
|
|
16
|
-
#
|
|
17
|
-
async def
|
|
18
|
+
# Данные для р2р из html Gate.io
|
|
19
|
+
async def c2c_data(self) -> dict:
|
|
18
20
|
await sleep(1)
|
|
19
|
-
doc = await self._get("/p2p")
|
|
21
|
+
# doc = await self._get("/p2p") # todo: почему не работает? хз
|
|
22
|
+
doc = requests.get("https://www.gate.io/p2p").text
|
|
20
23
|
await sleep(1)
|
|
21
24
|
soup = BeautifulSoup(doc, "html.parser")
|
|
22
|
-
script: Script = soup.body.find_all("script")[
|
|
23
|
-
strng = (
|
|
24
|
-
|
|
25
|
-
.replace("\n", "")
|
|
26
|
-
.replace(" ", "")
|
|
27
|
-
.replace(
|
|
28
|
-
',\'bank\': {image: "/images/payment/bank.png",index: "2",pay_name: lang_string("银行卡"),pay_type: "bank",rgb: "#FF860D",}',
|
|
29
|
-
"",
|
|
30
|
-
)
|
|
31
|
-
)
|
|
32
|
-
# pattern = r'var c2cData = (\{.*?\})\s+var transLang'
|
|
33
|
-
# pattern = r'payment_settings:\s{1}(\{.*?\}),\s?// 用户放开的支付方式'
|
|
34
|
-
pattern = r"payment_settings:\s{1}(\{.*?\}),paymentIdMap:"
|
|
25
|
+
script: Script = soup.body.find_all("script")[19] # 17-th not stable
|
|
26
|
+
strng = script.get_text().replace(" ", "")
|
|
27
|
+
pattern = r"var c2cData = (\{.*?\})\s+var transLang"
|
|
35
28
|
match = re.search(pattern, strng.replace(",}", "}").replace(",]", "]"), re.DOTALL)
|
|
36
29
|
res = match.group(1)
|
|
37
|
-
|
|
30
|
+
p = subprocess.Popen(
|
|
31
|
+
["node", "-e", f"const lang_string = a => a;console.log(JSON.stringify({res}))"], stdout=subprocess.PIPE
|
|
32
|
+
)
|
|
33
|
+
out = p.stdout.read().decode()
|
|
34
|
+
return loads(out)
|
|
35
|
+
|
|
36
|
+
# 20: Список всех платежных методов на бирже
|
|
37
|
+
async def pms(self, cur: Cur = None) -> DictOfDicts:
|
|
38
|
+
data = await self.c2c_data()
|
|
38
39
|
return {
|
|
39
|
-
pm["index"]: {"name": pm["pay_name"], "logo": pm["image"], "identifier": idf, "type_": pm
|
|
40
|
-
for idf, pm in
|
|
40
|
+
pm["index"]: {"name": pm["pay_name"], "logo": pm["image"], "identifier": idf, "type_": pm.get("base_type")}
|
|
41
|
+
for idf, pm in data["payment_settings"].items()
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
# 21: Список поддерживаемых валют
|
|
@@ -62,7 +63,7 @@ async def main():
|
|
|
62
63
|
_ = await init_db(PG_DSN, models, True)
|
|
63
64
|
bg = await Ex.get(name="Gate")
|
|
64
65
|
cl = ExClient(bg)
|
|
65
|
-
await cl.curs()
|
|
66
|
+
# curs = await cl.curs()
|
|
66
67
|
# await cl.coins()
|
|
67
68
|
pms = await cl.pms()
|
|
68
69
|
print(pms)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from x_model.types import Upd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PmUni(Upd):
|
|
7
|
+
_unq: list[str] = ["norm", "country"]
|
|
8
|
+
|
|
9
|
+
norm: str
|
|
10
|
+
acronym: str = None
|
|
11
|
+
country: str = None
|
|
12
|
+
alias: str = None
|
|
13
|
+
extra: str = None
|
|
14
|
+
bank: bool = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PmUnifier:
|
|
18
|
+
pms: dict[str, PmUni] = {} # {origin: normalized}
|
|
19
|
+
|
|
20
|
+
pm_map: dict[str, str] = {
|
|
21
|
+
"Юmoney": "YooMoney",
|
|
22
|
+
# "Local Bank (R-Green)": "Sberbank",
|
|
23
|
+
# "Local Bank (S-Green)": "Sberbank",
|
|
24
|
+
# "Local Card (Red)": "Alfa-Bank",
|
|
25
|
+
# "Local Card (Yellow)": "Tinkoff",
|
|
26
|
+
# "Local Card M-redTS": "MTS-bank",
|
|
27
|
+
# "Local Card-Green": "Sberbank",
|
|
28
|
+
# "Local Card-Yellow": "Tinkoff",
|
|
29
|
+
# "GTB Bank (Guarantee Trust Bank)": "GTBank",
|
|
30
|
+
}
|
|
31
|
+
re_bank = [
|
|
32
|
+
r"^bank (?!of )|bank$",
|
|
33
|
+
r" banka$",
|
|
34
|
+
r" bankas$",
|
|
35
|
+
r" bankası$",
|
|
36
|
+
r" banca$",
|
|
37
|
+
r"^banco(?! de | del )| banco$",
|
|
38
|
+
]
|
|
39
|
+
re_extra = [
|
|
40
|
+
r"\(card\)$|\bpay$|\bmoney$|\bwallet$|\bcash$",
|
|
41
|
+
r"b\.s\.c\.|k\.s\.c|s\.a$| sv$| gt$",
|
|
42
|
+
]
|
|
43
|
+
re_glut = [
|
|
44
|
+
r"\.io$|\.com$",
|
|
45
|
+
r"l'|d’|d'",
|
|
46
|
+
]
|
|
47
|
+
i18n_map = {
|
|
48
|
+
# " ": " ",
|
|
49
|
+
"nationale": "national",
|
|
50
|
+
"а": "a",
|
|
51
|
+
"á": "a",
|
|
52
|
+
"â": "a",
|
|
53
|
+
"о": "o",
|
|
54
|
+
"ó": "o",
|
|
55
|
+
"ō": "o",
|
|
56
|
+
"ú": "u",
|
|
57
|
+
"ü": "u",
|
|
58
|
+
"ų": "u",
|
|
59
|
+
"с": "c",
|
|
60
|
+
"č": "c",
|
|
61
|
+
"ç": "c",
|
|
62
|
+
"é": "e",
|
|
63
|
+
"è": "e",
|
|
64
|
+
"ş": "s",
|
|
65
|
+
"š": "s",
|
|
66
|
+
"ř": "r",
|
|
67
|
+
"í": "i",
|
|
68
|
+
}
|
|
69
|
+
rms = " -:`'’′"
|
|
70
|
+
|
|
71
|
+
def __init__(self, countries: list[str]):
|
|
72
|
+
self.cts = countries
|
|
73
|
+
|
|
74
|
+
def countries(self, name: str):
|
|
75
|
+
for ct in self.cts:
|
|
76
|
+
# Если имя кончается на "Название_страны" в скобках
|
|
77
|
+
if (
|
|
78
|
+
ct
|
|
79
|
+
and self.pms[name].norm.endswith((ct + ")", ct))
|
|
80
|
+
and not self.pms[name].norm.endswith((" of " + ct, " of the " + ct, " and " + ct, " de " + ct))
|
|
81
|
+
):
|
|
82
|
+
self.pms[name].norm = self.pms[name].norm.replace(ct, "")
|
|
83
|
+
self.pms[name].country = ct
|
|
84
|
+
self.clear(name)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
def extra(self, name: str):
|
|
88
|
+
for r in self.re_extra:
|
|
89
|
+
if match := re.search(r, self.pms[name].norm):
|
|
90
|
+
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
91
|
+
self.pms[name].extra = match.group()
|
|
92
|
+
self.clear(name)
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
def alias(self, name: str):
|
|
96
|
+
if match := re.search(r"\(.+\)$", self.pms[name].norm):
|
|
97
|
+
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
98
|
+
self.pms[name].alias = match.group()[1:-1]
|
|
99
|
+
self.clear(name)
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
def bank(self, name: str):
|
|
103
|
+
for r in self.re_bank:
|
|
104
|
+
if match := re.search(r, self.pms[name].norm):
|
|
105
|
+
self.pms[name].norm = self.pms[name].norm.replace(match.group(), "")
|
|
106
|
+
self.pms[name].bank = True
|
|
107
|
+
self.clear(name)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
def acro(self, name: str):
|
|
111
|
+
acr = "".join(
|
|
112
|
+
wrd[0]
|
|
113
|
+
for wrd in self.pms[name].norm.split(" ")
|
|
114
|
+
if not wrd.isupper()
|
|
115
|
+
and not wrd.startswith(("(", "the"))
|
|
116
|
+
and len(wrd) > 2
|
|
117
|
+
or (len(wrd) > 1 and wrd.istitle())
|
|
118
|
+
).upper()
|
|
119
|
+
if len(acr) >= 2 and (
|
|
120
|
+
f"({acr})" in self.pms[name].norm
|
|
121
|
+
or self.pms[name].norm.startswith((acr + " ", acr + ":"))
|
|
122
|
+
or self.pms[name].norm.endswith(" " + acr)
|
|
123
|
+
):
|
|
124
|
+
self.pms[name].norm = self.pms[name].norm.replace(acr, "", 1).replace("()", "", 1).strip()
|
|
125
|
+
self.pms[name].acronym = acr
|
|
126
|
+
self.clear(name)
|
|
127
|
+
|
|
128
|
+
def slim(self, name: str):
|
|
129
|
+
for rm in self.re_glut:
|
|
130
|
+
self.pms[name].norm = re.sub(rm, "", self.pms[name].norm)
|
|
131
|
+
|
|
132
|
+
def i18n(self, name: str):
|
|
133
|
+
for src, trgt in self.i18n_map.items():
|
|
134
|
+
self.pms[name].norm = self.pms[name].norm.replace(src, trgt)
|
|
135
|
+
|
|
136
|
+
def clear(self, name: str):
|
|
137
|
+
self.pms[name].norm = self.pms[name].norm.replace("()", "").replace(" ", " ").strip(" -:")
|
|
138
|
+
|
|
139
|
+
def __call__(self, s: str) -> PmUni:
|
|
140
|
+
# если в словаре замен есть текущее назвние - меняем, иначе берем строку до запятой
|
|
141
|
+
self.pms[s] = PmUni(norm=self.pm_map.get(s, s.split(",")[0]))
|
|
142
|
+
# вырезаем мусорные добавки
|
|
143
|
+
self.slim(s)
|
|
144
|
+
# заменяем локальные символы на англ:
|
|
145
|
+
self.i18n(s)
|
|
146
|
+
# находим и вырезаем аббревиатуру, если есть
|
|
147
|
+
self.acro(s)
|
|
148
|
+
# уменьшаем все буквы
|
|
149
|
+
self.pms[s].norm = self.pms[s].norm.lower()
|
|
150
|
+
# находим и вырезаем страны, если есть
|
|
151
|
+
self.countries(s)
|
|
152
|
+
self.bank(s)
|
|
153
|
+
self.extra(s)
|
|
154
|
+
self.alias(s)
|
|
155
|
+
self.bank(s)
|
|
156
|
+
self.countries(s)
|
|
157
|
+
self.extra(s)
|
|
158
|
+
# вырезаем каждый символ rms
|
|
159
|
+
[self.pms[s].norm.replace(rm, "") for rm in self.rms]
|
|
160
|
+
|
|
161
|
+
return self.pms[s]
|
|
@@ -19,6 +19,7 @@ tests/_todo_refact/Wallet/test_agent.py
|
|
|
19
19
|
tests/_todo_refact/Wallet/test_ex.py
|
|
20
20
|
xync_client/__init__.py
|
|
21
21
|
xync_client/loader.py
|
|
22
|
+
xync_client/pm_unifier.py
|
|
22
23
|
xync_client/pyro.py
|
|
23
24
|
xync_client.egg-info/PKG-INFO
|
|
24
25
|
xync_client.egg-info/SOURCES.txt
|
|
@@ -33,6 +34,7 @@ xync_client/Abc/BaseTest.py
|
|
|
33
34
|
xync_client/Abc/Ex.py
|
|
34
35
|
xync_client/Abc/InAgent.py
|
|
35
36
|
xync_client/Abc/Order.py
|
|
37
|
+
xync_client/Abc/types.py
|
|
36
38
|
xync_client/Binance/__init__.py
|
|
37
39
|
xync_client/Binance/binance_async.py
|
|
38
40
|
xync_client/Binance/earn_api.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Binance/test_binance.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Bybit/test_bybit.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Bybit/test_bybit_p2p.py
RENAMED
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Htx/test_htx_p2p.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/tests/_todo_refact/Wallet/test_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev42 → xync_client-0.0.25.dev43}/xync_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|