trd-utils 0.0.3__py3-none-any.whl → 0.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of trd-utils might be problematic. Click here for more details.
- trd_utils/__init__.py +1 -1
- trd_utils/common_utils/float_utils.py +11 -0
- trd_utils/exchanges/__init__.py +11 -0
- trd_utils/exchanges/blofin/__init__.py +6 -0
- trd_utils/exchanges/blofin/blofin_client.py +238 -0
- trd_utils/exchanges/blofin/blofin_types.py +144 -0
- trd_utils/{bx_ultra → exchanges/bx_ultra}/bx_types.py +101 -5
- trd_utils/{bx_ultra → exchanges/bx_ultra}/bx_ultra_client.py +234 -57
- trd_utils/{bx_ultra/common_utils.py → exchanges/bx_ultra/bx_utils.py} +0 -8
- trd_utils/exchanges/exchange_base.py +76 -0
- trd_utils/tradingview/tradingview_client.py +35 -39
- trd_utils/types_helper/base_model.py +0 -1
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.5.dist-info}/LICENSE +1 -1
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.5.dist-info}/METADATA +13 -3
- trd_utils-0.0.5.dist-info/RECORD +23 -0
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.5.dist-info}/WHEEL +1 -1
- trd_utils-0.0.3.dist-info/RECORD +0 -17
- /trd_utils/{bx_ultra → exchanges/bx_ultra}/__init__.py +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""BxUltra exchange subclass"""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from decimal import Decimal
|
|
4
5
|
import json
|
|
5
6
|
import logging
|
|
7
|
+
from typing import Type
|
|
6
8
|
import uuid
|
|
7
9
|
|
|
8
10
|
import httpx
|
|
@@ -10,9 +12,10 @@ import httpx
|
|
|
10
12
|
import time
|
|
11
13
|
from pathlib import Path
|
|
12
14
|
|
|
13
|
-
from .
|
|
14
|
-
from .bx_types import (
|
|
15
|
+
from trd_utils.exchanges.bx_ultra.bx_utils import do_ultra_ss
|
|
16
|
+
from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
15
17
|
AssetsInfoResponse,
|
|
18
|
+
ContractOrdersHistoryResponse,
|
|
16
19
|
ContractsListResponse,
|
|
17
20
|
CopyTraderTradePositionsResponse,
|
|
18
21
|
HintListResponse,
|
|
@@ -24,8 +27,11 @@ from .bx_types import (
|
|
|
24
27
|
UserFavoriteQuotationResponse,
|
|
25
28
|
ZenDeskABStatusResponse,
|
|
26
29
|
ZoneModuleListResponse,
|
|
30
|
+
BxApiResponse,
|
|
27
31
|
)
|
|
28
|
-
from
|
|
32
|
+
from trd_utils.cipher import AESCipher
|
|
33
|
+
|
|
34
|
+
from trd_utils.exchanges.exchange_base import ExchangeBase
|
|
29
35
|
|
|
30
36
|
PLATFORM_ID_ANDROID = "10"
|
|
31
37
|
PLATFORM_ID_WEB = "30"
|
|
@@ -42,25 +48,16 @@ TG_APP_VERSION = "5.0.15"
|
|
|
42
48
|
logger = logging.getLogger(__name__)
|
|
43
49
|
|
|
44
50
|
|
|
45
|
-
class BXUltraClient:
|
|
51
|
+
class BXUltraClient(ExchangeBase):
|
|
46
52
|
###########################################################
|
|
47
53
|
# region client parameters
|
|
48
54
|
we_api_base_host: str = "\u0061pi-\u0061pp.w\u0065-\u0061pi.com"
|
|
49
55
|
we_api_base_url: str = "https://\u0061pi-\u0061pp.w\u0065-\u0061pi.com/\u0061pi"
|
|
50
|
-
|
|
51
56
|
original_base_host: str = "https://\u0062ing\u0078.co\u006d"
|
|
52
|
-
|
|
53
57
|
qq_os_base_host: str = "https://\u0061pi-\u0061pp.\u0071\u0071-os.com"
|
|
54
58
|
qq_os_base_url: str = "https://\u0061pi-\u0061pp.\u0071\u0071-os.com/\u0061pi"
|
|
55
59
|
|
|
56
|
-
device_id: str = None
|
|
57
|
-
trace_id: str = None
|
|
58
|
-
app_version: str = "4.28.3"
|
|
59
|
-
platform_id: str = "10"
|
|
60
|
-
install_channel: str = "officialAPK"
|
|
61
|
-
channel_header: str = "officialAPK"
|
|
62
60
|
origin_header: str = "https://\u0062ing\u0078.co\u006d"
|
|
63
|
-
authorization_token: str = None
|
|
64
61
|
app_id: str = "30004"
|
|
65
62
|
main_app_id: str = "10009"
|
|
66
63
|
trade_env: str = "real"
|
|
@@ -69,12 +66,6 @@ class BXUltraClient:
|
|
|
69
66
|
device_brand: str = "SM-N976N"
|
|
70
67
|
platform_lang: str = "en"
|
|
71
68
|
sys_lang: str = "en"
|
|
72
|
-
user_agent: str = "okhttp/4.12.0"
|
|
73
|
-
x_requested_with: str = None
|
|
74
|
-
httpx_client: httpx.AsyncClient = None
|
|
75
|
-
account_name: str = "default"
|
|
76
|
-
|
|
77
|
-
_fav_letter: str = "^"
|
|
78
69
|
|
|
79
70
|
# endregion
|
|
80
71
|
###########################################################
|
|
@@ -87,6 +78,7 @@ class BXUltraClient:
|
|
|
87
78
|
app_version: str = ANDROID_APP_VERSION,
|
|
88
79
|
http_verify: bool = True,
|
|
89
80
|
fav_letter: str = "^",
|
|
81
|
+
sessions_dir: str = "sessions",
|
|
90
82
|
):
|
|
91
83
|
self.httpx_client = httpx.AsyncClient(
|
|
92
84
|
verify=http_verify, http2=True, http1=False
|
|
@@ -97,13 +89,15 @@ class BXUltraClient:
|
|
|
97
89
|
self.app_version = app_version
|
|
98
90
|
self._fav_letter = fav_letter
|
|
99
91
|
|
|
100
|
-
self.read_from_session_file(f"{self.account_name}.bx")
|
|
92
|
+
self.read_from_session_file(f"{sessions_dir}/{self.account_name}.bx")
|
|
101
93
|
|
|
102
94
|
# endregion
|
|
103
95
|
###########################################################
|
|
104
96
|
# region api/coin/v1
|
|
105
97
|
async def get_zone_module_info(
|
|
106
|
-
self,
|
|
98
|
+
self,
|
|
99
|
+
only_one_position: int = 0,
|
|
100
|
+
biz_type: int = 10,
|
|
107
101
|
) -> ZoneModuleListResponse:
|
|
108
102
|
"""
|
|
109
103
|
Fetches and returns zone module info from the API.
|
|
@@ -116,101 +110,113 @@ class BXUltraClient:
|
|
|
116
110
|
}
|
|
117
111
|
headers = self.get_headers(params)
|
|
118
112
|
headers["Only_one_position"] = f"{only_one_position}"
|
|
119
|
-
|
|
113
|
+
return await self.invoke_get(
|
|
120
114
|
f"{self.we_api_base_url}/coin/v1/zone/module-info",
|
|
121
115
|
headers=headers,
|
|
122
116
|
params=params,
|
|
117
|
+
model=ZoneModuleListResponse,
|
|
123
118
|
)
|
|
124
|
-
return ZoneModuleListResponse.deserialize(response.json(parse_float=Decimal))
|
|
125
119
|
|
|
126
120
|
async def get_user_favorite_quotation(
|
|
127
|
-
self,
|
|
128
|
-
|
|
121
|
+
self,
|
|
122
|
+
only_one_position: int = 0,
|
|
123
|
+
biz_type: int = 1,
|
|
124
|
+
) -> UserFavoriteQuotationResponse:
|
|
129
125
|
params = {
|
|
130
126
|
"bizType": f"{biz_type}",
|
|
131
127
|
}
|
|
132
128
|
headers = self.get_headers(params)
|
|
133
129
|
headers["Only_one_position"] = f"{only_one_position}"
|
|
134
|
-
|
|
130
|
+
return await self.invoke_get(
|
|
135
131
|
f"{self.we_api_base_url}/coin/v1/user/favorite/quotation",
|
|
136
132
|
headers=headers,
|
|
137
133
|
params=params,
|
|
138
|
-
|
|
139
|
-
return UserFavoriteQuotationResponse.deserialize(
|
|
140
|
-
response.json(parse_float=Decimal)
|
|
134
|
+
model=UserFavoriteQuotationResponse,
|
|
141
135
|
)
|
|
142
136
|
|
|
143
|
-
async def get_quotation_rank(
|
|
137
|
+
async def get_quotation_rank(
|
|
138
|
+
self,
|
|
139
|
+
only_one_position: int = 0,
|
|
140
|
+
order_flag: int = 0,
|
|
141
|
+
) -> QuotationRankResponse:
|
|
144
142
|
params = {
|
|
145
143
|
"orderFlag": f"{order_flag}",
|
|
146
144
|
}
|
|
147
145
|
headers = self.get_headers(params)
|
|
148
146
|
headers["Only_one_position"] = f"{only_one_position}"
|
|
149
|
-
|
|
147
|
+
return await self.invoke_get(
|
|
150
148
|
f"{self.we_api_base_url}/coin/v1/rank/quotation-rank",
|
|
151
149
|
headers=headers,
|
|
152
150
|
params=params,
|
|
151
|
+
model=QuotationRankResponse,
|
|
153
152
|
)
|
|
154
|
-
return QuotationRankResponse.deserialize(response.json(parse_float=Decimal))
|
|
155
153
|
|
|
156
|
-
async def get_hot_search(
|
|
154
|
+
async def get_hot_search(
|
|
155
|
+
self,
|
|
156
|
+
only_one_position: int = 0,
|
|
157
|
+
biz_type: int = 30,
|
|
158
|
+
) -> HotSearchResponse:
|
|
157
159
|
params = {
|
|
158
160
|
"bizType": f"{biz_type}",
|
|
159
161
|
}
|
|
160
162
|
headers = self.get_headers(params)
|
|
161
163
|
headers["Only_one_position"] = f"{only_one_position}"
|
|
162
|
-
|
|
164
|
+
return await self.invoke_get(
|
|
163
165
|
f"{self.we_api_base_url}/coin/v1/quotation/hot-search",
|
|
164
166
|
headers=headers,
|
|
165
167
|
params=params,
|
|
168
|
+
model=HotSearchResponse,
|
|
166
169
|
)
|
|
167
|
-
return HotSearchResponse.deserialize(response.json(parse_float=Decimal))
|
|
168
170
|
|
|
169
|
-
async def get_homepage(
|
|
171
|
+
async def get_homepage(
|
|
172
|
+
self,
|
|
173
|
+
only_one_position: int = 0,
|
|
174
|
+
biz_type: int = 30,
|
|
175
|
+
) -> HomePageResponse:
|
|
170
176
|
params = {
|
|
171
177
|
"biz-type": f"{biz_type}",
|
|
172
178
|
}
|
|
173
179
|
headers = self.get_headers(params)
|
|
174
180
|
headers["Only_one_position"] = f"{only_one_position}"
|
|
175
|
-
|
|
181
|
+
return await self.invoke_get(
|
|
176
182
|
f"{self.we_api_base_url}/coin/v1/discovery/homepage",
|
|
177
183
|
headers=headers,
|
|
178
184
|
params=params,
|
|
185
|
+
model=HomePageResponse,
|
|
179
186
|
)
|
|
180
|
-
return HomePageResponse.deserialize(response.json(parse_float=Decimal))
|
|
181
187
|
|
|
182
188
|
# endregion
|
|
183
189
|
###########################################################
|
|
184
190
|
# region customer
|
|
185
|
-
async def get_zendesk_ab_status(self):
|
|
191
|
+
async def get_zendesk_ab_status(self) -> ZenDeskABStatusResponse:
|
|
186
192
|
headers = self.get_headers()
|
|
187
|
-
|
|
193
|
+
return await self.invoke_get(
|
|
188
194
|
f"{self.we_api_base_url}/customer/v1/zendesk/ab-status",
|
|
189
195
|
headers=headers,
|
|
196
|
+
model=ZenDeskABStatusResponse,
|
|
190
197
|
)
|
|
191
|
-
return ZenDeskABStatusResponse.deserialize(response.json(parse_float=Decimal))
|
|
192
198
|
|
|
193
199
|
# endregion
|
|
194
200
|
###########################################################
|
|
195
201
|
# region platform-tool
|
|
196
202
|
async def get_hint_list(self) -> HintListResponse:
|
|
197
203
|
headers = self.get_headers()
|
|
198
|
-
|
|
204
|
+
return await self.invoke_get(
|
|
199
205
|
f"{self.we_api_base_url}/platform-tool/v1/hint/list",
|
|
200
206
|
headers=headers,
|
|
207
|
+
model=HintListResponse,
|
|
201
208
|
)
|
|
202
|
-
return HintListResponse.deserialize(response.json(parse_float=Decimal))
|
|
203
209
|
|
|
204
210
|
# endregion
|
|
205
211
|
###########################################################
|
|
206
212
|
# region asset-manager
|
|
207
213
|
async def get_assets_info(self) -> AssetsInfoResponse:
|
|
208
214
|
headers = self.get_headers(needs_auth=True)
|
|
209
|
-
|
|
215
|
+
return await self.invoke_get(
|
|
210
216
|
f"{self.we_api_base_url}/asset-manager/v1/assets/account-total-overview",
|
|
211
217
|
headers=headers,
|
|
218
|
+
model=AssetsInfoResponse,
|
|
212
219
|
)
|
|
213
|
-
return AssetsInfoResponse.deserialize(response.json(parse_float=Decimal))
|
|
214
220
|
|
|
215
221
|
# endregion
|
|
216
222
|
###########################################################
|
|
@@ -234,12 +240,140 @@ class BXUltraClient:
|
|
|
234
240
|
if margin_coin_name:
|
|
235
241
|
params["marginCoinName"] = margin_coin_name
|
|
236
242
|
headers = self.get_headers(params, needs_auth=True)
|
|
237
|
-
|
|
243
|
+
return await self.invoke_get(
|
|
238
244
|
f"{self.we_api_base_url}/v4/contract/order/hold",
|
|
239
245
|
headers=headers,
|
|
240
246
|
params=params,
|
|
247
|
+
model=ContractsListResponse,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
async def get_contract_order_history(
|
|
251
|
+
self,
|
|
252
|
+
fund_type: int = 1,
|
|
253
|
+
paging_size: int = 10,
|
|
254
|
+
page_id: int = 0,
|
|
255
|
+
from_order_no: int = 0,
|
|
256
|
+
margin_coin_name: str = "USDT",
|
|
257
|
+
margin_type: int = 0,
|
|
258
|
+
) -> ContractOrdersHistoryResponse:
|
|
259
|
+
params = {
|
|
260
|
+
"fundType": f"{fund_type}",
|
|
261
|
+
"pagingSize": f"{paging_size}",
|
|
262
|
+
"pageId": f"{page_id}",
|
|
263
|
+
"marginCoinName": margin_coin_name,
|
|
264
|
+
"marginType": f"{margin_type}",
|
|
265
|
+
}
|
|
266
|
+
if from_order_no:
|
|
267
|
+
params["fromOrderNo"] = f"{from_order_no}"
|
|
268
|
+
|
|
269
|
+
headers = self.get_headers(params, needs_auth=True)
|
|
270
|
+
return await self.invoke_get(
|
|
271
|
+
f"{self.we_api_base_url}/v2/contract/order/history",
|
|
272
|
+
headers=headers,
|
|
273
|
+
params=params,
|
|
274
|
+
model=ContractOrdersHistoryResponse,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
async def get_today_contract_earnings(
|
|
278
|
+
self,
|
|
279
|
+
margin_coin_name: str = "USDT",
|
|
280
|
+
page_size: int = 10,
|
|
281
|
+
max_total_size: int = 500,
|
|
282
|
+
delay_per_fetch: float = 1.0,
|
|
283
|
+
) -> Decimal:
|
|
284
|
+
"""
|
|
285
|
+
Fetches today's earnings from the contract orders.
|
|
286
|
+
NOTE: This method is a bit slow due to the API rate limiting.
|
|
287
|
+
NOTE: If the user has not opened ANY contract orders today,
|
|
288
|
+
this method will return None.
|
|
289
|
+
"""
|
|
290
|
+
return await self._get_period_contract_earnings(
|
|
291
|
+
period="day",
|
|
292
|
+
margin_coin_name=margin_coin_name,
|
|
293
|
+
page_size=page_size,
|
|
294
|
+
max_total_size=max_total_size,
|
|
295
|
+
delay_per_fetch=delay_per_fetch,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
async def get_this_week_contract_earnings(
|
|
299
|
+
self,
|
|
300
|
+
margin_coin_name: str = "USDT",
|
|
301
|
+
page_size: int = 10,
|
|
302
|
+
max_total_size: int = 500,
|
|
303
|
+
delay_per_fetch: float = 1.0,
|
|
304
|
+
) -> Decimal:
|
|
305
|
+
"""
|
|
306
|
+
Fetches this week's earnings from the contract orders.
|
|
307
|
+
NOTE: This method is a bit slow due to the API rate limiting.
|
|
308
|
+
NOTE: If the user has not opened ANY contract orders this week,
|
|
309
|
+
this method will return None.
|
|
310
|
+
"""
|
|
311
|
+
return await self._get_period_contract_earnings(
|
|
312
|
+
period="week",
|
|
313
|
+
margin_coin_name=margin_coin_name,
|
|
314
|
+
page_size=page_size,
|
|
315
|
+
max_total_size=max_total_size,
|
|
316
|
+
delay_per_fetch=delay_per_fetch,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
async def get_this_month_contract_earnings(
|
|
320
|
+
self,
|
|
321
|
+
margin_coin_name: str = "USDT",
|
|
322
|
+
page_size: int = 10,
|
|
323
|
+
max_total_size: int = 500,
|
|
324
|
+
delay_per_fetch: float = 1.0,
|
|
325
|
+
) -> Decimal:
|
|
326
|
+
"""
|
|
327
|
+
Fetches this month's earnings from the contract orders.
|
|
328
|
+
NOTE: This method is a bit slow due to the API rate limiting.
|
|
329
|
+
NOTE: If the user has not opened ANY contract orders this week,
|
|
330
|
+
this method will return None.
|
|
331
|
+
"""
|
|
332
|
+
return await self._get_period_contract_earnings(
|
|
333
|
+
period="month",
|
|
334
|
+
margin_coin_name=margin_coin_name,
|
|
335
|
+
page_size=page_size,
|
|
336
|
+
max_total_size=max_total_size,
|
|
337
|
+
delay_per_fetch=delay_per_fetch,
|
|
241
338
|
)
|
|
242
|
-
|
|
339
|
+
|
|
340
|
+
async def _get_period_contract_earnings(
|
|
341
|
+
self,
|
|
342
|
+
period: str,
|
|
343
|
+
margin_coin_name: str = "USDT",
|
|
344
|
+
page_size: int = 10,
|
|
345
|
+
max_total_size: int = 500,
|
|
346
|
+
delay_per_fetch: float = 1.0,
|
|
347
|
+
) -> Decimal:
|
|
348
|
+
total_fetched = 0
|
|
349
|
+
total_earnings = Decimal("0.00")
|
|
350
|
+
has_earned_any = False
|
|
351
|
+
while total_fetched < max_total_size:
|
|
352
|
+
current_page = total_fetched // page_size
|
|
353
|
+
result = await self.get_contract_order_history(
|
|
354
|
+
page_id=current_page,
|
|
355
|
+
paging_size=page_size,
|
|
356
|
+
margin_coin_name=margin_coin_name,
|
|
357
|
+
)
|
|
358
|
+
if period == "day":
|
|
359
|
+
temp_earnings = result.get_today_earnings()
|
|
360
|
+
elif period == "week":
|
|
361
|
+
temp_earnings = result.get_this_week_earnings()
|
|
362
|
+
elif period == "month":
|
|
363
|
+
temp_earnings = result.get_this_month_earnings()
|
|
364
|
+
if temp_earnings is None:
|
|
365
|
+
# all ended
|
|
366
|
+
break
|
|
367
|
+
total_earnings += temp_earnings
|
|
368
|
+
has_earned_any = True
|
|
369
|
+
total_fetched += page_size
|
|
370
|
+
if result.get_orders_len() < page_size:
|
|
371
|
+
break
|
|
372
|
+
await asyncio.sleep(delay_per_fetch)
|
|
373
|
+
|
|
374
|
+
if not has_earned_any:
|
|
375
|
+
return None
|
|
376
|
+
return total_earnings
|
|
243
377
|
|
|
244
378
|
# endregion
|
|
245
379
|
###########################################################
|
|
@@ -260,13 +394,11 @@ class BXUltraClient:
|
|
|
260
394
|
"copyTradeLabelType": f"{copy_trade_label_type}",
|
|
261
395
|
}
|
|
262
396
|
headers = self.get_headers(params)
|
|
263
|
-
|
|
397
|
+
return await self.invoke_get(
|
|
264
398
|
f"{self.we_api_base_url}/copy-trade-facade/v2/real/trader/positions",
|
|
265
399
|
headers=headers,
|
|
266
400
|
params=params,
|
|
267
|
-
|
|
268
|
-
return CopyTraderTradePositionsResponse.deserialize(
|
|
269
|
-
response.json(parse_float=Decimal)
|
|
401
|
+
model=CopyTraderTradePositionsResponse,
|
|
270
402
|
)
|
|
271
403
|
|
|
272
404
|
async def search_copy_traders(
|
|
@@ -298,25 +430,25 @@ class BXUltraClient:
|
|
|
298
430
|
"nickName": nick_name,
|
|
299
431
|
}
|
|
300
432
|
headers = self.get_headers(payload)
|
|
301
|
-
|
|
433
|
+
return await self.invoke_post(
|
|
302
434
|
f"{self.we_api_base_url}/v6/copy-trade/search/search",
|
|
303
435
|
headers=headers,
|
|
304
436
|
params=params,
|
|
305
|
-
content=
|
|
437
|
+
content=payload,
|
|
438
|
+
model=SearchCopyTradersResponse,
|
|
306
439
|
)
|
|
307
|
-
return SearchCopyTradersResponse.deserialize(response.json(parse_float=Decimal))
|
|
308
440
|
|
|
309
441
|
# endregion
|
|
310
442
|
###########################################################
|
|
311
443
|
# region welfare
|
|
312
444
|
async def do_daily_check_in(self):
|
|
313
445
|
headers = self.get_headers(needs_auth=True)
|
|
314
|
-
|
|
446
|
+
return await self.invoke_post(
|
|
315
447
|
f"{self.original_base_host}/api/act-operation/v1/welfare/sign-in/do",
|
|
316
448
|
headers=headers,
|
|
317
449
|
content="",
|
|
450
|
+
model=None,
|
|
318
451
|
)
|
|
319
|
-
return response.json()
|
|
320
452
|
|
|
321
453
|
# endregion
|
|
322
454
|
###########################################################
|
|
@@ -352,7 +484,7 @@ class BXUltraClient:
|
|
|
352
484
|
payload_data=payload,
|
|
353
485
|
),
|
|
354
486
|
"Timestamp": f"{the_timestamp}",
|
|
355
|
-
|
|
487
|
+
'Accept-Encoding': 'gzip, deflate',
|
|
356
488
|
"User-Agent": self.user_agent,
|
|
357
489
|
"Connection": "close",
|
|
358
490
|
"appsiteid": "0",
|
|
@@ -365,6 +497,51 @@ class BXUltraClient:
|
|
|
365
497
|
the_headers["Authorization"] = f"Bearer {self.authorization_token}"
|
|
366
498
|
return the_headers
|
|
367
499
|
|
|
500
|
+
async def invoke_get(
|
|
501
|
+
self,
|
|
502
|
+
url: str,
|
|
503
|
+
headers: dict | None = None,
|
|
504
|
+
params: dict | None = None,
|
|
505
|
+
model: Type[BxApiResponse] | None = None,
|
|
506
|
+
parse_float=Decimal,
|
|
507
|
+
) -> "BxApiResponse":
|
|
508
|
+
"""
|
|
509
|
+
Invokes the specific request to the specific url with the specific params and headers.
|
|
510
|
+
"""
|
|
511
|
+
response = await self.httpx_client.get(
|
|
512
|
+
url=url,
|
|
513
|
+
headers=headers,
|
|
514
|
+
params=params,
|
|
515
|
+
)
|
|
516
|
+
return model.deserialize(response.json(parse_float=parse_float))
|
|
517
|
+
|
|
518
|
+
async def invoke_post(
|
|
519
|
+
self,
|
|
520
|
+
url: str,
|
|
521
|
+
headers: dict | None = None,
|
|
522
|
+
params: dict | None = None,
|
|
523
|
+
content: dict | str | bytes = "",
|
|
524
|
+
model: Type[BxApiResponse] | None = None,
|
|
525
|
+
parse_float=Decimal,
|
|
526
|
+
) -> "BxApiResponse":
|
|
527
|
+
"""
|
|
528
|
+
Invokes the specific request to the specific url with the specific params and headers.
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
if isinstance(content, dict):
|
|
532
|
+
content = json.dumps(content, separators=(",", ":"), sort_keys=True)
|
|
533
|
+
|
|
534
|
+
response = await self.httpx_client.post(
|
|
535
|
+
url=url,
|
|
536
|
+
headers=headers,
|
|
537
|
+
params=params,
|
|
538
|
+
content=content,
|
|
539
|
+
)
|
|
540
|
+
if not model:
|
|
541
|
+
return response.json()
|
|
542
|
+
|
|
543
|
+
return model.deserialize(response.json(parse_float=parse_float))
|
|
544
|
+
|
|
368
545
|
async def aclose(self) -> None:
|
|
369
546
|
await self.httpx_client.aclose()
|
|
370
547
|
logger.info("BXUltraClient closed")
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import json
|
|
3
3
|
import uuid
|
|
4
|
-
from decimal import Decimal
|
|
5
4
|
|
|
6
|
-
default_quantize = Decimal("1.00")
|
|
7
5
|
|
|
8
6
|
default_e: str = (
|
|
9
7
|
"\u0039\u0035\u0064\u0036\u0035\u0063\u0037\u0033\u0064\u0063\u0035"
|
|
@@ -17,12 +15,6 @@ long_accept_header1: str = (
|
|
|
17
15
|
+ "image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
|
|
18
16
|
)
|
|
19
17
|
|
|
20
|
-
def dec_to_str(dec_value: Decimal) -> str:
|
|
21
|
-
return format(dec_value.quantize(default_quantize), "f")
|
|
22
|
-
|
|
23
|
-
def dec_to_normalize(dec_value: Decimal) -> str:
|
|
24
|
-
return format(dec_value.normalize(), "f")
|
|
25
|
-
|
|
26
18
|
def do_ultra_ss(
|
|
27
19
|
e_param: str,
|
|
28
20
|
se_param: str,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Any
|
|
4
|
+
from abc import ABC
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExchangeBase(ABC):
|
|
10
|
+
###########################################################
|
|
11
|
+
# region client parameters
|
|
12
|
+
user_agent: str = "okhttp/4.12.0"
|
|
13
|
+
x_requested_with: str = None
|
|
14
|
+
httpx_client: httpx.AsyncClient = None
|
|
15
|
+
account_name: str = "default"
|
|
16
|
+
sessions_dir: str = "sessions"
|
|
17
|
+
|
|
18
|
+
authorization_token: str = None
|
|
19
|
+
device_id: str = None
|
|
20
|
+
trace_id: str = None
|
|
21
|
+
app_version: str = "4.28.3"
|
|
22
|
+
platform_id: str = "10"
|
|
23
|
+
install_channel: str = "officialAPK"
|
|
24
|
+
channel_header: str = "officialAPK"
|
|
25
|
+
|
|
26
|
+
_fav_letter: str = "^"
|
|
27
|
+
# endregion
|
|
28
|
+
###########################################################
|
|
29
|
+
# region client helper methods
|
|
30
|
+
def get_headers(self, payload=None, needs_auth: bool = False) -> dict:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
async def invoke_get(
|
|
34
|
+
self,
|
|
35
|
+
url: str,
|
|
36
|
+
headers: dict | None,
|
|
37
|
+
params: dict | None,
|
|
38
|
+
model: Any,
|
|
39
|
+
parse_float=Decimal,
|
|
40
|
+
) -> Any:
|
|
41
|
+
"""
|
|
42
|
+
Invokes the specific request to the specific url with the specific params and headers.
|
|
43
|
+
"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
async def invoke_post(
|
|
47
|
+
self,
|
|
48
|
+
url: str,
|
|
49
|
+
headers: dict | None = None,
|
|
50
|
+
params: dict | None = None,
|
|
51
|
+
content: str | bytes = "",
|
|
52
|
+
model: None = None,
|
|
53
|
+
parse_float=Decimal,
|
|
54
|
+
):
|
|
55
|
+
"""
|
|
56
|
+
Invokes the specific request to the specific url with the specific params and headers.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
async def aclose(self) -> None:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def read_from_session_file(self, file_path: str) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Reads from session file; if it doesn't exist, creates it.
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def _save_session_file(self, file_path: str) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Saves current information to the session file.
|
|
72
|
+
"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
# endregion
|
|
76
|
+
###########################################################
|