xync-client 0.0.141__py3-none-any.whl → 0.0.155__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/Htx/agent.py CHANGED
@@ -1,18 +1,30 @@
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.loader import NET_TOKEN, PAY_TOKEN, TORM
14
+
15
+ from xync_client.Htx.etype.ad import Req, TradeRule, TradeRulesV2
4
16
  from xync_schema.enums import AdStatus, PmType, OrderStatus
5
17
  from xync_schema.models import Pm, Coin, Cur, Ad, Order
6
18
  from xync_schema import models
7
19
  from xync_client.Abc.Agent import BaseAgentClient
8
- from xync_client.Htx.etype import test
20
+ from xync_client.Htx.etype import test, ad
9
21
 
10
22
  import logging
11
23
 
12
24
  url_ads_req = "https://otc-cf.huobi.com/v1/data/trade-market"
13
25
  url_ads_web = "https://www.huobi.com/en-us/fiat-crypto/trade/"
14
26
  url_my_ads = "https://otc-api.trygofast.com/v1/data/trade-list?pageSize=50"
15
- url_my_ad = "https://www.huobi.com/-/x/otc/v1/otc/trade/" # + id
27
+ url_my_ad = "/-/x/otc/v1/otc/trade/" # + id
16
28
  url_my_bals = "https://www.huobi.com/-/x/otc/v1/capital/balance"
17
29
  url_paccs = "https://www.huobi.com/-/x/otc/v1/user/receipt-account"
18
30
 
@@ -22,9 +34,17 @@ class Public(Client):
22
34
 
23
35
 
24
36
  class AgentClient(BaseAgentClient):
25
- headers = {
26
- "portal": "web",
27
- }
37
+ headers = {"portal": "web"} | df_hdrs
38
+
39
+ async def login(self):
40
+ t = int(time() * 1000)
41
+ resp = await self._get("/-/x/uc/uc/open/ticket/get", {"t": t})
42
+ if ticket := resp["data"].get("ticket"):
43
+ resp = await self._post("/-/x/otc/v1/user/login", form_data={"ticket": ticket, "type": "WEB"})
44
+ if resp["success"]:
45
+ self.agent.auth["headers"]["Token"] = resp["data"]
46
+ await self.agent.save(update_fields=["auth"])
47
+ self.session.headers["Token"] = resp["data"]
28
48
 
29
49
  async def creds(self) -> list[test.CredEpyd]:
30
50
  resp = await self._get("/-/x/otc/v1/user/receipt-account")
@@ -46,7 +66,7 @@ class AgentClient(BaseAgentClient):
46
66
 
47
67
  async def cred_new(self, cred: models.Cred) -> models.CredEx:
48
68
  pmcur = await cred.pmcur
