xync-client 0.0.141__py3-none-any.whl → 0.0.156.dev18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xync_client/Abc/AdLoader.py +5 -0
- xync_client/Abc/Agent.py +354 -8
- xync_client/Abc/Ex.py +432 -25
- xync_client/Abc/HasAbotUid.py +10 -0
- xync_client/Abc/InAgent.py +0 -11
- xync_client/Abc/PmAgent.py +34 -26
- xync_client/Abc/xtype.py +57 -3
- xync_client/Bybit/InAgent.py +233 -409
- xync_client/Bybit/agent.py +844 -777
- xync_client/Bybit/etype/__init__.py +0 -0
- xync_client/Bybit/etype/ad.py +54 -86
- xync_client/Bybit/etype/cred.py +29 -9
- xync_client/Bybit/etype/order.py +75 -103
- xync_client/Bybit/ex.py +35 -48
- xync_client/Gmail/__init__.py +119 -98
- xync_client/Htx/agent.py +213 -40
- xync_client/Htx/etype/ad.py +40 -16
- xync_client/Htx/etype/order.py +194 -0
- xync_client/Htx/ex.py +17 -19
- xync_client/Mexc/agent.py +268 -0
- xync_client/Mexc/api.py +1255 -0
- xync_client/Mexc/etype/ad.py +52 -1
- xync_client/Mexc/etype/order.py +354 -0
- xync_client/Mexc/ex.py +34 -22
- xync_client/Okx/1.py +14 -0
- xync_client/Okx/agent.py +39 -0
- xync_client/Okx/ex.py +8 -8
- xync_client/Pms/Payeer/agent.py +396 -0
- xync_client/Pms/Payeer/login.py +1 -59
- xync_client/Pms/Payeer/trade.py +58 -0
- xync_client/Pms/Volet/__init__.py +82 -63
- xync_client/Pms/Volet/api.py +5 -4
- xync_client/loader.py +2 -0
- xync_client/pm_unifier.py +1 -1
- {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/METADATA +5 -1
- {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/RECORD +38 -29
- xync_client/Pms/Payeer/__init__.py +0 -253
- xync_client/Pms/Payeer/api.py +0 -25
- {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/WHEEL +0 -0
- {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/top_level.txt +0 -0
xync_client/Gmail/__init__.py
CHANGED
|
@@ -1,113 +1,134 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import pickle
|
|
3
|
+
import re
|
|
4
|
+
from base64 import urlsafe_b64decode
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from google.auth.transport.requests import Request
|
|
8
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
9
|
+
from googleapiclient.discovery import Resource, build
|
|
10
|
+
from requests import get
|
|
4
11
|
from xync_schema.models import User, Gmail
|
|
5
12
|
|
|
6
|
-
from xync_client.
|
|
13
|
+
from xync_client.Abc.HasAbotUid import HasAbotUid
|
|
14
|
+
from xync_client.loader import TORM
|
|
15
|
+
|
|
16
|
+
# Область доступа
|
|
17
|
+
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GmClient(HasAbotUid):
|
|
21
|
+
service: Resource
|
|
22
|
+
|
|
23
|
+
def __init__(self, user: User):
|
|
24
|
+
"""Авторизация и создание сервиса Gmail API"""
|
|
25
|
+
creds = None
|
|
26
|
+
# Файл token.pickle хранит токены доступа пользователя
|
|
27
|
+
if user.gmail.token:
|
|
28
|
+
creds = pickle.loads(user.gmail.token)
|
|
29
|
+
|
|
30
|
+
# Если нет валидных credentials, запрашиваем авторизацию
|
|
31
|
+
if not creds or not creds.valid:
|
|
32
|
+
if creds and creds.expired and creds.refresh_token:
|
|
33
|
+
creds.refresh(Request())
|
|
34
|
+
else:
|
|
35
|
+
flow = InstalledAppFlow.from_client_config(user.gmail.auth, SCOPES)
|
|
36
|
+
creds = flow.run_local_server(port=0)
|
|
7
37
|
|
|
38
|
+
# Сохраняем credentials для следующего запуска
|
|
39
|
+
user.gmail.token = pickle.dumps(creds)
|
|
8
40
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
user: User
|
|
12
|
-
page: Page
|
|
13
|
-
bot: UserClient = None
|
|
14
|
-
HOME = "https://mail.google.com/mail/u/0/"
|
|
41
|
+
self.service = build("gmail", "v1", credentials=creds)
|
|
42
|
+
self.uid = user.username_id
|
|
15
43
|
|
|
16
|
-
def
|
|
17
|
-
|
|
44
|
+
def _get_last_email(self, sender_email, subject_keyword=None):
|
|
45
|
+
"""
|
|
46
|
+
Получить последнее письмо от определенного отправителя
|
|
18
47
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
48
|
+
Args:
|
|
49
|
+
sender_email: email отправителя (например, 'example@gmail.com')
|
|
50
|
+
subject_keyword: ключевое слово в теме (опционально)
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def _get_email_body(payload):
|
|
54
|
+
"""Извлечь текст письма из payload"""
|
|
55
|
+
if "body" in payload and "data" in payload["body"]:
|
|
56
|
+
return urlsafe_b64decode(payload["body"]["data"]).decode("utf-8")
|
|
57
|
+
return ""
|
|
58
|
+
|
|
59
|
+
# Формируем поисковый запрос
|
|
60
|
+
query = f"from:{sender_email}"
|
|
61
|
+
if subject_keyword:
|
|
62
|
+
query += f" subject:{subject_keyword}"
|
|
63
|
+
|
|
64
|
+
# Ищем письма с этим запросом
|
|
65
|
+
results = (
|
|
66
|
+
self.service.users()
|
|
67
|
+
.messages()
|
|
68
|
+
.list(
|
|
69
|
+
userId="me",
|
|
70
|
+
q=query,
|
|
71
|
+
maxResults=1, # Только последнее письмо
|
|
24
72
|
)
|
|
25
|
-
|
|
26
|
-
channel="chrome",
|
|
27
|
-
headless=not headed,
|
|
28
|
-
args=[
|
|
29
|
-
"--disable-blink-features=AutomationControlled",
|
|
30
|
-
"--no-sandbox",
|
|
31
|
-
"--disable-web-security",
|
|
32
|
-
"--disable-infobars",
|
|
33
|
-
"--disable-extensions",
|
|
34
|
-
"--start-maximized",
|
|
35
|
-
],
|
|
73
|
+
.execute()
|
|
36
74
|
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
await
|
|
83
|
-
|
|
84
|
-
async def load_bot(self):
|
|
85
|
-
if not self.bot:
|
|
86
|
-
bot = FileClient(NET_TOKEN)
|
|
87
|
-
self.bot = UserClient(self.uid, bot)
|
|
88
|
-
if not self.bot.is_connected:
|
|
89
|
-
await self.bot.start()
|
|
90
|
-
|
|
91
|
-
async def stop(self):
|
|
92
|
-
if self.bot and self.bot.is_connected: # todo: do not stop if
|
|
93
|
-
await self.bot.stop(False)
|
|
94
|
-
await self.page.context.close()
|
|
95
|
-
await self.page.context.browser.close()
|
|
75
|
+
|
|
76
|
+
if not (messages := results.get("messages", [])):
|
|
77
|
+
logging.warning("Письма не найдены")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# Получаем полную информацию о письме
|
|
81
|
+
message_id = messages[0]["id"]
|
|
82
|
+
message = self.service.users().messages().get(userId="me", id=message_id, format="full").execute()
|
|
83
|
+
|
|
84
|
+
# Извлекаем заголовки
|
|
85
|
+
headers = message["payload"]["headers"]
|
|
86
|
+
subject = next((h["value"] for h in headers if h["name"] == "Subject"), "Нет темы")
|
|
87
|
+
from_email = next((h["value"] for h in headers if h["name"] == "From"), "Неизвестно")
|
|
88
|
+
date = next((h["value"] for h in headers if h["name"] == "Date"), "Неизвестно")
|
|
89
|
+
|
|
90
|
+
# Извлекаем текст письма
|
|
91
|
+
body = _get_email_body(message["payload"])
|
|
92
|
+
|
|
93
|
+
return {"id": message_id, "subject": subject, "from": from_email, "date": date, "body": body}
|
|
94
|
+
|
|
95
|
+
async def volet_confirm(self, amount: float, dt: datetime):
|
|
96
|
+
if email := self._get_last_email("noreply@volet.com", "Please Confirm Withdrawal"): # "Volet.com"
|
|
97
|
+
date = datetime.strptime(email["date"].split(",")[1].split(" +")[0], "%d %b %Y %H:%M:%S")
|
|
98
|
+
if match := re.search(r"Amount: <b>([\d.]+) [A-Z]{3}</b>", email["body"]):
|
|
99
|
+
amt = float(match.group(1))
|
|
100
|
+
if match := re.search(r"https://account\.volet\.com/verify/([a-f0-9-]+)", email["body"]):
|
|
101
|
+
token = match.group(1)
|
|
102
|
+
|
|
103
|
+
if email and amount == amt and date > dt and token:
|
|
104
|
+
get(f"https://account.volet.com/verify/{token}")
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
await self.receive("А нет запросов от волета")
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
async def bybit_code(self, dt: datetime) -> str | None:
|
|
111
|
+
if email := self._get_last_email("Bybit", "[Bybit]Security Code for Your Bybit Account"):
|
|
112
|
+
date = datetime.strptime(email["date"].split(",")[1].split(" +")[0], "%d %b %Y %H:%M:%S")
|
|
113
|
+
if match := re.search(r'<span style="font-size:28pt;color:#ff9c2e">(\d{6})</span>', email["body"]):
|
|
114
|
+
code = match.group(1)
|
|
115
|
+
|
|
116
|
+
if email and date > dt and code:
|
|
117
|
+
get(f"https://account.volet.com/verify/{code}")
|
|
118
|
+
return code
|
|
119
|
+
|
|
120
|
+
await self.receive("А нет запросов от волета")
|
|
121
|
+
return None
|
|
96
122
|
|
|
97
123
|
|
|
98
124
|
async def _test():
|
|
99
125
|
from x_model import init_db
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
gmc = GmClient(
|
|
105
|
-
|
|
106
|
-
await gmc.start(True)
|
|
107
|
-
except TimeoutError as te:
|
|
108
|
-
raise te
|
|
109
|
-
finally:
|
|
110
|
-
await gmc.stop()
|
|
126
|
+
|
|
127
|
+
_ = await init_db(TORM)
|
|
128
|
+
|
|
129
|
+
gm = await Gmail.get(id=1).prefetch_related("user__username")
|
|
130
|
+
gmc = GmClient(gm)
|
|
131
|
+
await gmc.volet_confirm(amount=90, dt=datetime.now())
|
|
111
132
|
|
|
112
133
|
|
|
113
134
|
if __name__ == "__main__":
|
xync_client/Htx/agent.py
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from json import dumps
|
|
3
|
+
from time import time
|
|
4
|
+
from urllib.parse import quote
|
|
2
5
|
|
|
6
|
+
from aiohttp import ClientResponse
|
|
7
|
+
from pyro_client.client.file import FileClient
|
|
8
|
+
from x_client import df_hdrs
|
|
3
9
|
from x_client.aiohttp import Client
|
|
10
|
+
from xync_bot import XyncBot
|
|
11
|
+
|
|
12
|
+
from xync_client.Abc.xtype import AdUpd, GetAds
|
|
13
|
+
from xync_client.Htx.etype.order import OrderItem, OrderFull
|
|
14
|
+
from xync_client.loader import NET_TOKEN, PAY_TOKEN, TORM
|
|
15
|
+
|
|
4
16
|
from xync_schema.enums import AdStatus, PmType, OrderStatus
|
|
5
|
-
from xync_schema.models import Pm, Coin, Cur, Ad, Order
|
|
6
17
|
from xync_schema import models
|
|
7
18
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
8
|
-
from xync_client.Htx.etype import test
|
|
19
|
+
from xync_client.Htx.etype import test, ad
|
|
9
20
|
|
|
10
21
|
import logging
|
|
11
22
|
|
|
12
23
|
url_ads_req = "https://otc-cf.huobi.com/v1/data/trade-market"
|
|
13
24
|
url_ads_web = "https://www.huobi.com/en-us/fiat-crypto/trade/"
|
|
14
25
|
url_my_ads = "https://otc-api.trygofast.com/v1/data/trade-list?pageSize=50"
|
|
15
|
-
url_my_ad = "
|
|
26
|
+
url_my_ad = "/-/x/otc/v1/otc/trade/" # + id
|
|
16
27
|
url_my_bals = "https://www.huobi.com/-/x/otc/v1/capital/balance"
|
|
17
28
|
url_paccs = "https://www.huobi.com/-/x/otc/v1/user/receipt-account"
|
|
18
29
|
|
|
@@ -22,9 +33,19 @@ class Public(Client):
|
|
|
22
33
|
|
|
23
34
|
|
|
24
35
|
class AgentClient(BaseAgentClient):
|
|
25
|
-
headers = {
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
headers = {"portal": "web"} | df_hdrs
|
|
37
|
+
|
|
38
|
+
async def login(self):
|
|
39
|
+
t = int(time() * 1000)
|
|
40
|
+
resp = await self._get("/-/x/uc/uc/open/ticket/get", {"t": t})
|
|
41
|
+
if resp.get("data") is None:
|
|
42
|
+
...
|
|
43
|
+
if ticket := resp["data"].get("ticket"):
|
|
44
|
+
resp = await self._post("/-/x/otc/v1/user/login", form_data={"ticket": ticket, "type": "WEB"})
|
|
45
|
+
if resp["success"]:
|
|
46
|
+
self.agent.auth["headers"]["Token"] = resp["data"]
|
|
47
|
+
await self.agent.save(update_fields=["auth"])
|
|
48
|
+
self.session.headers["Token"] = resp["data"]
|
|
28
49
|
|
|
29
50
|
async def creds(self) -> list[test.CredEpyd]:
|
|
30
51
|
resp = await self._get("/-/x/otc/v1/user/receipt-account")
|
|
@@ -46,7 +67,7 @@ class AgentClient(BaseAgentClient):
|
|
|
46
67
|
|
|
47
68
|
async def cred_new(self, cred: models.Cred) -> models.CredEx:
|
|
48
69
|
pmcur = await cred.pmcur
|
|
49
|
-
exid = str(await models.PmEx.get(pm_id=pmcur.
|
|
70
|
+
exid = str(await models.PmEx.get(pm_id=pmcur.pmex_exid, ex=self.ex_client.ex).values_list("exid", flat=True))
|
|
50
71
|
field_map = {
|
|
51
72
|
"payee": "name",
|
|
52
73
|
"bank": "extra",
|
|
@@ -71,7 +92,7 @@ class AgentClient(BaseAgentClient):
|
|
|
71
92
|
|
|
72
93
|
async def cred_upd(self, cred: models.Cred, exid: int) -> models.CredEx:
|
|
73
94
|
pmcur = await cred.pmcur
|
|
74
|
-
_exid = str(await models.PmEx.get(pm_id=pmcur.
|
|
95
|
+
_exid = str(await models.PmEx.get(pm_id=pmcur.pmex_exid, ex=self.ex_client.ex).values_list("exid", flat=True))
|
|
75
96
|
field_map = {
|
|
76
97
|
"payee": "name",
|
|
77
98
|
"bank": "extra",
|
|
@@ -95,14 +116,40 @@ class AgentClient(BaseAgentClient):
|
|
|
95
116
|
|
|
96
117
|
# 0
|
|
97
118
|
async def get_orders(
|
|
98
|
-
self,
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
self,
|
|
120
|
+
stauts: OrderStatus = OrderStatus.created,
|
|
121
|
+
coin: models.Coin = None,
|
|
122
|
+
cur: models.Cur = None,
|
|
123
|
+
is_sell: bool = None,
|
|
124
|
+
) -> list[OrderItem]:
|
|
125
|
+
resp = await self._get("/-/x/otc/v1/trade/order/process", {"needTradeCount": "true"})
|
|
126
|
+
if resp["success"]:
|
|
127
|
+
return [OrderItem(**o) for o in resp["data"]]
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
async def get_order(self, oid: int) -> OrderFull | None:
|
|
131
|
+
resp = await self._get("/-/x/otc/v1/trade/order", {"orderId": oid})
|
|
132
|
+
if resp["success"]:
|
|
133
|
+
o = OrderFull(**resp["data"])
|
|
134
|
+
return o
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
async def recv(self, order: OrderItem):
|
|
138
|
+
if order.orderStatus:
|
|
139
|
+
...
|
|
140
|
+
else:
|
|
141
|
+
...
|
|
142
|
+
|
|
143
|
+
async def start_listen(self):
|
|
144
|
+
"""Фоновая задача для ловли входящих ордеров"""
|
|
145
|
+
while True:
|
|
146
|
+
[await self.recv(o) for o in await self.get_orders()]
|
|
147
|
+
await sleep(9)
|
|
101
148
|
|
|
102
149
|
async def order_request(self, ad_id: int, amount: float) -> dict:
|
|
103
150
|
pass
|
|
104
151
|
|
|
105
|
-
async def my_fiats(self, cur: Cur = None) -> list[dict]:
|
|
152
|
+
async def my_fiats(self, cur: models.Cur = None) -> list[dict]:
|
|
106
153
|
pass
|
|
107
154
|
|
|
108
155
|
# async def fiat_new(self, fiat: FiatNew) -> Fiat.pyd():
|
|
@@ -124,30 +171,94 @@ class AgentClient(BaseAgentClient):
|
|
|
124
171
|
|
|
125
172
|
async def ad_new(
|
|
126
173
|
self,
|
|
127
|
-
coin: Coin,
|
|
128
|
-
cur: Cur,
|
|
174
|
+
coin: models.Coin,
|
|
175
|
+
cur: models.Cur,
|
|
129
176
|
is_sell: bool,
|
|
130
|
-
pms: list[Pm],
|
|
177
|
+
pms: list[models.Pm],
|
|
131
178
|
price: float,
|
|
132
179
|
is_float: bool = True,
|
|
133
180
|
min_fiat: int = None,
|
|
134
181
|
details: str = None,
|
|
135
182
|
autoreply: str = None,
|
|
136
183
|
status: AdStatus = AdStatus.active,
|
|
137
|
-
) -> Ad:
|
|
184
|
+
) -> models.Ad:
|
|
138
185
|
pass
|
|
139
186
|
|
|
140
|
-
async def
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
187
|
+
async def x2e_req_ad_upd(self, xreq: AdUpd) -> ad.AdsUpd:
|
|
188
|
+
creds = [
|
|
189
|
+
ad.TradeRule(
|
|
190
|
+
content="Payment method-%s",
|
|
191
|
+
contentCode="PAY",
|
|
192
|
+
hint="Please enter",
|
|
193
|
+
inputType=0,
|
|
194
|
+
inputValue=cx.cred.detail,
|
|
195
|
+
sort=1,
|
|
196
|
+
title="【Payment related】",
|
|
197
|
+
titleValue=(await models.PmEx.get(pm_id=cx.cred.pmcur.pm_id, ex=self.ex_client.ex)).name,
|
|
198
|
+
)
|
|
199
|
+
for cx in xreq.credexs
|
|
200
|
+
]
|
|
201
|
+
trade_rules = ad.TradeRulesV2(
|
|
202
|
+
[
|
|
203
|
+
*creds,
|
|
204
|
+
ad.TradeRule(
|
|
205
|
+
content="",
|
|
206
|
+
contentCode="MERCHANT",
|
|
207
|
+
hint="Please enter",
|
|
208
|
+
inputType=0,
|
|
209
|
+
inputValue="",
|
|
210
|
+
sort=4,
|
|
211
|
+
title="【Merchant Tips】",
|
|
212
|
+
),
|
|
213
|
+
]
|
|
214
|
+
).model_dump_json(exclude_none=True)
|
|
215
|
+
coin_id, coin_scale = await self.ex_client.x2e_coin(xreq.coin_id)
|
|
216
|
+
cur_id, cur_scale, minimum = await self.ex_client.x2e_cur(xreq.cur_id)
|
|
217
|
+
return ad.AdsUpd(
|
|
218
|
+
id=xreq.id,
|
|
219
|
+
tradeType=int(xreq.is_sell),
|
|
220
|
+
coinId=int(coin_id),
|
|
221
|
+
currency=int(cur_id),
|
|
222
|
+
minTradeLimit=minimum,
|
|
223
|
+
maxTradeLimit=round(xreq.amount - 10**-cur_scale, cur_scale),
|
|
224
|
+
tradeCount=round(xreq.quantity or xreq.amount / xreq.price, coin_scale),
|
|
225
|
+
password=self.agent.auth["pass"],
|
|
226
|
+
payTerm=15,
|
|
227
|
+
premium=0.00,
|
|
228
|
+
isFixed="on",
|
|
229
|
+
fixedPrice=round(xreq.price, cur_scale),
|
|
230
|
+
isAutoReply="off",
|
|
231
|
+
takerAcceptOrder=0,
|
|
232
|
+
isPayCode="off",
|
|
233
|
+
receiveAccounts=",".join([str(cx.exid) for cx in xreq.credexs]),
|
|
234
|
+
deviation=0,
|
|
235
|
+
isTakerLimit="on",
|
|
236
|
+
takerIsMerchant="on",
|
|
237
|
+
takerRealLevel="off",
|
|
238
|
+
takerIsPhoneBind="off",
|
|
239
|
+
takerIsPayment="on",
|
|
240
|
+
blockType=1,
|
|
241
|
+
session=1,
|
|
242
|
+
chargeType=False,
|
|
243
|
+
apiVersion=4,
|
|
244
|
+
channel="web",
|
|
245
|
+
tradeRulesV2=quote(trade_rules),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
async def _ad_upd(self, req: ad.AdsUpd, hdrs: dict[str, str] = None) -> dict:
|
|
249
|
+
res = await self._post(self.url_my_ad + str(req.id), form_data=req.model_dump(exclude_none=True), hdrs=hdrs)
|
|
250
|
+
if res["code"] == 200:
|
|
251
|
+
return res["data"]
|
|
252
|
+
elif res["code"] == 605:
|
|
253
|
+
hdrs = {"x-dialog-trace-id": res["extend"]["traceId"]}
|
|
254
|
+
return await self._ad_upd(req, hdrs)
|
|
255
|
+
elif res["code"] == 1010:
|
|
256
|
+
if (match := re.search(r"Available amount ([\d.]+)", res["message"])) and (qty := match.group(1)):
|
|
257
|
+
req.tradeCount = float(qty)
|
|
258
|
+
return await self._ad_upd(req, hdrs)
|
|
259
|
+
elif res["code"] == 401:
|
|
260
|
+
raise Exception(res)
|
|
261
|
+
raise BaseException(res)
|
|
151
262
|
|
|
152
263
|
async def ad_del(self) -> bool:
|
|
153
264
|
pass
|
|
@@ -182,32 +293,94 @@ class AgentClient(BaseAgentClient):
|
|
|
182
293
|
base_url = ""
|
|
183
294
|
middle_url = ""
|
|
184
295
|
|
|
185
|
-
htok: str = "Ev5lFfAvxDU2MA9BJ-Mc4U6zZG3Wb6qsp3Tx2fz6GIoY-uOP2m0-gvjE57ad1qDF"
|
|
186
|
-
|
|
187
296
|
url_ads_req = "https://otc-cf.huobi.com/v1/data/trade-market"
|
|
188
297
|
url_my_ads = "https://otc-api.trygofast.com/v1/data/trade-list?pageSize=50"
|
|
189
|
-
url_my_ad = "
|
|
298
|
+
url_my_ad = "/-/x/otc/v1/otc/trade/" # + id
|
|
190
299
|
url_my_bals = "https://www.huobi.com/-/x/otc/v1/capital/balance"
|
|
191
300
|
url_paccs = "https://www.huobi.com/-/x/otc/v1/user/receipt-account"
|
|
192
301
|
|
|
302
|
+
async def _proc(self, resp: ClientResponse, bp: dict | str = None) -> dict | str:
|
|
303
|
+
if (await resp.json()).get("code") == 401:
|
|
304
|
+
await self.login()
|
|
305
|
+
return await self.METHS[resp.method](self, resp.url.path, bp)
|
|
306
|
+
return await super()._proc(resp, bp)
|
|
307
|
+
|
|
193
308
|
|
|
194
309
|
async def _test():
|
|
195
310
|
from x_model import init_db
|
|
196
|
-
from xync_schema import TORM
|
|
197
311
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
312
|
+
cn = await init_db(TORM, True)
|
|
313
|
+
ex = await models.Ex[9]
|
|
314
|
+
filebot = FileClient(NET_TOKEN)
|
|
315
|
+
ecl = ex.client(filebot)
|
|
316
|
+
agent = (
|
|
317
|
+
await models.Agent.filter(actor__ex=ex, auth__isnull=False)
|
|
318
|
+
.prefetch_related(
|
|
319
|
+
"actor__ex",
|
|
320
|
+
"actor__person__user__gmail",
|
|
321
|
+
"actor__my_ads__my_ad__race",
|
|
322
|
+
"actor__my_ads__pair_side__pair__cur",
|
|
323
|
+
"actor__my_ads__pms",
|
|
324
|
+
)
|
|
325
|
+
.first()
|
|
201
326
|
)
|
|
202
|
-
cl: AgentClient =
|
|
203
|
-
cred = await models.Cred[89]
|
|
204
|
-
_ = await cl.cred_new(cred)
|
|
205
|
-
_creds = await cl.creds()
|
|
327
|
+
cl: AgentClient = agent.client(ecl, filebot, XyncBot(PAY_TOKEN, cn))
|
|
328
|
+
# cred = await models.Cred[89]
|
|
329
|
+
# _ = await cl.cred_new(cred)
|
|
330
|
+
# _creds = await cl.creds()
|
|
206
331
|
# _ = await cl.cred_del(16984748)
|
|
207
|
-
|
|
332
|
+
|
|
333
|
+
while True:
|
|
334
|
+
breq = GetAds(coin_id=1, cur_id=1, is_sell=False, pm_ids=[366])
|
|
335
|
+
sreq = GetAds(coin_id=1, cur_id=1, is_sell=True, pm_ids=[366])
|
|
336
|
+
breq_upd = AdUpd(id=1185713, price=87.01, **{**breq.model_dump(), "amount": 100000.01})
|
|
337
|
+
sreq_upd = AdUpd(id=1188929, price=98.99, **{**sreq.model_dump(), "amount": 200000.01})
|
|
338
|
+
|
|
339
|
+
bads: list[ad.Resp] = await cl.ex_client.ads(breq)
|
|
340
|
+
sads: list[ad.Resp] = await cl.ex_client.ads(sreq)
|
|
341
|
+
bceil = 101.11
|
|
342
|
+
sceil = 151
|
|
343
|
+
bads = [a for a in bads if a.price < bceil and a.tradeCount > 10 and (a.maxTradeLimit - a.minTradeLimit > 800)]
|
|
344
|
+
sads = [a for a in sads if a.price > sceil and a.tradeCount > 10 and (a.maxTradeLimit - a.minTradeLimit > 800)]
|
|
345
|
+
|
|
346
|
+
if len(bads) > 1:
|
|
347
|
+
if bads[0].uid == cl.actor.exid:
|
|
348
|
+
if round(bads[0].price - bads[1].price, 2) > 0.01:
|
|
349
|
+
breq_upd.price = bads[1].price + 0.01
|
|
350
|
+
await cl.ad_upd(breq_upd)
|
|
351
|
+
print(end="!", flush=True)
|
|
352
|
+
elif bads[0].price < bceil:
|
|
353
|
+
breq_upd.price = bads[0].price + 0.01
|
|
354
|
+
await cl.ad_upd(breq_upd)
|
|
355
|
+
print(end="!", flush=True)
|
|
356
|
+
|
|
357
|
+
if len(sads) > 1:
|
|
358
|
+
if sads[0].uid == cl.actor.exid:
|
|
359
|
+
if round(sads[1].price - sads[0].price, 2) > 0.01:
|
|
360
|
+
sreq_upd.price = sads[1].price - 0.01
|
|
361
|
+
await cl.ad_upd(sreq_upd)
|
|
362
|
+
print(end="!", flush=True)
|
|
363
|
+
elif sads[0].price > sceil:
|
|
364
|
+
sreq_upd.price = sads[0].price - 0.01
|
|
365
|
+
await cl.ad_upd(sreq_upd)
|
|
366
|
+
print(end="!", flush=True)
|
|
367
|
+
|
|
368
|
+
if (pos := await cl.get_orders()) and (po := pos.pop(0)):
|
|
369
|
+
if po.side: # is_sell
|
|
370
|
+
po.amount # check
|
|
371
|
+
else: # buy
|
|
372
|
+
order: OrderFull = await cl.get_order(po.orderId)
|
|
373
|
+
if ps := [pm.bankNumber for pm in order.paymentMethod if pm.bankType in [24]]:
|
|
374
|
+
if match := re.search(r"^[PpРр]\d{7,10}\b", ps[0]):
|
|
375
|
+
match.group()
|
|
376
|
+
|
|
377
|
+
print(end=".", flush=True)
|
|
378
|
+
await sleep(9)
|
|
379
|
+
|
|
380
|
+
await cl.stop()
|
|
208
381
|
|
|
209
382
|
|
|
210
383
|
if __name__ == "__main__":
|
|
211
|
-
from asyncio import run
|
|
384
|
+
from asyncio import run, sleep
|
|
212
385
|
|
|
213
386
|
run(_test())
|