49
- exid = str(await models.PmEx.get(pm_id=pmcur.pm_id, ex=self.ex_client.ex).values_list("exid", flat=True))
69
+ exid = str(await models.PmEx.get(pm_id=pmcur.pmex_exid, ex=self.ex_client.ex).values_list("exid", flat=True))
50
70
  field_map = {
51
71
  "payee": "name",
52
72
  "bank": "extra",
@@ -71,7 +91,7 @@ class AgentClient(BaseAgentClient):
71
91
 
72
92
  async def cred_upd(self, cred: models.Cred, exid: int) -> models.CredEx:
73
93
  pmcur = await cred.pmcur
74
- _exid = str(await models.PmEx.get(pm_id=pmcur.pm_id, ex=self.ex_client.ex).values_list("exid", flat=True))
94
+ _exid = str(await models.PmEx.get(pm_id=pmcur.pmex_exid, ex=self.ex_client.ex).values_list("exid", flat=True))
75
95
  field_map = {
76
96
  "payee": "name",
77
97
  "bank": "extra",
@@ -137,17 +157,79 @@ class AgentClient(BaseAgentClient):
137
157
  ) -> Ad:
138
158
  pass
139
159
 
140
- async def ad_upd(
141
- self,
142
- pms: [Pm] = None,
143
- price: float = None,
144
- is_float: bool = None,
145
- min_fiat: int = None,
146
- details: str = None,
147
- autoreply: str = None,
148
- status: AdStatus = None,
149
- ) -> bool:
150
- pass
160
+ async def _ad_upd(self, ad_upd: AdUpd, hdrs: dict[str, str] = None) -> dict:
161
+ creds = [
162
+ TradeRule(
163
+ content="Payment method-%s",
164
+ contentCode="PAY",
165
+ hint="Please enter",
166
+ inputType=0,
167
+ inputValue=cx.cred.detail,
168
+ sort=1,
169
+ title="【Payment related】",
170
+ titleValue=await models.PmEx.get(pm_id=cx.cred.pmcur.pm_id, ex=self.ex_client.ex).values_list(
171
+ "name", flat=True
172
+ ),
173
+ )
174
+ for cx in ad_upd.credexs
175
+ ]
176
+ trs = TradeRulesV2(
177
+ [
178
+ *creds,
179
+ TradeRule(
180
+ content="",
181
+ contentCode="MERCHANT",
182
+ hint="Please enter",
183
+ inputType=0,
184
+ inputValue="",
185
+ sort=4,
186
+ title="【Merchant Tips】",
187
+ ),
188
+ ]
189
+ ).model_dump_json(exclude_none=True)
190
+ req = Req(
191
+ tradeType=int(ad_upd.is_sell),
192
+ coinId=2,
193
+ currency=11,
194
+ minTradeLimit=500.00,
195
+ maxTradeLimit=ad_upd.amount,
196
+ tradeCount=ad_upd.quantity,
197
+ password=self.agent.auth["pass"],
198
+ payTerm=15,
199
+ premium=0.00,
200
+ isFixed="on",
201
+ fixedPrice=ad_upd.price,
202
+ isAutoReply="off",
203
+ takerAcceptOrder=0,
204
+ isPayCode="off",
205
+ receiveAccounts=ad_upd.credexs[0].exid,
206
+ deviation=0,
207
+ isTakerLimit="on",
208
+ takerIsMerchant="on",
209
+ takerRealLevel="off",
210
+ takerIsPhoneBind="off",
211
+ takerIsPayment="on",
212
+ blockType=1,
213
+ session=1,
214
+ chargeType=False,
215
+ apiVersion=4,
216
+ channel="web",
217
+ tradeRulesV2=quote(trs),
218
+ )
219
+ res = await self._post(self.url_my_ad + str(ad_upd.id), form_data=req.model_dump(exclude_none=True), hdrs=hdrs)
220
+ if res["code"] == 200:
221
+ return res["data"]
222
+ elif res["code"] == 605:
223
+ hdrs = {"x-dialog-trace-id": res["extend"]["traceId"]}
224
+ return await self._ad_upd(ad_upd, hdrs)
225
+ elif res["code"] == 1010:
226
+ if (match := re.search(r"Available amount ([\d.]+)", res["message"])) and (qty := match.group(1)):
227
+ ad_upd.quantity = float(qty)
228
+ ad_upd.amount = round(ad_upd.quantity * ad_upd.price, 2)
229
+ return await self._ad_upd(ad_upd, hdrs)
230
+ elif res["code"] == 401:
231
+ raise Exception(res)
232
+ raise BaseException(res)
151
233
 
152
234
  async def ad_del(self) -> bool:
153
235
  pass
@@ -182,32 +264,81 @@ class AgentClient(BaseAgentClient):
182
264
  base_url = ""
183
265
  middle_url = ""
184
266
 
185
- htok: str = "Ev5lFfAvxDU2MA9BJ-Mc4U6zZG3Wb6qsp3Tx2fz6GIoY-uOP2m0-gvjE57ad1qDF"
186
-
187
267
  url_ads_req = "https://otc-cf.huobi.com/v1/data/trade-market"
188
268
  url_my_ads = "https://otc-api.trygofast.com/v1/data/trade-list?pageSize=50"
189
- url_my_ad = "https://www.huobi.com/-/x/otc/v1/otc/trade/" # + id
269
+ url_my_ad = "/-/x/otc/v1/otc/trade/" # + id
190
270
  url_my_bals = "https://www.huobi.com/-/x/otc/v1/capital/balance"
191
271
  url_paccs = "https://www.huobi.com/-/x/otc/v1/user/receipt-account"
192
272
 
273
+ async def _proc(self, resp: ClientResponse, bp: dict | str = None) -> dict | str:
274
+ if (await resp.json()).get("code") == 401:
275
+ await self.login()
276
+ return await self.METHS[resp.method](self, resp.url.path, bp)
277
+ return await super()._proc(resp, bp)
278
+
193
279
 
194
280
  async def _test():
195
281
  from x_model import init_db
196
- from xync_schema import TORM
197
282
 
198
- _ = await init_db(TORM, True)
199
- actor = (
200
- await models.Actor.filter(ex_id=15, agent__isnull=False).prefetch_related("ex", "agent", "person__user").first()
283
+ cn = await init_db(TORM, True)
284
+ ex = await models.Ex[9]
285
+ filebot = FileClient(NET_TOKEN)
286
+ ecl = ex.client(filebot)
287
+ agent = (
288
+ await models.Agent.filter(actor__ex=ex, auth__isnull=False)
289
+ .prefetch_related(
290
+ "actor__ex",
291
+ "actor__person__user__gmail",
292
+ "actor__my_ads__my_ad__race",
293
+ "actor__my_ads__pair_side__pair__cur",
294
+ "actor__my_ads__pms",
295
+ )
296
+ .first()
201
297
  )
202
- cl: AgentClient = actor.client()
203
- cred = await models.Cred[89]
204
- _ = await cl.cred_new(cred)
205
- _creds = await cl.creds()
298
+ cl: AgentClient = agent.client(ecl, filebot, XyncBot(PAY_TOKEN, cn))
299
+ # cred = await models.Cred[89]
300
+ # _ = await cl.cred_new(cred)
301
+ # _creds = await cl.creds()
206
302
  # _ = await cl.cred_del(16984748)
207
- await cl.close()
303
+
304
+ while True:
305
+ breq = GetAds(coin_id=1, cur_id=1, is_sell=False, pm_ids=[366])
306
+ sreq = GetAds(coin_id=1, cur_id=1, is_sell=True, pm_ids=[366])
307
+ breq_upd = AdUpd(id=1185713, price=87.01, **{**breq.model_dump(), "amount": 80001})
308
+ sreq_upd = AdUpd(id=1188929, price=92.99, **{**sreq.model_dump(), "amount": 65001})
309
+
310
+ bads: list[ad.Resp] = await cl.ex_client.ads(breq)
311
+ sads: list[ad.Resp] = await cl.ex_client.ads(sreq)
312
+
313
+ if bads:
314
+ if bads[0].uid == cl.actor.exid:
315
+ if round(bads[0].price - bads[1].price, 2) > 0.01:
316
+ breq_upd.price = bads[1].price + 0.01
317
+ await cl.ad_upd(breq_upd)
318
+ print(end="!", flush=True)
319
+ elif bads[0].price < 89:
320
+ breq_upd.price = bads[0].price + 0.01
321
+ await cl.ad_upd(breq_upd)
322
+ print(end="!", flush=True)
323
+
324
+ if sads:
325
+ if sads[0].uid == cl.actor.exid:
326
+ if round(sads[1].price - sads[0].price, 2) > 0.01:
327
+ sreq_upd.price = sads[1].price - 0.01
328
+ await cl.ad_upd(sreq_upd)
329
+ print(end="!", flush=True)
330
+ elif sads[0].price > 91:
331
+ sreq_upd.price = sads[0].price - 0.01
332
+ await cl.ad_upd(sreq_upd)
333
+ print(end="!", flush=True)
334
+
335
+ print(end=".", flush=True)
336
+ await sleep(15)
337
+
338
+ await cl.stop()
208
339
 
209
340
 
210
341
  if __name__ == "__main__":
211
- from asyncio import run
342
+ from asyncio import run, sleep
212
343
 
213
344
  run(_test())
@@ -1,20 +1,24 @@
1
1
  from typing import Literal
2
2
 
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, RootModel
4
4
  from xync_schema.xtype import BaseAd
5
5
 
6
6
 
7
7
  class TradeRule(BaseModel):
8
8
  title: str
9
- titleValue: str
9
+ titleValue: str | None = None
10
10
  content: str
11
11
  inputType: int
12
12
  inputValue: str
13
13
  hint: str
14
- contentCode: str
14
+ contentCode: Literal["PAY", "MERCHANT"]
15
+ contentType: int | None = None
15
16
  sort: int
16
17
 
17
18
 
19
+ TradeRulesV2 = RootModel[list[TradeRule]]
20
+
21
+
18
22
  class Req(BaseModel):
19
23
  tradeType: int
20
24
  coinId: int
@@ -22,27 +26,30 @@ class Req(BaseModel):
22
26
  minTradeLimit: float
23
27
  maxTradeLimit: float
24
28
  tradeCount: float
25
- password: str
29
+ password: str = ""
26
30
  payTerm: int
27
31
  isFixed: Literal["off", "on"]
28
- premium: int
32
+ premium: float
29
33
  isAutoReply: Literal["off", "on"]
30
34
  takerAcceptOrder: int
31
35
  isPayCode: Literal["off", "on"]
32
- isVerifyCapital: bool
36
+ verifyCapitalStatus: str = None # todo: check if int
33
37
  receiveAccounts: int
34
38
  deviation: int
35
39
  isTakerLimit: Literal["off", "on"]
40
+ takerRealLevel: Literal["off", "on"]
41
+ takerIsPhoneBind: Literal["off", "on"]
42
+ takerIsMerchant: Literal["off", "on"]
43
+ takerIsPayment: Literal["off", "on"]
36
44
  blockType: int
37
45
  session: int
38
46
  chargeType: bool
39
47
  apiVersion: int
40
48
  channel: str
41
- tradeRulesV2: list[TradeRule]
49
+ tradeRulesV2: str # TradeRulesV2
42
50
  securityToken: str | None = ""
43
- fixedPrice: str | None = ""
51
+ fixedPrice: float | None = None
44
52
  autoReplyContent: str | None = ""
45
- tradeRule: str | None = ""
46
53
 
47
54
 
48
55
  class PayMethod(BaseModel):
@@ -75,9 +82,9 @@ class Resp(BaseAd):
75
82
  orderCompleteRate: str
76
83
  payMethod: str
77
84
  payMethods: list[PayMethod]
78
- payName: str # list[PayName] # приходит массив объектов внутри строки
85
+ payName: str # list[PayName] # приходит массив объектов внутри строки
79
86
  payTerm: int
80
- price: str
87
+ price: float
81
88
  takerAcceptAmount: str
82
89
  takerAcceptOrder: int
83
90
  takerLimit: int
xync_client/Htx/ex.py CHANGED
@@ -9,7 +9,7 @@ from xync_schema.models import Ex, Cur
9
9
  from xync_schema.enums import PmType
10
10
 
11
11
  from xync_client.Abc.Ex import BaseExClient
12
- from xync_client.Abc.xtype import PmEx, MapOfIdsList
12
+ from xync_client.Abc.xtype import PmEx, MapOfIdsList, GetAds
13
13
  from xync_client.Htx.etype import pm, Country, ad
14
14
  from xync_client.loader import NET_TOKEN
15
15
  from xync_client.loader import TORM
@@ -42,20 +42,18 @@ class ExClient(BaseExClient):
42
42
  pair[rcurs[cur["name"]]] += [coen]
43
43
  return tuple(pairs.values())
44
44
 
45
- async def ads(
46
- self, coin_exid: int, cur_exid: int, is_sell: bool, pm_exids: list[str] = None, amount: int = None
47
- ) -> list[xtype.BaseAd]:
45
+ async def _ads(self, req: GetAds, lim: int = None, vm_filter: bool = False, **kwargs) -> list[ad.Resp]:
48
46
  params = {
49
- "coinId": coin_exid,
50
- "currency": cur_exid,
51
- "tradeType": "sell" if is_sell else "buy",
47
+ "coinId": req.coin_id,
48
+ "currency": req.cur_id,
49
+ "tradeType": "sell" if req.is_sell else "buy",
52
50
  "currPage": 1,
53
- "payMethod": ",".join(pm_exids) if pm_exids else 0,
51
+ "payMethod": ",".join(req.pm_ids) if req.pm_ids else 0,
54
52
  "acceptOrder": 0,
55
53
  "blockType": "general",
56
54
  "online": 1,
57
55
  "range": 0,
58
- "amount": amount or "",
56
+ "amount": req.amount or "",
59
57
  "onlyTradable": "false",
60
58
  "isFollowed": "false",
61
59
  }
@@ -0,0 +1,85 @@
1
+ from asyncio import run
2
+ from hashlib import md5
3
+ from uuid import uuid4
4
+
5
+ from pyro_client.client.file import FileClient
6
+ from xync_bot import XyncBot
7
+ from xync_client.Bybit.etype.order import TakeAdReq
8
+
9
+ from xync_client.loader import PAY_TOKEN, NET_TOKEN
10
+ from xync_schema import models
11
+ from xync_schema.enums import UserStatus
12
+
13
+ from xync_client.Abc.Agent import BaseAgentClient
14
+
15
+
16
+ class AgentClient(BaseAgentClient):
17
+ i: int = 0
18
+ headers = {
19
+ "accept-language": "ru,en;q=0.9",
20
+ "language:": "ru-RU",
21
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
22
+ }
23
+
24
+ async def _take_ad(self, req: TakeAdReq):
25
+ self.i = 33 if self.i > 9998 else self.i + 2
26
+ hdrs = self.headers | {"trochilus-trace-id": f"{uuid4()}-{self.i:04d}"}
27
+ auth = {
28
+ "p0": self.actor.agent.auth["p0"],
29
+ "k0": self.actor.agent.auth["k0"],
30
+ "chash": self.actor.agent.auth["chash"],
31
+ "mtoken": self.actor.agent.auth["deviceId"],
32
+ "mhash": md5(self.actor.agent.auth["deviceId"].encode()).hexdigest(),
33
+ }
34
+ data = {
35
+ "scene": "TRADE_BUY",
36
+ "quantity": req.quantity,
37
+ "amount": req.amount,
38
+ "orderId": req.ad_id,
39
+ "authVersion": "v2",
40
+ "deviceId": auth["mtoken"],
41
+ }
42
+ res = await self._post("/api/verify/second_auth/risk/scene", json=data, hdrs=hdrs)
43
+ data = {
44
+ "amount": req.amount,
45
+ "authVersion": "v2",
46
+ "orderId": req.ad_id,
47
+ "price": req.price,
48
+ "ts": int(1761155700.8372989 * 1000),
49
+ "userConfirmPaymentId" if req.is_sell else "userConfirmPayMethodId": req.pm_id,
50
+ }
51
+ self.i = 33 if self.i > 9999 else self.i + 1
52
+ hdrs = self.headers | {"trochilus-trace-id": f"{uuid4()}-{self.i:04d}"}
53
+ res = await self._post("/api/order/deal?mhash=" + auth["mhash"], data=auth | data, hdrs=hdrs)
54
+ return res["data"]
55
+
56
+
57
+ async def main():
58
+ from x_model import init_db
59
+ from xync_client.loader import TORM
60
+
61
+ cn = await init_db(TORM, True)
62
+
63
+ agent = (
64
+ await models.Agent.filter(
65
+ actor__ex_id=12,
66
+ active=True,
67
+ auth__isnull=False,
68
+ actor__person__user__status=UserStatus.ACTIVE,
69
+ actor__person__user__pm_agents__isnull=False,
70
+ )
71
+ .prefetch_related("actor__ex", "actor__person__user__gmail")
72
+ .first()
73
+ )
74
+
75
+ bbot = XyncBot(PAY_TOKEN, cn)
76
+ fbot = FileClient(NET_TOKEN)
77
+
78
+ cl = agent.client(fbot, bbot)
79
+ req = TakeAdReq(ad_id="a1574088909645125632", amount=500, pm_id=366, cur_="RUB", price=85.8, is_sell=True)
80
+ res = await cl.take_ad(req)
81
+ print(res)
82
+
83
+
84
+ if __name__ == "__main__":
85
+ run(main())